看完《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 || /
(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 /
中的一個,那麼傳回值就是我們寫的函數的傳回值,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獲得此值並做妥善處理。
繼續努力!