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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

深入解析Windows窗口创建和消息分发

發(fā)布時(shí)間:2025/3/12 windows 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入解析Windows窗口创建和消息分发 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Windows GUI采用基于事件驅(qū)動(dòng)的編程模型,事實(shí)上幾乎所有的界面庫都是這樣做的。在純粹的Window32 SDK編程時(shí)代,人們還可以搞懂整個(gè)Windows窗體創(chuàng)建和消息的流通過程,但是在現(xiàn)在各種框架的包裝下很多在Window32 SDK下很明顯易懂的東西顯得不是那么簡單了。本文力圖去繁求簡,教你看懂所有框架的基本構(gòu)造,而事實(shí)上對(duì)于了解這一切的人來說,這些界面框架的設(shè)計(jì)都是如出一轍的,希望看完本文,再去看常見的MFC/WTL等框架時(shí),不會(huì)再覺得有任何的不適。

C程序的處理辦法

1.基本原理

先說古老的Win32 SDK的做法,他們很明顯,這里還是先貼上代碼,為了縮減篇幅很多地方我就省略了

[cpp] view plaincopy print?
  • int?WINAPI?WinMain?(HINSTANCE?hInstance,?HINSTANCE?hPrevInstance,?PSTR?szCmdLine,?int?iCmdShow)??
  • {??
  • ????static?TCHAR?szAppName[]?=?TEXT?("TestClass");??
  • ????HWND?????????hwnd;??
  • ????MSG??????????msg;??
  • ????WNDCLASSEX???wndclassex?=?{0};??
  • ??
  • ????//1.設(shè)計(jì)窗口類??
  • ????wndclassex.cbSize????????=?sizeof(WNDCLASSEX);??
  • ????wndclassex.style?????????=?CS_HREDRAW?|?CS_VREDRAW;??
  • ????wndclassex.lpfnWndProc???=?WndProc?...??
  • ??????
  • ????//2.注冊(cè)窗口類??
  • ????if?(!RegisterClassEx?(&wndclassex))??
  • ????{??
  • ????????MessageBox?(NULL,?TEXT?("RegisterClassEx?failed!"),?szAppName,?MB_ICONERROR);??
  • ????????return?0;??
  • ????}??
  • ??
  • ????//3.創(chuàng)建窗口??
  • ????hwnd?=?CreateWindowEx?(WS_EX_OVERLAPPEDWINDOW,???
  • ??????????????????????????szAppName,???
  • ??????????????????????????...??
  • ??????
  • ????//4.顯示窗口??
  • ????ShowWindow?(hwnd,?iCmdShow);??
  • ????UpdateWindow?(hwnd);??
  • ??????
  • ????//5.開始消息循環(huán),又稱消息泵??
  • ????while?(GetMessage?(&msg,?NULL,?0,?0))??
  • ????{??
  • ????????TranslateMessage?(&msg);??
  • ????????DispatchMessage?(&msg);??
  • ????}??
  • ????return?msg.wParam;??
  • }??
  • ??
  • //回調(diào)函數(shù)中做消息分發(fā)??
  • LRESULT?CALLBACK?WndProc?(HWND?hwnd,?UINT?message,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ????HDC?hdc;??
  • ????PAINTSTRUCT?ps;??
  • ??
  • ????//分發(fā)??
  • ????switch?(message)??
  • ????{??
  • ????case?WM_CREATE:??
  • ????????return?(0);??
  • ??????????
  • ????case?WM_PAINT:??
  • ????????...??
  • ????????return?(0);??
  • ??????????
  • ????case?WM_DESTROY:??
  • ????????PostQuitMessage?(0);??
  • ????????return?(0);??
  • ????}??
  • ??
  • ????//默認(rèn)處理函數(shù)??
  • ????return?DefWindowProc?(hwnd,?message,?wParam,?lParam);??
  • }??
  • int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {static TCHAR szAppName[] = TEXT ("TestClass");HWND hwnd;MSG msg;WNDCLASSEX wndclassex = {0};//1.設(shè)計(jì)窗口類wndclassex.cbSize = sizeof(WNDCLASSEX);wndclassex.style = CS_HREDRAW | CS_VREDRAW;wndclassex.lpfnWndProc = WndProc ...//2.注冊(cè)窗口類if (!RegisterClassEx (&wndclassex)){MessageBox (NULL, TEXT ("RegisterClassEx failed!"), szAppName, MB_ICONERROR);return 0;}//3.創(chuàng)建窗口hwnd = CreateWindowEx (WS_EX_OVERLAPPEDWINDOW, szAppName, ...//4.顯示窗口ShowWindow (hwnd, iCmdShow);UpdateWindow (hwnd);//5.開始消息循環(huán),又稱消息泵while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg);DispatchMessage (&msg);}return msg.wParam; }//回調(diào)函數(shù)中做消息分發(fā) LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {HDC hdc;PAINTSTRUCT ps;//分發(fā)switch (message){case WM_CREATE:return (0);case WM_PAINT:...return (0);case WM_DESTROY:PostQuitMessage (0);return (0);}//默認(rèn)處理函數(shù)return DefWindowProc (hwnd, message, wParam, lParam); } 設(shè)計(jì)窗口類和注冊(cè)窗口類可稱為 InitApplication,即初始化Windows 應(yīng)用所需要做的工作,這個(gè)窗口類 可以是公用的。

    創(chuàng)建一個(gè)窗口和顯示可稱為InitInstance,即初始化一個(gè)Windows 應(yīng)用實(shí)例所需要做的工作,對(duì)每個(gè)窗體來說這都是唯一的,可做定制化修改。

    開啟消息泵可稱為Run,一單消息泵開啟,意味著一個(gè)程序開始接受消息和分發(fā)消息,整個(gè)應(yīng)用程序算是開始運(yùn)行了。

    在WndProc中做的是判斷對(duì)應(yīng)的消息,然后做對(duì)應(yīng)的處理工作。


    2.改進(jìn)窗口創(chuàng)建

    可以看到,最原始的Win32 SDK編程完全是面向過程編程創(chuàng)建,比較繁瑣,為了簡化編寫,可在VS2008里面打開新建一個(gè)Win32 程序可以看到代碼如下:

    [cpp] view plaincopy print?
  • //?1.設(shè)計(jì)和注冊(cè)消息類??
  • ...??
  • MyRegisterClass(hInstance);??
  • ??
  • //?2.執(zhí)行應(yīng)用程序初始化:??
  • if?(!InitInstance?(hInstance,?nCmdShow))??
  • {??
  • ????return?FALSE;??
  • }??
  • ??
  • hAccelTable?=?LoadAccelerators(hInstance,?MAKEINTRESOURCE(IDC_WIN321));??
  • ??
  • //?3.主消息循環(huán):??
  • while?(GetMessage(&msg,?NULL,?0,?0))??
  • {??
  • ????if?(!TranslateAccelerator(msg.hwnd,?hAccelTable,?&msg))??
  • ????{??
  • ????????TranslateMessage(&msg);??
  • ????????DispatchMessage(&msg);??
  • ????}??
  • }??
  • ??
  • return?(int)?msg.wParam;??
  • // 1.設(shè)計(jì)和注冊(cè)消息類...MyRegisterClass(hInstance);// 2.執(zhí)行應(yīng)用程序初始化:if (!InitInstance (hInstance, nCmdShow)){return FALSE;}hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN321));// 3.主消息循環(huán):while (GetMessage(&msg, NULL, 0, 0)){if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){TranslateMessage(&msg);DispatchMessage(&msg);}}return (int) msg.wParam;
    可以看到按照在基本原理中講的,這里微軟的做法也一樣, 按照三大部分封裝到函數(shù)中,簡化操作,InitApplication命名成了MyRegisterClass而已。


    3.改進(jìn)消息分發(fā)

    前面講了改進(jìn)窗口創(chuàng)建,但是消息分發(fā)仍然是一團(tuán)亂麻,所有的消息響應(yīng)都塞在switch case中,這里我們自然想到和窗口創(chuàng)建一樣,對(duì)應(yīng)的處理分發(fā)到函數(shù)中。而事實(shí)上微軟也確實(shí)是這么做的,微軟提供了頭文件WindowsX.h來幫助我們分發(fā)消息,具體如下:

    [cpp] view plaincopy print?
  • LRESULT?CALLBACK?WndProc(HWND?hWnd,?UINT?message,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ????switch?(message)??
  • ????{??
  • ????????HANDLE_MSG(hWnd,?WM_PAINT,?Cls_OnPaint);??
  • ????????HANDLE_MSG(hWnd,?WM_DESTROY,?Cls_OnDestroy);??
  • ????}??
  • ??
  • ????return?DefWindowProc(hWnd,?message,?wParam,?lParam);??
  • }??
  • ??
  • void?Cls_OnPaint(HWND?hwnd)??
  • {??
  • ????HDC?hdc;??
  • ????PAINTSTRUCT?ps;??
  • ??
  • ????hdc?=?BeginPaint(hwnd,?&ps);??
  • ????//...??
  • ????EndPaint(hwnd,?&ps);??
  • }??
  • ??
  • void?Cls_OnDestroy(HWND?hwnd)??
  • {??
  • ????PostQuitMessage(0);??
  • }??
  • LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {switch (message){HANDLE_MSG(hWnd, WM_PAINT, Cls_OnPaint);HANDLE_MSG(hWnd, WM_DESTROY, Cls_OnDestroy);}return DefWindowProc(hWnd, message, wParam, lParam); }void Cls_OnPaint(HWND hwnd) {HDC hdc;PAINTSTRUCT ps;hdc = BeginPaint(hwnd, &ps);//...EndPaint(hwnd, &ps); }void Cls_OnDestroy(HWND hwnd) {PostQuitMessage(0); }可以看到,這里借助于HANDLE_MSG宏讓消息對(duì)應(yīng)到具體的處理函數(shù)上,HANDLE_MSG展開如下:

    [cpp] view plaincopy print?
  • #define?HANDLE_MSG(hwnd,?message,?fn)????\??
  • ????case?(message):?return?HANDLE_##message((hwnd),?(wParam),?(lParam),?(fn))??
  • #define HANDLE_MSG(hwnd, message, fn) \case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))HANDLE##message為處理函數(shù)
    可看到這里 借助宏來減少switch case代碼的編寫量,但實(shí)際代碼內(nèi)容是一樣的。

    實(shí)際上對(duì)話框的處理略有不同,如下:

    [cpp] view plaincopy print?
  • #define?chHANDLE_DLGMSG(hwnd,?message,?fn)?case?(message):?\??
  • ????return?(SetDlgMsgResult(hwnd,?uMsg,?HANDLE_##message((hwnd),?(wParam),?(lParam),?(fn))))??
  • ??
  • INT_PTR?CALLBACK?Dlg_Proc(HWND?hDlg,?UINT?message,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ????UNREFERENCED_PARAMETER(lParam);??
  • ????switch?(message)??
  • ????{??
  • ????????chHANDLE_DLGMSG(hwnd,?WM_INITDIALOG,?Dlg_OnInitDialog);??
  • ????????chHANDLE_DLGMSG(hwnd,?WM_COMMAND,????Dlg_OnCommand);??
  • ????}??
  • ??
  • ????return?(INT_PTR)FALSE;??
  • }??
  • #define chHANDLE_DLGMSG(hwnd, message, fn) case (message): \return (SetDlgMsgResult(hwnd, uMsg, HANDLE_##message((hwnd), (wParam), (lParam), (fn))))INT_PTR CALLBACK Dlg_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {UNREFERENCED_PARAMETER(lParam);switch (message){chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);}return (INT_PTR)FALSE; }這里的chHANDLE_DLGMSG是仿照HANDLE_MSG自定義的。

    C++程序的處理辦法

    在C++時(shí)代,人們提倡面向?qū)ο缶幊?#xff0c;對(duì)于窗口的創(chuàng)建和消息的分發(fā)響應(yīng)都是窗口的行為,所以幾乎所有的框架都是想辦法把這兩者封裝在一起,這也是我們講解的重點(diǎn)。對(duì)于C++程序我們先講大框架,再講窗口類封裝。

    1.MFC大框架

    盜用侯捷先生一張圖,MFC的基本層次結(jié)構(gòu)如下:


    MFC將開啟消息循環(huán)放到CWinThread中,將窗口注冊(cè)、創(chuàng)建、消息分發(fā)響應(yīng)均放到CWnd中處理,這樣所有和窗口處理相關(guān)的都是由同一個(gè)類來完成,符合C++的封裝特性,也便于使用。

    VS安裝完目錄VC\atlmfc\src\mfc下有部分mfc源碼,我們直接看微軟的實(shí)現(xiàn)。

    首先,入口文件appmodul.cpp中定義入口如下:

    [cpp] view plaincopy print?
  • extern?"C"?int?WINAPI?_tWinMain(HINSTANCE?hInstance,?HINSTANCE?hPrevInstance,?_In_?LPTSTR?lpCmdLine,?int?nCmdShow)??
  • #pragma?warning(suppress:?4985)??
  • {??
  • ????//?call?shared/exported?WinMain??
  • ????return?AfxWinMain(hInstance,?hPrevInstance,?lpCmdLine,?nCmdShow);??
  • }??
  • extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow) #pragma warning(suppress: 4985) {// call shared/exported WinMainreturn AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); }
    然后,在winmain.cpp查看定義AfxWinMain如下

    [cpp] view plaincopy print?
  • int?AFXAPI?AfxWinMain(HINSTANCE?hInstance,?HINSTANCE?hPrevInstance,?_In_?LPTSTR?lpCmdLine,?int?nCmdShow)??
  • {??
  • ????...??
  • ??
  • ????//?AFX?internal?initialization??
  • ????if?(!AfxWinInit(hInstance,?hPrevInstance,?lpCmdLine,?nCmdShow))??
  • ????????goto?InitFailure;??
  • ??
  • ????//?App?global?initializations?(rare)??
  • ????if?(pApp?!=?NULL?&&?!pApp->InitApplication())??
  • ????????goto?InitFailure;??
  • ??
  • ????//?Perform?specific?initializations??
  • ????if?(!pThread->InitInstance())??
  • ????{??
  • ????????...??
  • ????}??
  • ????nReturnCode?=?pThread->Run();??
  • ??
  • ????...??
  • }??
  • int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow) {...// AFX internal initializationif (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))goto InitFailure;// App global initializations (rare)if (pApp != NULL && !pApp->InitApplication())goto InitFailure;// Perform specific initializationsif (!pThread->InitInstance()){...}nReturnCode = pThread->Run();... }
    所以還是InitApplication、InitInstance、Run三大塊,AfxWinInit用于做一些框架的初始化工作。

    CWinApp::InitApplication在appcore.cpp中,和C程序略有不同,這里的工作主要是Doc模板管理器的初始化工作。

    CThread::InitInstance虛函數(shù)會(huì)被用戶改寫,在這當(dāng)中調(diào)用CWnd完成窗口的注冊(cè)和創(chuàng)建,這個(gè)在之后一起講

    CThread::Run在thrdcore.cpp中,Run-》PumpMessage-》AfxInternalPumpMessage完成消息泵的開啟,如下:

    [cpp] view plaincopy print?
  • BOOL?AFXAPI?AfxInternalPumpMessage()??
  • {??
  • ????_AFX_THREAD_STATE?*pState?=?AfxGetThreadState();??
  • ??
  • ????if?(!::GetMessage(&(pState->m_msgCur),?NULL,?NULL,?NULL))??
  • ????{??
  • ...??
  • ????????return?FALSE;??
  • ????}??
  • ??
  • ...??
  • ??
  • ????if?(pState->m_msgCur.message?!=?WM_KICKIDLE?&&?!AfxPreTranslateMessage(&(pState->m_msgCur)))??
  • ????{??
  • ????????::TranslateMessage(&(pState->m_msgCur));??
  • ????????::DispatchMessage(&(pState->m_msgCur));??
  • ????}??
  • ??return?TRUE;??
  • }??
  • BOOL AFXAPI AfxInternalPumpMessage() {_AFX_THREAD_STATE *pState = AfxGetThreadState();if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL)){ ...return FALSE;}...if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur))){::TranslateMessage(&(pState->m_msgCur));::DispatchMessage(&(pState->m_msgCur));}return TRUE; }

    2.MFC封裝窗口創(chuàng)建和消息分發(fā)

    利用C++面向?qū)ο蟮奶卣?#xff0c;將窗口創(chuàng)建和消息分發(fā)、響應(yīng)分裝在一個(gè)類中,這樣一個(gè)窗口類對(duì)應(yīng)一個(gè)實(shí)際窗口,非常簡單直觀。

    首先我們思考下,把窗口創(chuàng)建和消息分發(fā)封裝在一起有哪些難點(diǎn)?

    1.怎么將不同的窗口過程勾到一起

    歷史經(jīng)驗(yàn)告訴我們,專制往往有時(shí)候好辦事。如果每個(gè)窗口都有自己的窗口過程,那樣處理起來就比較麻煩,最好的做法是所有的窗口在同一個(gè)窗口過程中控制分發(fā)。

    2.同一窗口過程中怎樣將不同的hwnd消息分發(fā)給對(duì)應(yīng)的CWnd類去處理響應(yīng)

    因?yàn)榇翱诨卣{(diào)函數(shù)的限制,回調(diào)函數(shù)不能擁有對(duì)應(yīng)CWnd類的this指針,也就是說來了窗口消息,怎樣才能辨別對(duì)應(yīng)的hwnd對(duì)應(yīng)的CWnd,把消息分發(fā)給CWnd去處理呢?

    3.最后,如果CWnd拿到了消息,怎樣去簡單有效的去處理和響應(yīng)呢

    我們說過消息的響應(yīng)也是在Cwnd中處理,怎樣將拿到的消息對(duì)應(yīng)成具體的類成員函數(shù)呢?


    這些問題串通后,MFC的做法,我們畫一張消息流通圖如下:


    a).窗口創(chuàng)建

    同樣我們拿源碼來解釋,


    在MFC中我們自定義的窗口類繼承關(guān)系如下:

    CWnd->CFrameWnd->CMyFrameWnd

    winfrm.cpp中CFrameWnd::LoadFrame

    首先,調(diào)用GetIconWndClass->AfxRegisterWndClass完成窗口類設(shè)計(jì)和注冊(cè)

    然后,調(diào)用CFrameWnd::Create->CWnd::CreateEx完成窗口創(chuàng)建,如下:

    [cpp] view plaincopy print?
  • BOOL?CFrameWnd::LoadFrame(UINT?nIDResource,?DWORD?dwDefaultStyle,?CWnd*?pParentWnd,?CCreateContext*?pContext)??
  • {??
  • ...??
  • ????LPCTSTR?lpszClass?=?GetIconWndClass(dwDefaultStyle,?nIDResource);??
  • ????CString?strTitle?=?m_strTitle;??
  • ????if?(!Create(lpszClass,?strTitle,?dwDefaultStyle,?rectDefault,?pParentWnd,?ATL_MAKEINTRESOURCE(nIDResource),?0L,?pContext))??
  • ????{??
  • ????????return?FALSE;???//?will?self?destruct?on?failure?normally??
  • ????}??
  • ??
  • ...??
  • }??
  • BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext) { ...LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);CString strTitle = m_strTitle;if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault, pParentWnd, ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext)){return FALSE; // will self destruct on failure normally}... } [cpp] view plaincopy print?
  • LPCTSTR?CFrameWnd::GetIconWndClass(DWORD?dwDefaultStyle,?UINT?nIDResource)??
  • {??
  • ...??
  • ????if?(hIcon?!=?NULL)??
  • ????{??
  • ...??
  • ????????{??
  • ????????????//?register?a?very?similar?WNDCLASS??
  • ????????????return?AfxRegisterWndClass(wndcls.style,?wndcls.hCursor,?wndcls.hbrBackground,?hIcon);??
  • ????????}??
  • ????}??
  • ????return?NULL;????????//?just?use?the?default??
  • }??
  • LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource) { ...if (hIcon != NULL){ ...{// register a very similar WNDCLASSreturn AfxRegisterWndClass(wndcls.style, wndcls.hCursor, wndcls.hbrBackground, hIcon);}}return NULL; // just use the default }
    在wincore.cpp的CWnd::CreateEx中, 創(chuàng)建窗口
    [cpp] view plaincopy print?
  • BOOL?CWnd::CreateEx(...)??
  • {??
  • ...??
  • ????//?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;??
  • ??
  • ????...??
  • ??
  • ????AfxHookWindowCreate(this);??
  • ????HWND?hWnd?=?::AfxCtxCreateWindowEx(cs.dwExStyle,?cs.lpszClass,??
  • ????????????cs.lpszName,?cs.style,?cs.x,?cs.y,?cs.cx,?cs.cy,??
  • ????????????cs.hwndParent,?cs.hMenu,?cs.hInstance,?cs.lpCreateParams);??
  • ??
  • ????...??
  • }??
  • BOOL CWnd::CreateEx(...) { ...// 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;...AfxHookWindowCreate(this);HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);... }其中,在 AfxHookWindowCreate中 安裝鉤子使所有窗口消息勾到一起處理如下:

    [cpp] view plaincopy print?
  • void?AFXAPI?AfxHookWindowCreate(CWnd*?pWnd)??
  • {??
  • ...??
  • ????????pThreadState->m_hHookOldCbtFilter?=?::SetWindowsHookEx(WH_CBT,?_AfxCbtFilterHook,?NULL,?::GetCurrentThreadId());??
  • ????...??
  • }??
  • void AFXAPI AfxHookWindowCreate(CWnd* pWnd) { ...pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT, _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());... }在_AfxCbtFilterHook中代碼如下:

    [cpp] view plaincopy print?
  • LRESULT?CALLBACK?_AfxCbtFilterHook(int?code,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ...??
  • ????????if?(pWndInit?!=?NULL)??
  • ????????{??
  • ????????????AFX_MANAGE_STATE(pWndInit->m_pModuleState);??
  • ??
  • ????????????//?the?window?should?not?be?in?the?permanent?map?at?this?time??
  • ????????????ASSERT(CWnd::FromHandlePermanent(hWnd)?==?NULL);??
  • ??
  • ????????????//?connect?the?HWND?to?pWndInit...??
  • ????????????pWndInit->Attach(hWnd);??
  • ????????????//?allow?other?subclassing?to?occur?first??
  • ????????????pWndInit->PreSubclassWindow();??
  • ??
  • ????????????WNDPROC?*pOldWndProc?=?pWndInit->GetSuperWndProcAddr();??
  • ????????????ASSERT(pOldWndProc?!=?NULL);??
  • ??
  • ????????????//?subclass?the?window?with?standard?AfxWndProc??
  • ????????????WNDPROC?afxWndProc?=?AfxGetAfxWndProc();??
  • ????????????oldWndProc?=?(WNDPROC)SetWindowLongPtr(hWnd,?GWLP_WNDPROC,??
  • ????????????????(DWORD_PTR)afxWndProc);??
  • ????????????ASSERT(oldWndProc?!=?NULL);??
  • ????????????if?(oldWndProc?!=?afxWndProc)??
  • ????????????????*pOldWndProc?=?oldWndProc;??
  • ??
  • ????????...??
  • }??
  • LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam) { ...if (pWndInit != NULL){AFX_MANAGE_STATE(pWndInit->m_pModuleState);// the window should not be in the permanent map at this timeASSERT(CWnd::FromHandlePermanent(hWnd) == NULL);// connect the HWND to pWndInit...pWndInit->Attach(hWnd);// allow other subclassing to occur firstpWndInit->PreSubclassWindow();WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();ASSERT(pOldWndProc != NULL);// subclass the window with standard AfxWndProcWNDPROC afxWndProc = AfxGetAfxWndProc();oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc);ASSERT(oldWndProc != NULL);if (oldWndProc != afxWndProc)*pOldWndProc = oldWndProc;... }其中pWndInit->Attach完成 句柄hwnd和窗口類CWnd*的綁定,建立一張hash表,對(duì)應(yīng)的HashMap結(jié)構(gòu)可參照CWnd::afxMapHWND對(duì)應(yīng)的winhand_.h中的CHandleMap

    SetWindowLongPtr使所有的窗口響應(yīng)都走AfxWndProc中,在AfxWndProc中完成消息分發(fā)到對(duì)應(yīng)的Cwnd中。

    b).消息的分發(fā)和響應(yīng)

    [cpp] view plaincopy print?
  • LRESULT?CALLBACK?AfxWndProc(HWND?hWnd,?UINT?nMsg,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ...??
  • ????//?all?other?messages?route?through?message?map??
  • ????CWnd*?pWnd?=?CWnd::FromHandlePermanent(hWnd);??
  • ????...??
  • ????return?AfxCallWndProc(pWnd,?hWnd,?nMsg,?wParam,?lParam);??
  • }??
  • LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { ...// all other messages route through message mapCWnd* pWnd = CWnd::FromHandlePermanent(hWnd);...return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); }可以看到, 根據(jù)hwnd取得對(duì)應(yīng)的CWnd*,然后看AfxCallWndProc如下:

    [cpp] view plaincopy print?
  • LRESULT?AFXAPI?AfxCallWndProc(CWnd*?pWnd,?HWND?hWnd,?UINT?nMsg,?WPARAM?wParam?=?0,?LPARAM?lParam?=?0)??
  • {??
  • ...??
  • ??
  • ????????//?delegate?to?object's?WindowProc??
  • ????????lResult?=?pWnd->WindowProc(nMsg,?wParam,?lParam);??
  • ...??
  • }??
  • LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg, WPARAM wParam = 0, LPARAM lParam = 0) { ...// delegate to object's WindowProclResult = pWnd->WindowProc(nMsg, wParam, lParam); ... }
    在這里開始 調(diào)用CWnd成員響應(yīng)函數(shù),終于又回到CWnd中了,接著往下看

    [cpp] view plaincopy print?
  • LRESULT?CWnd::WindowProc(UINT?message,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ...??
  • ????if?(!OnWndMsg(message,?wParam,?lParam,?&lResult))??
  • ????????...??
  • }??
  • LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { ...if (!OnWndMsg(message, wParam, lParam, &lResult))... }
    在OnWndMsg中做了什么呢?看下面代碼

    [cpp] view plaincopy print?
  • BOOL?CWnd::OnWndMsg(UINT?message,?WPARAM?wParam,?LPARAM?lParam,?LRESULT*?pResult)??
  • {??
  • ...??
  • ????//WM_COMMAND特殊處理??
  • ????if?(message?==?WM_COMMAND)??
  • ????{??
  • ????????if?(OnCommand(wParam,?lParam))??
  • ????????{??
  • ????????????lResult?=?1;??
  • ????????????goto?LReturnTrue;??
  • ????????}??
  • ????????return?FALSE;??
  • ????}??
  • ...??
  • ??
  • ????//找到當(dāng)前的CWnd類的MessageMap表,查表得到對(duì)應(yīng)響應(yīng)函數(shù)并處理??
  • ????const?AFX_MSGMAP*?pMessageMap;?pMessageMap?=?GetMessageMap();??
  • ????...??
  • ??
  • ????????for?(/*?pMessageMap?already?init'ed?*/;?pMessageMap->pfnGetBaseMap?!=?NULL;??
  • ????????????pMessageMap?=?(*pMessageMap->pfnGetBaseMap)())??
  • ????????{??
  • ????????????...??
  • ????????}??
  • ??
  • ...??
  • }??
  • BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { ...//WM_COMMAND特殊處理if (message == WM_COMMAND){if (OnCommand(wParam, lParam)){lResult = 1;goto LReturnTrue;}return FALSE;} ...//找到當(dāng)前的CWnd類的MessageMap表,查表得到對(duì)應(yīng)響應(yīng)函數(shù)并處理const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap();...for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;pMessageMap = (*pMessageMap->pfnGetBaseMap)()){...}... }
    可以看到,到此 完成了CWnd中的查表調(diào)用消息對(duì)應(yīng)的處理函數(shù),至于具體的OnCommand消息處理和具體響應(yīng)函數(shù)調(diào)用過程,恕不詳述。

    但是等等,還有一個(gè)問題沒有解決,那就是CWnd中的消息-處理函數(shù)表怎么來的,這就是我們常見的如下結(jié)構(gòu)

    [cpp] view plaincopy print?
  • BEGIN_MESSAGE_MAP(CMainFrame,?...)??
  • ????ON_WM_CREATE()??
  • ????ON_WM_SETFOCUS()??
  • ...??
  • END_MESSAGE_MAP()??
  • BEGIN_MESSAGE_MAP(CMainFrame, ...)ON_WM_CREATE()ON_WM_SETFOCUS() ... END_MESSAGE_MAP()頭文件中的DECLARE_MESSAGE_MAP定義如下,可以看到回調(diào)函數(shù)中 取消息映射表的函數(shù)GetMessageMap在此定義

    [cpp] view plaincopy 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; \

    BEGIN_MESSAGE_MAP結(jié)構(gòu)展開如下

    [cpp] view plaincopy 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 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[] = \{可見 真正的映射表結(jié)構(gòu)_massgeEntries在此定義,ON_WM_CRATE完成實(shí)際的表內(nèi)容填充,例如:
    [cpp] view plaincopy print?
  • #define?ON_WM_CREATE()?\??
  • ????{?WM_CREATE,?0,?0,?0,?AfxSig_is,?\??
  • ????????(AFX_PMSG)?(AFX_PMSGW)?\??
  • ????????(static_cast<?int?(AFX_MSG_CALL?CWnd::*)(LPCREATESTRUCT)?>?(?&ThisClass?::?OnCreate))?},??
  • #define ON_WM_CREATE() \{ WM_CREATE, 0, 0, 0, AfxSig_is, \(AFX_PMSG) (AFX_PMSGW) \(static_cast< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > ( &ThisClass :: OnCreate)) },將 WM_CREATE和OnCreate函數(shù)綁定


    至此,窗口類的封裝過程盡在眼前,可能你覺得過程比較繁瑣,那么我把它概括如下:

    1.Create窗口時(shí)完成兩件事:(1)窗口過程勾到一起處理(2)hwnd和對(duì)應(yīng)的CWnd*綁定

    2.CWnd中利用BEGIN_MESSAGE_MAP結(jié)構(gòu)定義【消息-響應(yīng)函數(shù)】的路由表

    3.響應(yīng)函數(shù)中根據(jù)傳入的hwnd查表得到CWnd*,調(diào)用CWnd->GetMassageMap獲取【消息-響應(yīng)函數(shù)】表,查對(duì)應(yīng)消息的響應(yīng)函數(shù),調(diào)用完成響應(yīng)

    現(xiàn)在再返回去看,是不是清晰明朗了?

    3.ATL大框架

    MFC出現(xiàn)在C++尚未完善時(shí),沒有采用c++的高級(jí)特性,基本上都是繼承和虛函數(shù)、查表,類的層次過多,顯得比較臃腫。相比而言,ATL就好多了,采用模板技術(shù)簡化了設(shè)計(jì),也沒有那么多的層次結(jié)構(gòu),非常輕量,在此基礎(chǔ)上上封裝的WTL界面庫被越來越多的人使用。WTL雖然是在ATL上封裝的,但是窗口的創(chuàng)建和消息分發(fā)原理并沒有變,所以我們?nèi)匀灰訟TL來講解整個(gè)過程。

    ATL的框架基本上是自己搭建起來的,自己編寫_tWinMain函數(shù),期間可借助CMessageLoop完成消息泵的開啟,如下:

    [cpp] view plaincopy print?
  • int?WINAPI?_tWinMain(HINSTANCE?hInstance,?HINSTANCE?/*hPrevInstance*/,?LPTSTR?lpstrCmdLine,?int?nCmdShow)??
  • {??
  • ...??
  • ????int?nRet?=?Run(lpstrCmdLine,?nCmdShow);??
  • ...??
  • }??
  • ??
  • ??
  • int?Run(LPTSTR?/*lpstrCmdLine*/?=?NULL,?int?nCmdShow?=?SW_SHOWDEFAULT)??
  • {??
  • ????CMessageLoop?theLoop;??
  • ????_Module.AddMessageLoop(&theLoop);??
  • ??
  • ????CMainFrame?wndMain;??
  • ??
  • ????if(wndMain.CreateEx()?==?NULL)??
  • ????{??
  • ????????ATLTRACE(_T("Main?window?creation?failed!\n"));??
  • ????????return?0;??
  • ????}??
  • ??
  • ????wndMain.ShowWindow(nCmdShow);??
  • ??
  • ????int?nRet?=?theLoop.Run();??
  • ??
  • ????_Module.RemoveMessageLoop();??
  • ????return?nRet;??
  • }??
  • int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow) { ...int nRet = Run(lpstrCmdLine, nCmdShow); ... }int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT) {CMessageLoop theLoop;_Module.AddMessageLoop(&theLoop);CMainFrame wndMain;if(wndMain.CreateEx() == NULL){ATLTRACE(_T("Main window creation failed!\n"));return 0;}wndMain.ShowWindow(nCmdShow);int nRet = theLoop.Run();_Module.RemoveMessageLoop();return nRet; }
    可知CMainFrame::CreateEx完成窗口創(chuàng)建,atlapp.h中CMessageLoop完成消息泵開啟,代碼如下:

    [cpp] view plaincopy print?
  • //?message?loop??
  • ????int?Run()??
  • ????{??
  • ...??
  • ??
  • ????????for(;;)??
  • ????????{??
  • ...??
  • ??
  • ????????????bRet?=?::GetMessage(&m_msg,?NULL,?0,?0);??
  • ...??
  • ????????????if(!PreTranslateMessage(&m_msg))??
  • ????????????{??
  • ????????????????::TranslateMessage(&m_msg);??
  • ????????????????::DispatchMessage(&m_msg);??
  • ????????????}??
  • ...??
  • ????????}??
  • ????????return?(int)m_msg.wParam;??
  • ????}??
  • // message loopint Run(){ ...for(;;){ ...bRet = ::GetMessage(&m_msg, NULL, 0, 0); ...if(!PreTranslateMessage(&m_msg)){::TranslateMessage(&m_msg);::DispatchMessage(&m_msg);} ...}return (int)m_msg.wParam;}
    整個(gè)大框架和Win32 SDK很像,沒什么封裝,唯一不同的是所有的窗口創(chuàng)建和消息分發(fā)都封裝到窗口類中了,這個(gè)接下來重點(diǎn)說說。

    4.ATL封裝窗口創(chuàng)建和消息分發(fā)

    和MFC封裝窗口類一樣,這里同樣需要考慮之前說的三個(gè)問題,重要的事情說三遍,我就再貼一次之前的話。

    1.怎么將不同的窗口過程勾到一起

    2.同一窗口過程中怎樣將不同的hwnd消息分發(fā)給對(duì)應(yīng)的CWnd類去處理響應(yīng)

    3.最后,如果CWnd拿到了消息,怎樣去簡單有效的去處理和響應(yīng)呢


    這里和MFC一樣,

    1.所有的窗體窗口過程函數(shù)一樣,保證統(tǒng)一處理

    2.hwnd和對(duì)應(yīng)窗口類是通過匯編強(qiáng)制粘連起來的

    3.CWnd拿到消息后類似前面的C語言通過一組宏簡化switch case結(jié)構(gòu)調(diào)用對(duì)應(yīng)的消息響應(yīng)函數(shù)

    同樣我們從源碼開始入手:

    所有的窗體類都繼承于CWndImpl,我們關(guān)注這個(gè)類即可

    a).窗口創(chuàng)建

    atlwin.app中CWindowImpl::Create中如下,取得窗口信息,注冊(cè)窗口類

    [cpp] view plaincopy print?
  • HWND?Create(HWND?hWndParent,?_U_RECT?rect?=?NULL,?LPCTSTR?szWindowName?=?NULL,??
  • ????????????DWORD?dwStyle?=?0,?DWORD?dwExStyle?=?0,??
  • ????????????_U_MENUorID?MenuOrID?=?0U,?LPVOID?lpCreateParam?=?NULL)??
  • {??
  • ????if?(T::GetWndClassInfo().m_lpszOrigName?==?NULL)??
  • ????????T::GetWndClassInfo().m_lpszOrigName?=?GetWndClassName();??
  • ????ATOM?atom?=?T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);??
  • ??
  • ????dwStyle?=?T::GetWndStyle(dwStyle);??
  • ????dwExStyle?=?T::GetWndExStyle(dwExStyle);??
  • ??
  • ????...??
  • ??
  • ????return?CWindowImplBaseT<?TBase,?TWinTraits?>::Create(hWndParent,?rect,?szWindowName,??
  • ?????????????????????????????????????????????????????????dwStyle,?dwExStyle,?MenuOrID,?atom,?lpCreateParam);??
  • }??
  • HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,DWORD dwStyle = 0, DWORD dwExStyle = 0,_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) {if (T::GetWndClassInfo().m_lpszOrigName == NULL)T::GetWndClassInfo().m_lpszOrigName = GetWndClassName();ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);dwStyle = T::GetWndStyle(dwStyle);dwExStyle = T::GetWndExStyle(dwExStyle);...return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rect, szWindowName,dwStyle, dwExStyle, MenuOrID, atom, lpCreateParam); }
    可以看到調(diào)用 GetWndClassInfo.Register注冊(cè)窗口類,每個(gè)類中使用DECLARE_WND_CLASS等宏來填充對(duì)應(yīng)信息。

    DECLARE_WND_CLASS展開如下:

    [cpp] view plaincopy print?
  • #define?DECLARE_WND_CLASS(WndClassName)?\??
  • static?ATL::CWndClassInfo&?GetWndClassInfo()?\??
  • {?\??
  • ????static?ATL::CWndClassInfo?wc?=?\??
  • ????{?\??
  • ????????{?sizeof(WNDCLASSEX),?CS_HREDRAW?|?CS_VREDRAW?|?CS_DBLCLKS,?StartWindowProc,?\??
  • ??????????0,?0,?NULL,?NULL,?NULL,?(HBRUSH)(COLOR_WINDOW?+?1),?NULL,?WndClassName,?NULL?},?\??
  • ????????NULL,?NULL,?IDC_ARROW,?TRUE,?0,?_T("")?\??
  • ????};?\??
  • ????return?wc;?\??
  • }??
  • #define DECLARE_WND_CLASS(WndClassName) \ static ATL::CWndClassInfo& GetWndClassInfo() \ { \static ATL::CWndClassInfo wc = \{ \{ sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, \0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, \NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \}; \return wc; \ }可知默認(rèn)的 所有窗口的窗口過程函數(shù)是StartWindowProc,完成統(tǒng)一控制
    實(shí)際的 窗口創(chuàng)建函數(shù)如下:

    [cpp] view plaincopy print?
  • template?<class?TBase,?class?TWinTraits>??
  • HWND?CWindowImplBaseT<?TBase,?TWinTraits?>::Create(...)??
  • {??
  • ????BOOL?result;??
  • ????ATLASSUME(m_hWnd?==?NULL);??
  • ??
  • ????//?初始化Thunk結(jié)構(gòu)體??
  • ????result?=?m_thunk.Init(NULL,NULL);??
  • ...??
  • ??
  • ????//保存當(dāng)前窗口類指針到全局??
  • ????_AtlWinModule.AddCreateWndData(&m_thunk.cd,?this);??
  • ??
  • ????//創(chuàng)建窗口??
  • ????HWND?hWnd?=?::CreateWindowEx(dwExStyle,?MAKEINTATOM(atom),?szWindowName,??
  • ????????dwStyle,?rect.m_lpRect->left,?rect.m_lpRect->top,?rect.m_lpRect->right?-?rect.m_lpRect->left,??
  • ????????rect.m_lpRect->bottom?-?rect.m_lpRect->top,?hWndParent,?MenuOrID.m_hMenu,??
  • ????????_AtlBaseModule.GetModuleInstance(),?lpCreateParam);??
  • ...??
  • }??
  • template <class TBase, class TWinTraits> HWND CWindowImplBaseT< TBase, TWinTraits >::Create(...) {BOOL result;ATLASSUME(m_hWnd == NULL);// 初始化Thunk結(jié)構(gòu)體result = m_thunk.Init(NULL,NULL); ...//保存當(dāng)前窗口類指針到全局_AtlWinModule.AddCreateWndData(&m_thunk.cd, this);//創(chuàng)建窗口HWND hWnd = ::CreateWindowEx(dwExStyle, MAKEINTATOM(atom), szWindowName,dwStyle, rect.m_lpRect->left, rect.m_lpRect->top, rect.m_lpRect->right - rect.m_lpRect->left,rect.m_lpRect->bottom - rect.m_lpRect->top, hWndParent, MenuOrID.m_hMenu,_AtlBaseModule.GetModuleInstance(), lpCreateParam); ... }
    這里的Thunk和保存指針到全局之后再說。

    至此創(chuàng)建過程完成。

    b).消息分發(fā)和響應(yīng)

    前面說了,所有的窗口類的響應(yīng)函數(shù)都是在StartWndProc中,如下:

    [cpp] view plaincopy print?
  • template?<class?TBase,?class?TWinTraits>??
  • LRESULT?CALLBACK?CWindowImplBaseT<?TBase,?TWinTraits?>::StartWindowProc(HWND?hWnd,?UINT?uMsg,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ????CWindowImplBaseT<?TBase,?TWinTraits?>*?pThis?=?(CWindowImplBaseT<?TBase,?TWinTraits?>*)_AtlWinModule.ExtractCreateWndData();??
  • ????...??
  • ????pThis->m_thunk.Init(pThis->GetWindowProc(),?pThis);??
  • ????WNDPROC?pProc?=?pThis->m_thunk.GetWNDPROC();??
  • ????WNDPROC?pOldProc?=?(WNDPROC)::SetWindowLongPtr(hWnd,?GWLP_WNDPROC,?(LONG_PTR)pProc);??
  • ????...??
  • ????return?pProc(hWnd,?uMsg,?wParam,?lParam);??
  • }??
  • template <class TBase, class TWinTraits> LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)_AtlWinModule.ExtractCreateWndData();...pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);WNDPROC pProc = pThis->m_thunk.GetWNDPROC();WNDPROC pOldProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);...return pProc(hWnd, uMsg, wParam, lParam); }可知, 第一次窗口響應(yīng)會(huì)進(jìn)入到此函數(shù),這里的代碼從全局結(jié)構(gòu)中拿到當(dāng)前窗口類的指針,初始化Thunk,設(shè)置Thunk為代理窗口響應(yīng)函數(shù),通過pThis->m_thunk.Init(pThis->GetWindowProc(), pThis); 將窗口的this指針和窗口消息處理函數(shù)WindowProc初始化到thunk靜態(tài)結(jié)構(gòu)里。設(shè)置所有的窗體過程函數(shù)為WindowProc。


    這里用到了Thunk轉(zhuǎn)換技術(shù),所謂Thunk就是轉(zhuǎn)換的意思,這里的基本思想是替換掉傳統(tǒng)的WndProc的第一個(gè)句柄參數(shù)hwnd,讓這里的hwnd實(shí)際上是對(duì)應(yīng)的CWndImpl的指針,這樣完成了hwnd到窗體類的映射。具體的實(shí)現(xiàn)在atlstdthunk.h中,如下:

    [cpp] view plaincopy print?
  • #pragma?pack(push,1)??
  • struct?_stdcallthunk??
  • {??
  • ????DWORD???m_mov;??????????//?替換hwnd參數(shù)為對(duì)應(yīng)CWndImpl指針?mov?dword?ptr?[esp+0x4],?pThis?(esp+0x4?is?hWnd)??
  • ????DWORD???m_this;?????????//??
  • ????BYTE????m_jmp;??????????//?跳轉(zhuǎn)到WndProc??
  • ????DWORD???m_relproc;??????//?relative?jmp??
  • ????BOOL?Init(DWORD_PTR?proc,?void*?pThis)??
  • ????{??
  • ????????m_mov?=?0x042444C7;??//C7?44?24?0C??
  • ????????m_this?=?PtrToUlong(pThis);??
  • ????????m_jmp?=?0xe9;??
  • ????????m_relproc?=?DWORD((INT_PTR)proc?-?((INT_PTR)this+sizeof(_stdcallthunk)));???//?write?block?from?data?cache?and??
  • ????????FlushInstructionCache(GetCurrentProcess(),?this,?sizeof(_stdcallthunk));????//??flush?from?instruction?cache??
  • ????????return?TRUE;??
  • ????}??
  • ...??
  • };??
  • #pragma?pack(pop)??
  • #pragma pack(push,1) struct _stdcallthunk {DWORD m_mov; // 替換hwnd參數(shù)為對(duì)應(yīng)CWndImpl指針 mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)DWORD m_this; //BYTE m_jmp; // 跳轉(zhuǎn)到WndProcDWORD m_relproc; // relative jmpBOOL Init(DWORD_PTR proc, void* pThis){m_mov = 0x042444C7; //C7 44 24 0Cm_this = PtrToUlong(pThis);m_jmp = 0xe9;m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk))); // write block from data cache andFlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallthunk)); // flush from instruction cachereturn TRUE;} ... }; #pragma pack(pop)

    WindowProc處理如下:

    [cpp] view plaincopy print?
  • template?<class?TBase,?class?TWinTraits>??
  • LRESULT?CALLBACK?CWindowImplBaseT<?TBase,?TWinTraits?>::WindowProc(HWND?hWnd,?UINT?uMsg,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ????CWindowImplBaseT<?TBase,?TWinTraits?>*?pThis?=?(CWindowImplBaseT<?TBase,?TWinTraits?>*)hWnd;//hwnd轉(zhuǎn)換成CWindowImplBaseT指針??
  • ????...??
  • ????BOOL?bRet?=?pThis->ProcessWindowMessage(pThis->m_hWnd,?uMsg,?wParam,?lParam,?lRes,?0);//調(diào)用對(duì)應(yīng)的窗體類的ProcessWindowMessage處理函數(shù)??
  • ????...??
  • }??
  • template <class TBase, class TWinTraits> LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)hWnd;//hwnd轉(zhuǎn)換成CWindowImplBaseT指針...BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);//調(diào)用對(duì)應(yīng)的窗體類的ProcessWindowMessage處理函數(shù)... }可知在具體的窗口過程函數(shù)中, 將hWnd轉(zhuǎn)換成對(duì)應(yīng)的窗口類,接著調(diào)用窗口類的ProcessWindowMessage調(diào)用對(duì)應(yīng)的窗體類處理函數(shù)。
    每個(gè)窗體類都有ProcessWindowMessage函數(shù),它使用一組宏定義如下:

    [cpp] view plaincopy print?
  • BEGIN_MSG_MAP(CMainFrame)??
  • ????MESSAGE_HANDLER(WM_CREATE,?OnCreate)??
  • ????...??
  • END_MSG_MAP()??
  • BEGIN_MSG_MAP(CMainFrame)MESSAGE_HANDLER(WM_CREATE, OnCreate)... END_MSG_MAP()
    展開顯示如下:

    [cpp] view plaincopy print?
  • #define?BEGIN_MSG_MAP(theClass)?\??
  • public:?\??
  • ????BOOL?ProcessWindowMessage(HWND?hWnd,?UINT?uMsg,?WPARAM?wParam,?LPARAM?lParam,?LRESULT&?lResult,?DWORD?dwMsgMapID?=?0)?\??
  • ????{?\??
  • ????????BOOL?bHandled?=?TRUE;?\??
  • ????????(hWnd);?\??
  • ????????(uMsg);?\??
  • ????????(wParam);?\??
  • ????????(lParam);?\??
  • ????????(lResult);?\??
  • ????????(bHandled);?\??
  • ????????switch(dwMsgMapID)?\??
  • ????????{?\??
  • ????????case?0:??
  • ??????????
  • #define?MESSAGE_HANDLER(msg,?func)?\??
  • ????if(uMsg?==?msg)?\??
  • ????{?\??
  • ????????bHandled?=?TRUE;?\??
  • ????????lResult?=?func(uMsg,?wParam,?lParam,?bHandled);?\??
  • ????????if(bHandled)?\??
  • ????????????return?TRUE;?\??
  • ????}??
  • #define BEGIN_MSG_MAP(theClass) \ public: \BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \{ \BOOL bHandled = TRUE; \(hWnd); \(uMsg); \(wParam); \(lParam); \(lResult); \(bHandled); \switch(dwMsgMapID) \{ \case 0:#define MESSAGE_HANDLER(msg, func) \if(uMsg == msg) \{ \bHandled = TRUE; \lResult = func(uMsg, wParam, lParam, bHandled); \if(bHandled) \return TRUE; \}其實(shí)就是 宏定義的switch case結(jié)構(gòu)

    至此整個(gè)過程如下:

    1.Create中指定統(tǒng)一的窗口過程StartWindowProc

    2.StartWindowProc第一次響應(yīng)時(shí)完成hwnd和CWndImpl的映射綁定,設(shè)置響應(yīng)函數(shù)為WindowProc

    3.WindowProc中轉(zhuǎn)hwnd為CWndImpl*,調(diào)用對(duì)應(yīng)類的ProcessWindowMessage分發(fā)處理消息

    4.BEGIN_MSG_MAP簡化switch case結(jié)構(gòu),在每個(gè)窗口類中分發(fā)處理


    總之封裝窗口類需要考慮之前說的三點(diǎn),搞懂了這三點(diǎn)其他的問題也就迎刃而解了。最后不要嫌我煩,再貼一遍我一直強(qiáng)調(diào)的重點(diǎn),牢記這三點(diǎn),看相應(yīng)的框架封裝過程大同小異:

    1.怎么將不同的窗口過程勾到一起

    2.同一窗口過程中怎樣將不同的hwnd消息分發(fā)給對(duì)應(yīng)的CWnd類去處理響應(yīng)

    3.最后,如果CWnd拿到了消息,怎樣去簡單有效的去處理和響應(yīng)呢


    原創(chuàng),轉(zhuǎn)載請(qǐng)注明來自http://blog.csdn.net/wenzhou1219

    總結(jié)

    以上是生活随笔為你收集整理的深入解析Windows窗口创建和消息分发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。