• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Chip Studio

            常用鏈接

            統(tǒng)計

            最新評論

            WinAPI編程基礎(chǔ) 之 消息分流器

            WinAPI編程基礎(chǔ) 之 消息分流器[z]


            文章作者:Bideyore[E.S.T]
            信息來源:邪惡八進制 中國(www.EvilOctal.com

            對于熟悉Win API編程的同志們來說,windowsx.h這個頭文件應(yīng)該不會太陌生吧,這次要講的內(nèi)容就來自這個windowsx.h頭文件。

            經(jīng)常能在msdn上查到這樣一些函數(shù),明明是個函數(shù),而且模樣長得和一般的api函數(shù)也一樣一樣的,可卻叫做macro,為什么呢?留意一下函數(shù)使用的requirement,你會發(fā)現(xiàn),它的聲明正是在windowsx.h這個頭文件里。

            Windowsx.h包含了這樣一些內(nèi)容:
            宏API,窗口消息分流器,控件API;

            所 有的這些宏定義,可以使你的程序更加安全,簡潔,結(jié)構(gòu)更清晰,大大提高程序的可讀性;其中窗口消息分流器(message cracker)是我們今天要討論的話題,它可以使我們的API程序變得更簡潔。下面就進入我們的主題:(有關(guān)windowsx.h的更多內(nèi)容,可以參考 MS Knowledge Base Article #83456.)

            消息分流器是Windows提供的一組宏定義,它的兩個最大的作用,用MS的話來說,就是:

            ● 安全的數(shù)據(jù)類型,因為消息分流器完成了大量的類型轉(zhuǎn)換的工作;
            ● 使程序向32位windows的轉(zhuǎn)化更簡單;

            當(dāng)然,使用消息分流器會大大改變程序的面貌,你也可以選擇不使用它。

            下面我們就以一個對話框窗口的消息處理過程為例,看看消息分流器到底是怎么運作的。


            1.消息分流器的基本使用
            先看一個普通的窗口消息處理函數(shù),它可能需要處理一些窗口的初始化,無效客戶區(qū)重繪等消息:

            LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,
            WPARAM wParam, LPARAM lParam)
            {
            switch(msg)
            {
            case WM_CREATE:
            // ...
            return 0;

            case WM_PAINT:
            // ...
            return 0;

            case WM_DESTROY:
            //...
            return 0;
            }
            return DefWindowProc(hwnd, msg, wParam, lParam);
            }

            而通過使用消息分流器,我們可以把每個case都寫到相應(yīng)的消息處理函數(shù)中,就像下面這樣:

            LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,
            WPARAM wParam, LPARAM lParam)
            {
            switch(msg)
            {
            case WM_CREATE:
            return HANDLE_WM_CREATE(hwnd, wParam, lParam, Cls_OnCreate);

            case WM_PAINT:
            return HANDLE_WM_PAINT(hwnd, wParam, lParam, Cls_OnPaint);

            case WM_DESTROY:
            return HANDLE_WM_DESTROY(hwnd, wParam, lParam, Cls_OnDestroy);
            }
            return DefWindowProc(hwnd, msg, wParam, lParam);
            }

            這里用到了三個宏定義:HANDLE_WM_CREATE, HANDLE_WM_PAINT, HANDLE_WM_DESTROY;這三個宏定義就是我們的三個消息分流器(別看叫什么分流器,說穿了也不值幾個錢,呵呵),它們在windowsx.h中的定義如下:

            #define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn)
            ((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L)
            #define HANDLE_WM_PAINT(hwnd, wParam, lParam, fn)
            ((fn)(hwnd), 0L)
            #define HANDLE_WM_DESTROYCLIPBOARD(hwnd, wParam, lParam, fn)
            ((fn)(hwnd), 0L)

            把這三個宏定義替換回去,就變成:

            LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,
            WPARAM wParam, LPARAM lParam)
            {
            switch(msg)
            {
            case WM_CREATE:
            return Cls_OnCreate(hwnd, (LPCREATESTRUCT)(lParam) ? 0L : (LRESULT)-1L;
            // 如果處理了消息,則Cls_OnCreate應(yīng)返回TRUE,導(dǎo)致WndProc返回0,否則Cls_OnCreate返回FALSE,導(dǎo)致WndProc返回-1;
            case WM_PAINT:
            return Cls_OnPaint(hwnd), 0L;
            // 逗號表達式;Cls_OnPaint是void類型,這里返回0;
            case WM_DESTROY:
            return Cls_OnDestroy(hwnd), 0L; // 同Cls_OnPaint
            }
            return DefWindowProc(hwnd, msg, wParam, lParam);
            }

            之后我們就可以按照消息分流器的定義編寫相應(yīng)的消息處理函數(shù)了:

            BOOL Cls_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct){…};
            void Cls_OnPaint(HWND hwnd){…};
            void Cls_OnDestroyClipboard(HWND hwnd){…};

            windowsx.h還提供了一個更加簡化的方法:使用HANDLE_MSG宏,這個宏是這樣定義的:

            #define HANDLE_MSG(hwnd, message, fn)
            case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))

            這個宏要做的就是根據(jù)不同的message(##用來連接前后的字符串),把自己“變成”相應(yīng)的HANDLE_XXXXMESSAGE形式的宏,再通過相應(yīng)的宏來執(zhí)行消息處理代碼;
            比如實際代碼中寫入:

            HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate)

            則經(jīng)過轉(zhuǎn)換就變成:

            case (WM_CREATE): return HANDLE_WM_CREATE((hwnd), (wParam), (lParam), (Cls_OnCreate))

            這樣,我們就可以直接把程序?qū)憺椋?br>LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,
            WPARAM wParam, LPARAM lParam)
            {
            switch(msg)
            {
            HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate);
            HANDLE_MSG(hwnd, WM_PAINT, Cls_OnPaint);
            HANDLE_MSG(hwnd, WM_DESTROY, Cls_OnDestroy);
            }
            return DefWindowProc(hwnd, msg, wParam, lParam);
            }

            之 后直接編寫相應(yīng)的消息處理過程就可以了。是不是簡潔多了?而且把消息處理封裝到函數(shù)里面,就可以使用VS直接跳轉(zhuǎn)到這個函數(shù),再也不用費勁去找那個 case了。要注意的一點是,雖然windowsx.h里包括了所有消息對應(yīng)的分流器,但它們的參數(shù)是宏定義顯式說明的,在編寫消息處理函數(shù)時,必須遵循 宏定義中的參數(shù)類型,否則會導(dǎo)致錯誤;這么多消息分流器,我們每次新寫一個消息處理函數(shù)時就得看看是否把參數(shù)設(shè)置正確了,整個過程繁瑣冗長。好在已經(jīng)有一 個工具叫Message Cracker Wizard,可以幫助我們生成消息分流器和相關(guān)的處理過程,具體見:http://www.codeproject.com/win32/msgcrackwizard.asp


            2.在對話框中使用消息分流器
            在對話框消息處理中,窗口子類化是我們經(jīng)常使用的手段,這也可以通過消息分流器實現(xiàn),但是有點小問題 :>
            下面是一個使用了windowsx.h消息分流器的對話框及其處理過程:
            ……
            int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int)
            {
            DialogBoxParam(
            hinstExe, MAKEINTRESOURCE(IDD_PASSTHRU), NULL, (DLGPROC)Dlg_Proc, 0);

            return(0);
            }
            ……

            LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg,
            WPARAM wParam, LPARAM lParam)
            {
            switch(msg)
            {
            HANDLE_MSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog); // 不能直接使用HANDLE_MSG宏
            HANDLE_MSG(hwnd, WM_COMMAND, Cls_OnCommand); // 不能直接使用HANDLE_MSG宏
            }

            return false;
            }

            以上程序中直接使用HANDLE_MSG可能導(dǎo)致錯誤;為什么呢?問題出在子類化的消息處理過程的返回值上,msdn中對于對話框消息處理過程的返回值有如下說明:

            一般情況下,對話框過程函數(shù)應(yīng)該在處理了消息的情況下返回TRUE,如果沒有處理,則返回FALSE。如果對話框過程返回了FALSE,那么對話框管理器為這條消息準(zhǔn)備默認的對話操作。

            如 果對話框處理了一個需要特定返回值的消息,則對話框的返回值應(yīng)該被設(shè)置為調(diào)用SetWindowLong(The SetWindowLong function changes an attribute of the specified window. The function also sets a 32-bit (long) value at the specified offset into the extra window memory of a window. )后的返回值,并在返回TRUE之前立即返回這個 值。注意你必須立即調(diào)用SetWindowLong(這個函數(shù)用于調(diào)用窗口子類化的過程),這會導(dǎo)致DWL_MSGRESULT值被一個嵌套的對話框消息 改寫。返回值為特定值的消息有:
            • WM_CHARTOITEM
            • WM_COMPAREITEM
            • WM_CTLCOLORBTN
            • WM_CTLCOLORDLG
            • WM_CTLCOLOREDIT
            • WM_CTLCOLORLISTBOX
            • WM_CTLCOLORSCROLLBAR
            • WM_CTLCOLORSTATIC
            • WM_INITDIALOG
            • WM_QUERYDRAGICON
            • WM_VKEYTOITEM
            看到?jīng)]有? 我們的消息WM_INITDIALOG也在其中,對這個消息進行處理的過程不能簡單的返回TRUE表示對消息進行了處理,而是另有其意;它將轉(zhuǎn)化為:

            case (WM_INITDIALOG): return HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, Cls_OnInitDialog);

            宏HANDLE_WM_INITDIALOG定義如下:

            #define HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, fn)
            (LRESULT)(DWORD)(UINT)(BOOL)(fn)((hwnd), (HWND)(wParam), lParam)

            對WM_INITDIALOG的處理,如果返回TRUE,則表示設(shè)置鍵盤焦點到對話框的默認控件,否則返回FALSE;這時好像還看不出什么問題,而對于我們的另外一個消息WM_COMMAND,HANDLE_MSG簡單的把它變成:

            case (WM_COMMAND): return HANDLE_WM_COMMAND(hwnd, wParam, lParam, Cls_OnCommand);

            宏HANDLE_WM_COMMAND定義如下:

            #define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn)
            ((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L)

            問 題出來了,我們的Cls_OnCommand由于是個void型的函數(shù),是沒有返回值的,因此windows默認這種消息處理過程必須返回一個0值,而返 回0值不就表示我們的消息過程不處理這個消息么?這個矛盾是HANDLE_MSG無法解決的。怎么辦才能使消息過程在處理完WM_COMMAND消息之后 正確的返回一個TRUE呢? 答案是使用另一個windowsx.h中的宏:SetDlgMsgResult(hwnd, msg, result)

            這個宏定義如下:

            #define SetDlgMsgResult(hwnd, msg, result) ((
            (msg) == WM_CTLCOLORMSGBOX ||
            (msg) == WM_CTLCOLOREDIT ||
            (msg) == WM_CTLCOLORLISTBOX ||
            (msg) == WM_CTLCOLORBTN ||
            (msg) == WM_CTLCOLORDLG ||
            (msg) == WM_CTLCOLORSCROLLBAR ||
            (msg) == WM_CTLCOLORSTATIC ||
            (msg) == WM_COMPAREITEM ||
            (msg) == WM_VKEYTOITEM ||
            (msg) == WM_CHARTOITEM ||
            (msg) == WM_QUERYDRAGICON ||
            (msg) == WM_INITDIALOG
            ) ? (BOOL)(result) : (SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE))

            (有沒有注意到,里面多了一個WM_CTLCOLORMSGBOX ? 這個消息是16位WinAPI中的消息,一度被轉(zhuǎn)換為Win32 API的一個消息;現(xiàn)在在最新的32位API中已經(jīng)被刪除了;保留它可能考慮到兼容性的問題,這里不做進一步討論)
            現(xiàn) 在看到了,如果對話框過程處理的消息恰巧為返回特定值中的一個,則如實返回result;不要被前面的BOOL蒙蔽,BOOL在頭文件中的定義實際上是一 個int型,一旦需要返回非TRUE或FALSE的其他值,照樣可以;這樣,我們的Cls_OnInitDialog就能夠正確的返回它的BOOL值了, 而Cls_OnCommand在處理之后,也可以由后面的逗號表達式正確的返回一個TRUE表示消息已處理。

            在《Windows核心編程》一書中,大牛Jeffrey自己定義了一個宏,使SetDlgMsgResult宏的使用更加方便:

            #define chHANDLE_DLGMSG(hwnd, message, fn)
            case (message): return (SetDlgMsgResult(hwnd, uMsg,
            HANDLE_##message((hwnd), (wParam), (lParam), (fn))))

            可見這個宏只是簡單的對SetDlgMsgRseult宏進行了封裝。

            這樣,我們最終的代碼可以寫成:

            LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg,
            WPARAM wParam, LPARAM lParam)
            {
            switch(msg)
            {
            chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog); // 使用大牛的chHANDLE_DLGMSG宏
            chHANDLE_DLGMSG(hwnd, WM_COMMAND, Cls_OnCommand);
            }

            return false;
            }



            下面把原來程序整個框架列出來:

            LRESULT CALLBACK Dlg_Proc(HWND hwnd, UNIT umsg, WPARAM wparam, LPARAM lparam)
            {
            switch(msg)
            {
            case WM_COMMAND: // 每個case都被一個message cracker代替,這里使用大牛同志的
            // do something; // chHANDLE_DLGMSG宏;這個宏負責(zé)對消息篩選,處理并返回相應(yīng)的值
            return true;

            case WM_INITDIALOG:
            // do something;
            return xxxx;
            }

            return false; // 如果消息不在我們的DlgProc過程中被處理,則告訴調(diào)用這個DlgProc的消息,
            } //告訴系統(tǒng)的對話框管理器,這個消息我們不處理,交給你了

            對比一下,消息分流器的作用不言自明。

            以上只是介紹了消息分流器的部分應(yīng)用,更多創(chuàng)造性的用法還等你自己在實踐中發(fā)掘。

            下面列出一些有用的參考資料:

            http://support.microsoft.com/default.aspx?scid=kb;en-us;83456 介紹了STRICT宏定義以及windowsx.h
            http://www.codeproject.com/win32/msgcrackwizard.asp 提供message cracker wizard的下載,而且附有源代碼
            《windows核心編程》windows系統(tǒng)編程,就跟定大牛了 :> 他在自己的sample中大量使用了message cracker


            posted on 2008-02-04 02:53 MyChip 閱讀(181) 評論(0)  編輯 收藏 引用 所屬分類: VC

            四虎亚洲国产成人久久精品| 精品无码久久久久久国产| 伊人久久大香线蕉影院95| 色噜噜狠狠先锋影音久久| 久久成人小视频| 国产综合久久久久久鬼色| 欧美日韩中文字幕久久久不卡| 久久久久久久久66精品片| 国产精品综合久久第一页| 久久精品国产秦先生| 国产成人精品免费久久久久| 久久久久国产一区二区三区| 久久久久亚洲AV综合波多野结衣 | 狠狠人妻久久久久久综合蜜桃| 色偷偷88欧美精品久久久| 99热都是精品久久久久久| 亚洲午夜久久久久久久久久| 亚洲国产精品综合久久网络| 99久久精品免费国产大片| 波多野结衣AV无码久久一区| 四虎国产精品成人免费久久| 国产精品无码久久久久| 久久91精品国产91久久麻豆| 人妻精品久久久久中文字幕69| 7777久久久国产精品消防器材| 久久久久九国产精品| 精品国产91久久久久久久a| 99久久国产亚洲高清观看2024| 国产精品美女久久久| 久久精品国产国产精品四凭 | 亚洲国产成人久久综合碰| 久久93精品国产91久久综合| 99久久国产主播综合精品| 66精品综合久久久久久久| 99久久99久久精品国产片| 国产99久久久久久免费看| 久久精品无码专区免费| 亚洲国产精品成人久久蜜臀| 久久久久久久免费视频| 香蕉久久av一区二区三区| 国产午夜精品久久久久九九|