本文共 3029 字,大约阅读时间需要 10 分钟。
注:以下以名为Test的对话框工程为例讲解
对话框程序中,如果我们有拖标准控件到界面中,并且有和控件类变量绑定,则会有
void CTestDlg::DoDataExchange(CDataExchange* pDX)
{ CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_BTN_TEST, m_testBtn); DDX_Control(pDX, IDC_BUTTON1, m_btnTest); } 1 2 3 4 5 6 void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl) 其中DDX_Control用于将ID为nIDC的控件与类型为CWnd的变量rControl绑定 实际上 (1)执行了子类化也即改写了Windows标准控件的窗口过程函数为MFC中通用的窗口过程函数 (2)将rControl与nIDC对应控件的HWnd窗口句柄绑定,并将在MFC全局的HWND与CWnd的Map表中增加一项[dlgdata.cpp]
void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
{ if (rControl.m_hWnd == NULL) { HWND hWndCtrl; pDX->m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl); rControl.SubclassWindow(hWndCtrl); //子类化 } } 1 2 3 4 5 6 7 8 9 [wincore.cpp]BOOL CWnd::SubclassWindow(HWND hWnd)
{ if (!Attach(hWnd)) return FALSE;PreSubclassWindow();
WNDPROC* lplpfn = GetSuperWndProcAddr();
//子类化控件,使其窗口过程函数为MFC的通用窗口过程函数 WNDPROC oldWndProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (INT_PTR)AfxGetAfxWndProc());if (*lplpfn == NULL)
*lplpfn = oldWndProc; //保存下来return TRUE;
} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [wincore.cpp]WNDPROC AFXAPI AfxGetAfxWndProc()
{ #ifdef _AFXDLL return AfxGetModuleState()->m_pfnAfxWndProc; #else return &AfxWndProc; #endif } 1 2 3 4 5 6 7 8 MFC中通用的窗口过程函数如下: [wincore.cpp]LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{ CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); //从HWND获取对应的CWnd* if (pWnd == NULL || pWnd->m_hWnd != hWnd) return ::DefWindowProc(hWnd, nMsg, wParam, lParam);return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
} 1 2 3 4 5 6 7 8 [wincore.cpp]LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam = 0, LPARAM lParam = 0) { LRESULT lResult; //通用的窗口过程函数中具体消息的处理转化为类的成员函数去处理 lResult = pWnd->WindowProc(nMsg, wParam, lParam); return lResult; } 1 2 3 4 5 6 7 8 [wincore.cpp]LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{ LRESULT lResult = 0; if (!OnWndMsg(message, wParam, lParam, &lResult)) //在消息映射表找到对应的处理函数进行处理 lResult = DefWindowProc(message, wParam, lParam); //没有找到则调用未子类化之前的窗口过程函数处理 return lResult; } 1 2 3 4 5 6 7 [wincore.cpp]LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{ if (m_pfnSuper != NULL) return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);WNDPROC pfnWndProc;
if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL) return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam); else return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam); } 1 2 3 4 5 6 7 8 9 10 11 很显然,通过DDX_Control,标准控件绑定后虽然有子类化即改写了窗口过程函数. 但是消息的处理基本上是通过pfnWndProc即未绑定前的那个标准窗口过程函数处理的. 对于自绘控件而言,我们会去写一些诸如DrawItem,MeasureItem,OnPaint等消息处理函数, 这时候子类化才真正发挥了作用.如果我们仅仅在MFC中将控件拉到界面中而没有和任何CButton,CEdit等控件变量绑定,
也即没有调用DDX_Control,则我们没有子类化这种控件,它们的窗口过程函数就是Windows 对应标准控件的标准窗口过程函数而没有被MFC改写. 同时该控件的窗口句柄没有和任何CWnd对象绑定,也即在MFC全局的HWND与CWnd的Map表是没有 该控件Item的. 在这种情况下,我们调用::GetDlgItem获取该控件的窗口句柄,再调用CWnd::FromHandlePermanent 获取的CWnd指针为NULL,因为Map表中不存在. 原文:https://blog.csdn.net/hisinwang/article/details/45786719