原文:http://blog.csdn.net/xujiezhige/article/details/6329545
看完《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获得此值并做妥善处理。