天天看點

chHANDLE_DLGMSG(windows核心程式設計)講解

看完《Windows程式設計》後開始看《windows核心程式設計》,

結果看第一個案例的時候就很驚人的發現,Jeffery大牛的代碼很深奧。乍一看好像沒有包含《windows.h》。

看看包含的頭檔案發現,CmnHdr.h中已經包含了《windows.h》。而CmnHdr.h中的代碼更吓人,如果沒有講解,不知道怎麼看才好。後來才知道原來書的最後有專門的搭建環境的介紹,基本上全面的講解了CmnHdr.h的東西。

CmnHdr.h中包含了大牛的很多自己的東西。在看到chHANDLE_DLGMSG這個宏的時候,才知道有消息分流器這麼個東西。後來到處查找資訊,才發現很多以前不知道的東西。具體的消息分流器我不介紹,隻是想屢一下大牛寫chHANDLE_DLGMSG的動機。書中介紹很簡短,隻是說HANDLE_MSG對處理消息對話框過程不能傳回TRUE或FALSE來告訴對話框過程我們到底有沒有處理消息。為什麼HANDLE_MSG不能正确傳回呢?

原因在于HANDLE_MSG隻是針對HANDLE_##message的簡單封轉。##message是指将HANDLE與具體的消息連接配接起來,而且是消息的宏定義方式(如WM_INITDIALOG而不是0x0110)。例如:

HANDLE_##WM_INITDIALOG就得到HANDLE_WM_INITDIALOG,但你可不能把WM_INITDIALOG的具體的數字寫進去。

HANDLE_MSG宏定義如下:

#define HANDLE_MSG(hwnd, message, fn)    /

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

可以看到隻是加上case和return語句。是以HANDLE_MSG隻是考慮到了形式上正确,卻無法應對所有的消息處理的傳回值。HANDLE_MSG的消息傳回完全靠HANDLE_##message的宏處理。

假如:HANDLE_##message的message是WM_INITDIALOG,那麼HANDLE_##message就是HANDLE_WM_INITIDALOG。

HANDLE_WM_INITDIALOG宏定義如下:

#define HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, fn) /

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

可以看到如果在消息對話框過程中使用HANDLE_MSG(hwnd,WM_INITDIALOG,Dlg_InitDialog);是沒有問題的。是啊,我隻說了對WM_INITDIALOG沒有問題,windows裡面那麼多消息,其他消息就很有問題。看WM_COMMAND消息吧!

HANDLE_WM_COMMAND宏定義如下:

#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) /

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

可以看出HANDLE_WM_COMMAND根本沒有傳回值,因為fn的形式根本有沒指明傳回值。沒有傳回值那為什麼HANDLE_MSG要傳回呢?

這就是HANDLE_MSG的問題了,設計HANDLE_WM_COMMAND的隻是想做case WM_COMMAND和return TRUE之間的過程。而HANDLE_MSG卻隻是做的了簡單形式的封裝,是以這就是HANDLE_MSG的問題。

對于對話框消息處理過程我們應該使用SetDlgMsgResult宏,來正确傳回。這就是大牛chHANDLE_DLGMSG用到的宏,chHANDLE_DLGMSG中封裝了SetDlgMsgResult。

這是大牛的chHANDLE_DLGMSG的宏定義:

#define chHANDLE_DLGMSG(hWnd, message, fn)                 /

   case (message): return (SetDlgMsgResult(hWnd, uMsg,     /

      HANDLE_##message((hWnd), (wParam), (lParam), (fn))))

這是SetDlgMsgResult宏定義:

#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))

chHANDLE_DLGMSG中,return傳回的值即使SetDlgMsgResult傳回的值。那麼SetDlgMsgResult傳回什麼值呢?

讓我們看看,如果消息是

(msg) == WM_CTLCOLORMSGBOX      || /

中的一個,那麼傳回值就是我們寫的函數的傳回值,result在chHANDLE_DLGMSG中被替換成對應的消息處理函數fn。如果消息不是其中的,那麼那麼我們先執行SetWindowLongPtr(稍後講),最後傳回TRUE(根據逗号操作符特性)。注意執行SetWindowLongPtr的時候調用的消息處理函數result(其實就是fn)。

但為什麼要這麼做呢?這些WM_CTLCOLORMSGBOX     WM_CTLCOLOREDIT 。。。有什麼特殊的嗎? 這些消息的傳回值都是有已知的且有自己含義的,對于這些消息隻需要傳回它們函數處理的傳回值就讓程式正常運作。但對于消息WM_COMMAND就很難說了,很有可能是某個控件的通知消息,需要一個值,這個值可能是零也可能不是零,是以不能像WM_CTLCOLORMSGBOX那樣處理了,因為對話框過程中TRUE大部分時候代表使用者處理此消息,FALSE或0代表使用者沒處理此消息,那麼對話框的父消息處理過程會進行預設處理。這樣的話,如果一個WM_COMMAND的處理消息需要傳回0,怎麼辦?這樣辦,先傳回TRUE,然後用SetWindowLongPtr設定需要傳回的傳回值,傳回TRUE是向父視窗表明你已經處理了,但傳回值最後被代替成了SetWindowlongPtr的值,這樣才能正常工作。就這樣了!

其實這麼繞人的根本原因是因為:

windows内的對話框消息過程是這樣處理的.

LRESULT CALLBACK DefDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)  

{  

  DLGPROC dp = (DLGPROC)GetWindowLongPtr(hdlg, DWLP_DLGPROC);  

  SetWindowLongPtr(hdlg, DWLP_MSGRESULT, 0);  

  INT_PTR fResult = dp(hdlg, uMsg, wParam, lParam);  

 if (fResult) return GetWindowLongPtr(hdlg, DWLP_MSGRESULT);  

  else ...做預設的事...  

上述代碼轉載了以為csdn朋友的内容,我稍加解釋:

就是因為對話框真正的消息處理過程是這樣的。通過對話框過程(對話框視窗過程和對話框過程要厘清)傳回值表明對話框過程是否處理此消息,通過SetWindowlongPtr(...,DLG_MSGRESULT)設定對話框的傳回值,因為對話框視窗過程用GetWindowLongPtr獲得此值并做妥善處理。

繼續努力!

繼續閱讀