MFC消息映射


 
突然间想起这个是因为最近看到了AFX_MSGMAP_ENTRY这个结构,结果愣了半天,于是乎干脆又将这一块的相关知识看了一遍,哎,人上了岁数记忆就不行,需要写点东西帮忙记录一下自己的学习历程,不提这个,跑题了:
提起MFC消息,到底程序在哪里处理消息的,因为MFC对windows程序的很多地方进行了封装,导致入口地方不是那么容易找到,不过其实无非就是封装而已——CWinApp(应用程序),在MFC类窗口CxxxApp中查看基类就能找到,调试一下就会发现MFC程序执行的大致流程是:
CWinApp------>AfxWinMain(AfxGetApp,AfxInit)------>CWinApp::InitApplication------>CWinApp::InitINstance(Create)------>PreCreateWindow------>CWnd::ShowWindow,CWnd::UpdateWindow
AfxWinMain------>CWinApp::Run------>AfxInternalPumpMessage
Run这个函数来创建和处理消息循环
BOOL AFXAPI AfxInternalPumpMessage()
    {
        MSG msg;
        ::GetMessage(&msg, NULL, NULL, NULL);
        if (!AfxPreTranslateMessage(&msg))
        {
            ::TranslateMessage(&msg);
	    ::DispatchMessage(&msg);
        }
        return TRUE;
    }</span></span>
显而易见,MFC中处理消息也是利用了win32下的消息处理
那么还是这样的结构:
typedef struct tagMSG { 
HWND hwnd; 
UINT message;  //消息的类型(即事件的类型) 
WPARAM wParam;  //附加或称数据参数1 
LPARAM lParam; 
DWORD time; 
POINT pt; 
} MSG; </span>
有了这个概念之后我们知道,MFC通过消息映射机制来派发或处理各种消息,关于消息映射,我去偷张图片过来:

没有高清无码大图,只能凑合着看了,关于上图可以这样理解:
子窗口在处理消息时需要用FUN1与是在自己的表中找到了,可是要用FUN100的时候,就找不到了,然后通过ParentTable到父类中MsgHandles找,找不到了,那就调用DefWindowProc默认处理了。
而MFC在实现这一过程中,需要去构建这个表,我们经常看到这样的代码,刚开始只是知道这个由vs自动生成,可是并不知道为什么
BEGIN_MESSAGE_MAP(CexampleDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()



我们来详细讲讲这个地方:
转到定义:
#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




上面代码实质上添加了_messageEntries和messageMap(这是构建消息映射的关键)


AFX_MSGMAP_ENTRY _messageEntries[]
   这是一个AFX_MSGMAP_ENTRY 类型的数组变量,是一个静态成员变量,用来容纳类的消息映射条目。一个消息映射条目可以用AFX_MSGMAP_ENTRY结构来描述。
 
AFX_MSGMAP_ENTRY结构的定义如下:
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)
};


 
    从上述结构可以看出,每条映射有两部分的内容:第一部分是关于消息ID的,包括前四个域;第二部分是关于消息对应的执行函数,包括后两个域。
 
    在上述结构的六个域中,pfn是一个指向CCmdTarger成员函数的指针。函数指针的类型定义如下:
 
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
 
    当使用一条或者多条消息映射条目初始化消息映射数组时,各种不同类型的消息函数都被转换成这样的类型:不接收参数,也不返回参数的类型。因为所有可以有消息映射的类都是从CCmdTarge派生的,所以可以实现这样的转换。
    nSig是一个标识变量,用来标识不同原型的消息处理函数,每一个不同原型的消息处理函数对应一个不同的nSig。在消息分发时,MFC内部根据nSig把消息派发给对应的成员函数处理,实际上,就是根据nSig的值把pfn还原成相应类型的消息处理函数并执行它。
 
  第二个成员变量的声明
   AFX_MSGMAP messageMap;
   这是一个AFX_MSGMAP类型的静态成员变量,从其类型名称和变量名称可以猜出,它是一个包含了消息映射信息的变量。的确,它把消息映射的信息(消息映射数组)和相关函数打包在一起,也就是说,得到了一个消息处理类的该变量,就得到了它全部的消息映射数据和功能。AFX_MSGMAP结构的定义如下:
struct AFX_MSGMAP
{
    const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
    const AFX_MSGMAP_ENTRY* lpEntries;
};


 
    成员函数 GetMessageMap() 用来得到自身消息映射的函数。

有了上面的一个概念之后,应该很容易理解MFC在处理一个消息的时候,往_messageEntries里加入要处理的函数,然后通过messageMap就可以得到消息映射数组_messageEntries和基类的消息映射数组的地址了


回头看看MFC的消息映射机制,了然了,在处理消息的时候构建的那张表,

BEGIN_MESSAGE_MAP(CexampleDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()



第一个处理的消息是ON_WM_SYSCOMMAND(),处理消息函数(右击转到定义)OnSysCommand

建好消息映射表后,消息就会流向到指定的处理函数里。


差不多消息映射机制就回顾了这么多了,写的很乱,放元旦搁了好几天又开始写的,思路接不上了呃。




发布了25 篇原创文章 · 获赞 12 · 访问量 7万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章