日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MFC原理 消息传递

發布時間:2023/12/18 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MFC原理 消息传递 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一丶簡介  

    通過上一講我們的消息映射表.我們得知. 消息映射表 會保存父類的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 destroyedHINSTANCE 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++ objectreturn FALSE;}}m_strTitle = lpszWindowName; // save title for laterif (!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 parametersCREATESTRUCT 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 _DEBUGif (hWnd == NULL){TRACE(traceAppMsg, 0, "Warning: Window creation failed: GetLastError returns 0x%8.8X\n",GetLastError());} #endifif (!AfxUnhookWindowCreate())PostNcDestroy(); // cleanup if CreateWindowEx fails too soonif (hWnd == NULL)return FALSE;ASSERT(hWnd == m_hWnd); // should have been set in send msg hookreturn 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 onceASSERT(pThreadState->m_pWndInit == NULL); // hook not already in progresspThreadState->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 callLRESULT 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 AfxWndProcif (nMsg == WM_QUERYAFXWNDPROC)return 1;// all other messages route through message mapCWnd* 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 commandsif (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 notifiesif (message == WM_NOTIFY){NMHDR* pNMHDR = (NMHDR*)lParam;if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))goto LReturnTrue;return FALSE;}// special case for activationif (message == WM_ACTIVATE)_AfxHandleActivate(this, wParam, CWnd::FromHandle((HWND)lParam));// special case for set cursor HTERRORif (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 hitlpEntry = pMsgCache->lpEntry;winMsgLock.Unlock();if (lpEntry == NULL)return FALSE;// cache hit, and it needs to be handledif (message < 0xC000)goto LDispatch;elsegoto LDispatchRegistered;}else{// not in cache, look for itpMsgCache->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 messageif ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,message, 0, 0)) != NULL){pMsgCache->lpEntry = lpEntry;winMsgLock.Unlock();goto LDispatch;}}else{// registered windows messagelpEntry = pMessageMap->lpEntries;while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL){UINT* pnID = (UINT*)(lpEntry->nSig);ASSERT(*pnID >= 0xC000 || *pnID == 0);// must be successfully registeredif (*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 等技術,期待你的進入。

總結

以上是生活随笔為你收集整理的MFC原理 消息传递的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。