您的位置:首页 > 其它

MFC学习小记(1) MFC的入口点与消息循环,消息映射

2017-02-20 21:26 573 查看
以前一直用的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的消息流向即消息映射。

[cpp]
view plain
copy

print?

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);  
}  



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函数.

[cpp]
view plain
copy

print?

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 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 = pWnd->WindowProc(nMsg, wParam, lParam)这句,将会执行由消息接收句柄对应窗体的窗口过程函数。

[cpp]
view plain
copy

print?

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;  
}  



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;
}

在这里又被转发到该窗体的OnWndMsg函数内。

该函数很长,实现在wincore.cpp内,由此我们可以所有的MFC窗体共享一个窗口过程,即AfxWindowProc,然后由这个中转站根据接收句柄来得到需要得到这个消息的对象,然后执行该对象内的窗口过程。

OnWndMsg又是MFC消息映射的实现函数,首先我们得看下消息映射的一系列宏定义。

[cpp]
view plain
copy

print?

#define DECLARE_MESSAGE_MAP() \  
protected: \  
    static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \  
    virtual const AFX_MSGMAP* GetMessageMap() const; \  



#define DECLARE_MESSAGE_MAP() \
protected: \
static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
virtual const AFX_MSGMAP* GetMessageMap() const; \


[cpp]
view plain
copy

print?

struct AFX_MSGMAP  
{  
<span style="white-space:pre">  </span>const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();  
<span style="white-space:pre">  </span>const AFX_MSGMAP_ENTRY* lpEntries;  
};  



struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};


[cpp]
view plain
copy

print?

struct AFX_MSGMAP_ENTRY  
{  
<span style="white-space:pre">  </span>UINT nMessage;   // windows message  
<span style="white-space:pre">  </span>UINT nCode;      // control code or WM_NOTIFY code  
<span style="white-space:pre">  </span>UINT nID;        // control ID (or 0 for windows messages)  
<span style="white-space:pre">  </span>UINT nLastID;    // used for entries specifying a range of control id's  
<span style="white-space:pre">  </span>UINT_PTR nSig;       // signature type (action) or pointer to message #  
<span style="white-space:pre">  </span>AFX_PMSG pfn;    // routine to call (or special value)  
};  



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)
};


这是在定义一个支持消息映射的类必须要写的宏定义。可见这个宏展开就是声明了两个protected权限的函数,一个为静态函数,一个为虚函数。其中两者返回的都是AFX_MSGMAP的结构体,结构体成员为1个函数指针和一个结构体,这个结构体保存了消息与对应执行函数的对应关系。

要使用一个消息循环,得添加一个宏。

[cpp]
view plain
copy

print?

#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  



#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

首先是BEGIN_MESSAGE_MAP这个宏,由于得对照这来,得喝END_MESSAGE_MAP()一起来看。这两个宏的主要目的就是为了实现在类声明时候加入的DECLARE_MESSAGEMAP这个宏的两个函数。实现的第一个虚函数即为调用了这个类的GetThisMessageMap这个静态函数。

下面来看这个静态函数的实现,在这个静态函数中有一个静态的数组,该数组保存着消息对应执行的消息函数,在譬如ON_WM_LBUTTONDOWN这些宏的帮助下把AFX_MSGMAP_ENTRY这个结构初始化。

举个例子,ON_WM_LBUTTONDOWN展开是

[cpp]
view plain
copy

print?

#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)) },  



#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这个消息和这个类想要对应的OnLButtonDown这个函数绑定在一起了,消息映射表中就存在着了一个对应关系。其中的AfxSig代表了不同类型的消息。其中将派生类的OnLButton进行了三次强转,第一次是转为CWnd成员函数指针类型,参数不变,第二次转为了无参版本,第三次转为了CCmdTarget无参版本,无论怎么转换,它都是个成员函数,始终是派生类的成员函数地址。而AfxSig会记录下具体的参数,在OnWndMsg中会解析出来并还原执行。

当住消息循环收到WM_LBUTTONDOWN这个消息时,会根据接收的句柄来调用本窗口对应的窗口过程,即OnWndMsg这个函数,当是下面子控件的通知消息时,就交给自身的OnCommand等函数处理。

[cpp]
view plain
copy

print?

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;  
}  



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最终会被此处执行。

[cpp]
view plain
copy

print?

case AfxSig_v_u_p:  
        {  
            CPoint point(lParam);  
            (this->*mmf.pfn_v_u_p)(static_cast<UINT>(wParam), point);  
        }  
        break;  



case AfxSig_v_u_p:
{
CPoint point(lParam);
(this->*mmf.pfn_v_u_p)(static_cast<UINT>(wParam), point);
}
break;

最终执行到了重写的OnLButtonDown这个函数。union MessageMapFunctions是一个联合体,大小为一个指针大小,存放着一个函数指针,也就是对应的消息响应函数(这里很特别,联合体的定义是所有的消息响应函数,直接根据sig可以获得具体的参数及返回值后无需强转就执行到了那个函数)。假如直接用WM_消息的话,还得每次都强转为相应的指针,而这里只需要依据特定的sig就能执行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: