以前一直用的Qt,最近找工作后,基本就定在windows平台上了,无聊之中研究了下以前没怎么学的MFC。现在看看没有以前那么深奥了。
1.MFC的入口点与简单的执行过程
MFC隐藏了windows程序的入口点winMain,其实是在appmodule.cpp文件下,该入口点调用MFC的全局函数AfxWinMain作为MFC的入口点
然后AfxWinMain会进行一些初始化操作,并执行全局CWinApp的InitInstance函数,即我们重写的一个虚函数
在CWinApp函数中,会初始化我们的窗口指针,调用了该窗口的构造函数,在构造函数中会有一个Create函数,该函数会注册一个窗口类,但是还没有类名和窗口过程,接着会执行一个PreCreateWindow的函数,处理好后,会给一个类名和一个DefWindowProc,并注册好这个窗口类,紧接着,会执行AfxHookWindowCreate函数,作用是将该窗口的窗口过程变为AfxWindowProc,即为一个全局的窗口过程函数,即MFC下所有窗口都共享一个窗口过程。窗口类注册好后,就用CreateWindow的api函数创建了一个窗口,窗口的创建就完成了。
这是由入口点大概的执行流程,中间也忽略了不少函数,但是差不多就是windows sdk写程序的大概流程了,注册窗口类之类的全在里面了。
最主要的还是窗口的消息循环,由上述步骤可以发现,MFC的窗口的WindowProc是一个函数,即全局的AfxWindowProc,接下来记录下MFC的消息流向即消息映射。
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { // special message which identifies the window as using AfxWndProc if (nMsg == WM_QUERYAFXWNDPROC) return 1; // all other messages route through message map CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); ASSERT(pWnd != NULL); ASSERT(pWnd==NULL || pWnd->m_hWnd == hWnd); if (pWnd == NULL || pWnd->m_hWnd != hWnd) return ::DefWindowProc(hWnd, nMsg, wParam, lParam); return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); }
这是MFC窗体所共有的窗口过程,即任何窗体接受到的消息,最终都会流向这里,首先 该函数会由消息的接受句柄来得到它的窗体指针,接着会调用AfxCallWndProc函数.
LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg, WPARAM wParam = 0, LPARAM lParam = 0) { _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); MSG oldState = pThreadState->m_lastSentMsg; // save for nesting pThreadState->m_lastSentMsg.hwnd = hWnd; pThreadState->m_lastSentMsg.message = nMsg; pThreadState->m_lastSentMsg.wParam = wParam; pThreadState->m_lastSentMsg.lParam = lParam; #ifdef _DEBUG _AfxTraceMsg(_T("WndProc"), &pThreadState->m_lastSentMsg); #endif // Catch exceptions thrown outside the scope of a callback // in debug builds and warn the user. LRESULT lResult; TRY { #ifndef _AFX_NO_OCC_SUPPORT // special case for WM_DESTROY if ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL)) pWnd->m_pCtrlCont->OnUIActivate(NULL); #endif // special case for WM_INITDIALOG CRect rectOld; DWORD dwStyle = 0; if (nMsg == WM_INITDIALOG) _AfxPreInitDialog(pWnd, &rectOld, &dwStyle); // delegate to object's WindowProc lResult = pWnd->WindowProc(nMsg, wParam, lParam); // more special case for WM_INITDIALOG if (nMsg == WM_INITDIALOG) _AfxPostInitDialog(pWnd, rectOld, dwStyle); } CATCH_ALL(e) { lResult = AfxProcessWndProcException(e, &pThreadState->m_lastSentMsg); TRACE(traceAppMsg, 0, "Warning: Uncaught exception in WindowProc (returning %ld).\n", lResult); DELETE_EXCEPTION(e); } END_CATCH_ALL pThreadState->m_lastSentMsg = oldState; return lResult; }
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { // OnWndMsg does most of the work, except for DefWindowProc call LRESULT lResult = 0; if (!OnWndMsg(message, wParam, lParam, &lResult)) lResult = DefWindowProc(message, wParam, lParam); return lResult; }
该函数很长,实现在wincore.cpp内,由此我们可以所有的MFC窗体共享一个窗口过程,即AfxWindowProc,然后由这个中转站根据接收句柄来得到需要得到这个消息的对象,然后执行该对象内的窗口过程。
OnWndMsg又是MFC消息映射的实现函数,首先我们得看下消息映射的一系列宏定义。
#define DECLARE_MESSAGE_MAP() \ protected: \ static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \ virtual const AFX_MSGMAP* GetMessageMap() const; \
struct AFX_MSGMAP { const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)(); const AFX_MSGMAP_ENTRY* lpEntries; };
struct AFX_MSGMAP_ENTRY { UINT nMessage; // windows message UINT nCode; // control code or WM_NOTIFY code UINT nID; // control ID (or 0 for windows messages) UINT nLastID; // used for entries specifying a range of control id's UINT_PTR nSig; // signature type (action) or pointer to message # AFX_PMSG pfn; // routine to call (or special value) };
要使用一个消息循环,得添加一个宏。
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \ PTM_WARNING_DISABLE \ const AFX_MSGMAP* theClass::GetMessageMap() const \ { return GetThisMessageMap(); } \ const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \ { \ typedef theClass ThisClass; \ typedef baseClass TheBaseClass; \ static const AFX_MSGMAP_ENTRY _messageEntries[] = \ { #define END_MESSAGE_MAP() \ {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; \ static const AFX_MSGMAP messageMap = \ { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \ return &messageMap; \ } \ PTM_WARNING_RESTORE
下面来看这个静态函数的实现,在这个静态函数中有一个静态的数组,该数组保存着消息对应执行的消息函数,在譬如ON_WM_LBUTTONDOWN这些宏的帮助下把AFX_MSGMAP_ENTRY这个结构初始化。
举个例子,ON_WM_LBUTTONDOWN展开是
#define ON_WM_LBUTTONDOWN() \ { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \ (AFX_PMSG)(AFX_PMSGW) \ (static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, CPoint) > ( &ThisClass :: OnLButtonDown)) },
当住消息循环收到WM_LBUTTONDOWN这个消息时,会根据接收的句柄来调用本窗口对应的窗口过程,即OnWndMsg这个函数,当是下面子控件的通知消息时,就交给自身的OnCommand等函数处理。
if (message == WM_COMMAND) { if (OnCommand(wParam, lParam)) { lResult = 1; goto LReturnTrue; } return FALSE; } // special case for notifies if (message == WM_NOTIFY) { NMHDR* pNMHDR = (NMHDR*)lParam; if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult)) goto LReturnTrue; return FALSE; }
在消息映射表中,指定了特定的消息类型,LButtonDown最终会被此处执行。
case AfxSig_v_u_p: { CPoint point(lParam); (this->*mmf.pfn_v_u_p)(static_cast<UINT>(wParam), point); } break;