深入浅出MFC:DDX_Control本质探究
注:以下以名為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?
?
總結
以上是生活随笔為你收集整理的深入浅出MFC:DDX_Control本质探究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高速率AVS整数变换的汇编实现与优化
- 下一篇: 网站程序员的程序员成长之路大概分几个阶段