首页 > 代码库 > [转载]关于chHANDLE_DLGMSG

[转载]关于chHANDLE_DLGMSG

===========================================

最近读到windows核心编程 作者制作的chHANDLE_DLGMSG 对话框消息处理宏

  

 
  1. #define chHANDLE_DLGMSG(hWnd, message, fn)                 /  
  2.    case (message): return (SetDlgMsgResult(hWnd, uMsg,     /  
  3.       HANDLE_##message((hWnd), (wParam), (lParam), (fn))))  

 

这个宏直接调用的windowsx.h里的另一个宏:

 
  1. #define     SetDlgMsgResult(hwnd, msg, result) (( /  
  2.         (msg) == WM_CTLCOLORMSGBOX      || /  
  3.         (msg) == WM_CTLCOLOREDIT        || /  
  4.         (msg) == WM_CTLCOLORLISTBOX     || /  
  5.         (msg) == WM_CTLCOLORBTN         || /  
  6.         (msg) == WM_CTLCOLORDLG         || /  
  7.         (msg) == WM_CTLCOLORSCROLLBAR   || /  
  8.         (msg) == WM_CTLCOLORSTATIC      || /  
  9.         (msg) == WM_COMPAREITEM         || /  
  10.         (msg) == WM_VKEYTOITEM          || /  
  11.         (msg) == WM_CHARTOITEM          || /  
  12.         (msg) == WM_QUERYDRAGICON       || /  
  13.         (msg) == WM_INITDIALOG             /  
  14.     ) ? (BOOL)(result) : (SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE))  

 

可以看到,如果消息是以上列出的消息之一,这些是对话框需要特定返回值的消息,则直接返回"result" 的结果,

 

如果是其他消息,则返回TRUE表示已经处理过此消息,返回FALSE表示没有处理,没有处理的消息将交由系统的"对话框管理器处理"

 

从第二个宏可以看到,如果是其他消息,则必然返回TRUE

 

既然返回了TRUE,那为什么还要在返回之前调用SetWindowLongPtr函数呢.

 

原来是这样:

 

对话框回调函数的返回值跟消息的返回值是两码事,

 

对话框消息函数的返回值 是BOOL值 表示 是否处理了这条消息. TRUE表示已经处理

 

而消息的返回值是指处理的结果,一般用0表示处理成功

 

比如在窗口处理WM_CREATE消息的时候,申请内存失败,就有可能返回-1,windows就不会继续窗口的创建过程.

 

由第一个宏所知 result 其实就是 HANDLE_WM_**** 一系列宏,这些宏都是返回0.

 

因此,这里调用SetWindowLongPtr的目的是表示该消息处理成功.

=========================================================



看完《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获得此值并做妥善处理。

 

继续努力!

[转载]关于chHANDLE_DLGMSG