博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MFC原理 消息传递
阅读量:4302 次
发布时间:2019-05-27

本文共 11427 字,大约阅读时间需要 38 分钟。

一丶简介  

    通过上一讲我们的消息映射表.我们得知. 消息映射表 会保存父类的MessageMap 以及自己当前的消息结构体数组.

消息传递是一层一层的递进的.那么我们现在要看一下怎么递进的.

要学习的知识

    1.窗口创建的流程.以及默认的回调函数

    2.消息处理流程

 

二丶窗口创建的流程.以及默认的回调函数

  我们要看窗口创建.那么就需要跟进 MFC源码去看. 首先就是对我们的Create函数下断点.看一下做了什么事情.

进入Create函数内部.

复制代码

BOOL CFrameWnd::Create(LPCTSTR lpszClassName,    LPCTSTR lpszWindowName,    DWORD dwStyle,    const RECT& rect,    CWnd* pParentWnd,    LPCTSTR lpszMenuName,    DWORD dwExStyle,    CCreateContext* pContext){    HMENU hMenu = NULL;    if (lpszMenuName != NULL)    //首先判断我们有彩蛋吗.如果有加载我们的菜单.    {        // load in a menu that will get destroyed when window gets destroyed        HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);        if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)        {            TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");            PostNcDestroy();            // perhaps delete the C++ object            return FALSE;        }    }    m_strTitle = lpszWindowName;    // save title for later    if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,        rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,     //内部还是调用的CreateEx函数.所以我们继续跟进去查看.        pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))    {        TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.\n");        if (hMenu != NULL)            DestroyMenu(hMenu);        return FALSE;    }    return TRUE;}

复制代码

CreateEx查看. 窗口过程处理函数.

复制代码

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,    LPCTSTR lpszWindowName, DWORD dwStyle,    int x, int y, int nWidth, int nHeight,    HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam){    ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) ||         AfxIsValidAtom(lpszClassName));    ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));        // allow modification of several common create parameters    CREATESTRUCT cs;                                //熟悉的窗口类创建但是是一个新的结构.    cs.dwExStyle = dwExStyle;    cs.lpszClass = lpszClassName;    cs.lpszName = lpszWindowName;    cs.style = dwStyle;    cs.x = x;    cs.y = y;    cs.cx = nWidth;                               //其中高版本在这里就会设置默认的窗口回调.    cs.cy = nHeight;    cs.hwndParent = hWndParent;    cs.hMenu = nIDorHMenu;    cs.hInstance = AfxGetInstanceHandle();    cs.lpCreateParams = lpParam;    if (!PreCreateWindow(cs))                      //内部进行风格设置以及注册窗口类.    {        PostNcDestroy();        return FALSE;    }    AfxHookWindowCreate(this);                                //Hook 窗口回调函数.设置窗口回调函数. Create消息来到的时候    HWND hWnd = CreateWindowEx(cs.dwExStyle, cs.lpszClass,            cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,            cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);#ifdef _DEBUG    if (hWnd == NULL)    {        TRACE(traceAppMsg, 0, "Warning: Window creation failed: GetLastError returns 0x%8.8X\n",            GetLastError());    }#endif    if (!AfxUnhookWindowCreate())        PostNcDestroy();        // cleanup if CreateWindowEx fails too soon    if (hWnd == NULL)        return FALSE;    ASSERT(hWnd == m_hWnd); // should have been set in send msg hook    return TRUE;}

复制代码

新的结构

复制代码

typedef struct tagCREATESTRUCTA {    LPVOID      lpCreateParams;    HINSTANCE   hInstance;    HMENU       hMenu;    HWND        hwndParent;    int         cy;    int         cx;    int         y;    int         x;    LONG        style;    LPCSTR      lpszName;    LPCSTR      lpszClass;    DWORD       dwExStyle;} CREATESTRUCTA, *LPCREATESTRUCTA;

复制代码

新的类跟注册窗口的时候很相似. 我们看一下窗口回调在哪里设置的吧.

窗口回调函数 是通过

AfxHookWindowCreate 函数来进行设置.而这个函数本身就是一个Windows自带的HOOK. 其真正的窗口回调函数.是在内部中.设置回调的时候 新的回调函数进行设置的.

复制代码

void AFXAPI AfxHookWindowCreate(CWnd* pWnd){    _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();    if (pThreadState->m_pWndInit == pWnd)        return;    if (pThreadState->m_hHookOldCbtFilter == NULL)    {        pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,           //设置CBT HOOK . _AfxCbtFilterHook里面才是真正的替换窗口过程处理函数.            _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());        if (pThreadState->m_hHookOldCbtFilter == NULL)            AfxThrowMemoryException();    }    ASSERT(pThreadState->m_hHookOldCbtFilter != NULL);    ASSERT(pWnd != NULL);    ASSERT(pWnd->m_hWnd == NULL);   // only do once    ASSERT(pThreadState->m_pWndInit == NULL);   // hook not already in progress    pThreadState->m_pWndInit = pWnd;}

复制代码

看一下函数内部

复制代码

LRESULT CALLBACK_AfxCbtFilterHook( int  code, WPARAM wParam, LPARAM lParam)   {     //  …      WNDPROC afxWndProc  =  AfxGetAfxWndProc();    oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc);  //重要的位置就是这里.使用的SetWindowLong这个函数.将窗口过程函数替换为了 afxWndProc     //  …  }  WNDPROC AFXAPI AfxGetAfxWndProc()   {     //  …      return   & AfxWndProc;}

复制代码

总结: 通过上面代码我们得知了.窗口在创建的时候以及窗口回调进行的一些列设置

  1.调用Create创建窗口

  2.设置窗口类.

  3.注册窗口类.

  4.通过AfxHookWindowsCreate 将我们的默认窗口回调改成了 afxWndProc

  5.窗口创建完毕.

上面五条则是我们创建窗口的时候进行的一系列操作. 所以我们的消息处理函数变成了 afxWndProc了这个消息处理函数就会在发生消息的时候第一个来到.

 

三丶消息处理流程

  通过上面我们得知了窗口处理回调已经更改了. 现在我们直接对我们的消息下段点.就可以验证一下.是否是我们的函数首次来到.

对我们的按钮点击下段点. 通过栈回朔一层一层往上看.

第一层

 第一层级就是判断我们的消息.进行不同的处理. 所以不重要.跳过.

第二层消息处理层这一层就是我们要进行的消息处理的一层.如果消息不处理则默认交给默认的处理函数进行处理

复制代码

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

复制代码

第n层.因为不重要了.所以我们栈回朔到最顶层即可.

复制代码

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

复制代码

 我们如果自己去看.可以看到.WindProc函数是被外部调用的. 而且这个函数是一个虚函数.也就是说如果我们重写了消息处理函数.那么我们自己就可以处理消息了.

如果自己不处理.那么默认就调用 CWnd里面的消息处理函数了

而里面的 OnMsg函数同样也是一个虚函数. 如果不该写一样调用父类的

调试可以看一下.

 

 

 只是部分代码截图.如果有兴趣可以深究. 我们知道. Windows 消息分为三大类. 

1.普通消息.  

2.菜单消息. WM_COMMAND

3.WM_NOTIFY  

而我们的鼠标点击消息就是普通消息.  如果来菜单消息了就统一为WM_COMMAND消息. 代表的是通知类消息.

而我们的这个方法就是判断消息是什么类型的. 进行不同消息的处理. 

如果说来的消息都不包括的话.那么下面就开始遍历消息映射表.然后进行消息查找.

完整代码

复制代码

代码太多删减一下.BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult){    LRESULT lResult = 0;    union MessageMapFunctions mmf;    mmf.pfn = 0;    CInternalGlobalLock winMsgLock;    // special case for commands    if (message == WM_COMMAND)    {        if (OnCommand(wParam, lParam))        {            lResult = 1;            goto LReturnTrue;        }        return FALSE;    }    if (message == WM_CREATE && m_pDynamicLayout != NULL)    {        ASSERT_VALID(m_pDynamicLayout);        if (!m_pDynamicLayout->Create(this))        {            delete m_pDynamicLayout;            m_pDynamicLayout = NULL;        }        else        {            InitDynamicLayout();        }    }    // special case for notifies    if (message == WM_NOTIFY)    {        NMHDR* pNMHDR = (NMHDR*)lParam;        if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))            goto LReturnTrue;        return FALSE;    }    // special case for activation    if (message == WM_ACTIVATE)        _AfxHandleActivate(this, wParam, CWnd::FromHandle((HWND)lParam));    // special case for set cursor HTERROR    if (message == WM_SETCURSOR &&        _AfxHandleSetCursor(this, (short)LOWORD(lParam), HIWORD(lParam)))    {        lResult = 1;        goto LReturnTrue;    }   // special case for windows that contain windowless ActiveX controls   .......const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap();   //获得自己当前的消息映射表. 下面就开始遍历消息判断消息了    UINT iHash; iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1);    winMsgLock.Lock(CRIT_WINMSGCACHE);    AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];    const AFX_MSGMAP_ENTRY* lpEntry;    if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)    {        // cache hit        lpEntry = pMsgCache->lpEntry;        winMsgLock.Unlock();        if (lpEntry == NULL)            return FALSE;        // cache hit, and it needs to be handled        if (message < 0xC000)            goto LDispatch;        else            goto LDispatchRegistered;    }    else    {        // not in cache, look for it        pMsgCache->nMsg = message;        pMsgCache->pMessageMap = pMessageMap;        for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;            pMessageMap = (*pMessageMap->pfnGetBaseMap)())        {            // Note: catch not so common but fatal mistake!!            //      BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)            ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());            if (message < 0xC000)            {                // constant window message                if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,                    message, 0, 0)) != NULL)                {                    pMsgCache->lpEntry = lpEntry;                    winMsgLock.Unlock();                    goto LDispatch;                }            }            else            {                // registered windows message                lpEntry = pMessageMap->lpEntries;                while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)                {                    UINT* pnID = (UINT*)(lpEntry->nSig);                    ASSERT(*pnID >= 0xC000 || *pnID == 0);                        // must be successfully registered                    if (*pnID == message)                    {                        pMsgCache->lpEntry = lpEntry;                        winMsgLock.Unlock();                        goto LDispatchRegistered;                    }                    lpEntry++;      // keep looking past this one                }            }        }  pMsgCache->lpEntry = NULL;  winMsgLock.Unlock();  return FALSE;}LDispatch:                              因为自己的当前MessageMap表i中保存着消息结构体数组. 所以遍历可以得出 消息.以及对应的函数指针ASSERT(message < 0xC000);mmf.pfn = lpEntry->pfn;                                     然后其结果保存在 mmf.pfn中.  个mmf是一个结构.联合体结构. 具体下方可以看一下这个结构.其实结构其实就是保存了函数返回值以及类型信息switch (lpEntry->nSig)                                      我们消息结构体中前边也讲过.有一个sig标识.代表了函数的返回值以及参数类型. 进而通过不同的函数.调用不同的消息处理函数{default:ASSERT(FALSE);break;case AfxSig_l_p: 结构.只显示部分union MessageMapFunctions{AFX_PMSG pfn; // generic member function pointerBOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_D)(CDC*);BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_b)(BOOL);BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_u)(UINT);BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_h)(HANDLE);BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_u_u)(CWnd*, UINT, UINT);BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_COPYDATASTRUCT)(CWnd*, COPYDATASTRUCT*);BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_HELPINFO)(LPHELPINFO);HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_W_u)(CDC*, CWnd*, UINT);HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_u)(CDC*, UINT);int (AFX_MSG_CALL CCmdTarget::*pfn_i_u_W_u)(UINT, CWnd*, UINT);}

复制代码

 如果是 WM_COMMAND 或者 WM_NOTIFY 消息.则取对应的 OnCommand中. 这个函数跟上面类似.也是遍历消息映射表去寻找.有兴趣的可以自己看下源码.

坚持两字,简单,轻便,但是真正的执行起来确实需要很长很长时间.当你把坚持两字当做你要走的路,那么你总会成功. 想学习,有问题请加群.群号:725864912群名称: 逆向学习小分队 群里有大量学习资源. 以及定期直播答疑.有一个良好的学习氛围. 涉及到外挂反外挂病毒 司法取证加解密 驱动过保护 VT 等技术,期待你的进入。

转载地址:http://azlws.baihongyu.com/

你可能感兴趣的文章
消息队列2
查看>>
C++ 线程同步之临界区CRITICAL_SECTION
查看>>
测试—自定义消息处理
查看>>
MFC中关于虚函数的一些问题
查看>>
判断浏览器类型
查看>>
JS 获得地址栏参数
查看>>
利用jquery 给指定table 添加删除行
查看>>
java 用poi框架读取excel 2010
查看>>
Sybase存储过程的使用
查看>>
java 完美读取properties 文件中属性值
查看>>
工作反思
查看>>
jdbc PreparedStatement Statement 比较
查看>>
JDBC PreparedStatement 处理区间查询
查看>>
SSH2 DAO注入sessionfactory的方式汇总
查看>>
struts.xml的加载路径及配置问题
查看>>
安全深度学习框架PySyft
查看>>
游戏引擎mota-js-v3.0 施工记录
查看>>
LeetCode笔记:Move Zeros[Difficulty: Easy]
查看>>
二叉搜索树的一种构造方法
查看>>
提防向量迭代器变成野指针
查看>>