当前位置:文档之家› WinAPI编程基础之消息分流器

WinAPI编程基础之消息分流器

[原创]WinAPI编程基础之消息分流器
文章标题:[原创]WinAPI编程基础之消息分流器顶部 bideyore 发布于:2004-12-1512:40 [楼主][原创]WinAPI编程基础之消息分流器
文章作者:Bideyore[E.S.T]
信息来源:邪恶八进制PRC(https://www.doczj.com/doc/fc12084594.html,)

对于熟悉WinAPI编程的同志们来说,windowsx.h这个头文件应该不会太陌生吧,这次要讲的内容就来自这个windowsx.h头文件。

经常能在msdn上查到这样一些函数,明明是个函数,而且模样长得和一般的api函数也一样一样的,可却叫做macro,为什么呢?留意一下函数使用的requirement,你会发现,它的声明正是在windowsx.h这个头文件里。

Windowsx.h包含了这样一些内容:
宏API,窗口消息分流器,控件API;

所有的这些宏定义,可以使你的程序更加安全,简洁,结构更清晰,大大提高程序的可读性;其中窗口消息分流器(messagecracker)是我们今天要讨论的话题,它可以使我们的API程序变得更简洁。下面就进入我们的主题:(有关windowsx.h的更多内容,可以参考MSKnowledgeBaseArticle#83456.)

消息分流器是Windows提供的一组宏定义,它的两个最大的作用,用MS的话来说,就是:

●安全的数据类型,因为消息分流器完成了大量的类型转换的工作;
●使程序向32位windows的转化更简单;

当然,使用消息分流器会大大改变程序的面貌,你也可以选择不使用它。

下面我们就以一个对话框窗口的消息处理过程为例,看看消息分流器到底是怎么运作的。


1.消息分流器的基本使用
先看一个普通的窗口消息处理函数,它可能需要处理一些窗口的初始化,无效客户区重绘等消息:

LRESULTCALLBACKWndProc(HWNDhwnd,UINTmsg,
WPARAMwParam,LPARAMlParam)
{
switch(msg)
{
caseWM_CREATE:
//...
return0;

caseWM_PAINT:
//...
return0;

caseWM_DESTROY:
//...
return0;
}
returnDefWindowProc(hwnd,msg,wParam,lParam);
}

而通过使用消息分流器,我们可以把每个case都写到相应的消息处理函数中,就像下面这样:

LRESULTCALLBACKWndProc(HWNDhwnd,UINTmsg,
WPARAMwParam,LPARAMlParam)
{
switch(msg)
{
caseWM_CREATE:
returnHANDLE_WM_CREATE(hwnd,wParam,lParam,Cls_OnCreate);

caseWM_PAINT:
returnHANDLE_WM_PAINT(hwnd,wParam,lParam,Cls_OnPaint);

caseWM_DESTROY:
returnHANDLE_WM_DESTROY(hwnd,wParam,lParam,Cls_OnDestroy);
}
returnDefWindowProc(hwnd,msg,wParam,lParam);
}

这里用到了三个宏定义:HANDLE_WM_CREATE,HANDLE_WM_PAINT,HANDLE_WM_DESTROY;这三个宏定义就是我们的三个消息分流器(别看叫什么分流器,说穿了也不值几个钱,呵呵),它们在windowsx.h中的定义如下:

#defineHANDLE_WM_CREATE(hwnd,wParam,lParam,fn)\
((fn)((hwnd),(LPCREATESTRUCT)(lParam))?0L:(LRESULT)-1L)
#defineHANDLE_WM_PAINT(hw

nd,wParam,lParam,fn)\
((fn)(hwnd),0L)
#defineHANDLE_WM_DESTROYCLIPBOARD(hwnd,wParam,lParam,fn)\
((fn)(hwnd),0L)

把这三个宏定义替换回去,就变成:

LRESULTCALLBACKWndProc(HWNDhwnd,UINTmsg,
WPARAMwParam,LPARAMlParam)
{
switch(msg)
{
caseWM_CREATE:
returnCls_OnCreate(hwnd,(LPCREATESTRUCT)(lParam)?0L:(LRESULT)-1L;
//如果处理了消息,则Cls_OnCreate应返回TRUE,导致WndProc返回0,否则Cls_OnCreate返回FALSE,导致WndProc返回-1;
caseWM_PAINT:
returnCls_OnPaint(hwnd),0L;
//逗号表达式;Cls_OnPaint是void类型,这里返回0;
caseWM_DESTROY:
returnCls_OnDestroy(hwnd),0L; //同Cls_OnPaint
}
returnDefWindowProc(hwnd,msg,wParam,lParam);
}

之后我们就可以按照消息分流器的定义编写相应的消息处理函数了:

BOOLCls_OnCreate(HWNDhwnd,LPCREATESTRUCTlpCreateStruct){…};
voidCls_OnPaint(HWNDhwnd){…};
voidCls_OnDestroyClipboard(HWNDhwnd){…};

windowsx.h还提供了一个更加简化的方法:使用HANDLE_MSG宏,这个宏是这样定义的:

#defineHANDLE_MSG(hwnd,message,fn) \
case(message):returnHANDLE_##message((hwnd),(wParam),(lParam),(fn))

这个宏要做的就是根据不同的message(##用来连接前后的字符串),把自己“变成”相应的HANDLE_XXXXMESSAGE形式的宏,再通过相应的宏来执行消息处理代码;
比如实际代码中写入:

HANDLE_MSG(hwnd,WM_CREATE,Cls_OnCreate)

则经过转换就变成:

case(WM_CREATE):returnHANDLE_WM_CREATE((hwnd),(wParam),(lParam),(Cls_OnCreate))

这样,我们就可以直接把程序写为:
LRESULTCALLBACKWndProc(HWNDhwnd,UINTmsg,
WPARAMwParam,LPARAMlParam)
{
switch(msg)
{
HANDLE_MSG(hwnd,WM_CREATE,Cls_OnCreate);
HANDLE_MSG(hwnd,WM_PAINT,Cls_OnPaint);
HANDLE_MSG(hwnd,WM_DESTROY,Cls_OnDestroy);
}
returnDefWindowProc(hwnd,msg,wParam,lParam);
}

之后直接编写相应的消息处理过程就可以了。是不是简洁多了?而且把消息处理封装到函数里面,就可以使用VS直接跳转到这个函数,再也不用费劲去找那个case了。要注意的一点是,虽然windowsx.h里包括了所有消息对应的分流器,但它们的参数是宏定义显式说明的,在编写消息处理函数时,必须遵循宏定义中的参数类型,否则会导致错误;这么多消息分流器,我们每次新写一个消息处理函数时就得看看是否把参数设置正确了,整个过程繁琐冗长。好在已经有一个工具叫MessageCrackerWizard,可以帮助我们生成消息分流器和相关的处理过程,具体见:https://www.doczj.com/doc/fc12084594.html,/win32/msgcrackwizard.asp。


2.在对话框中使用消息分流器
在对话框消息处理中,窗口子类化是我们经常使用的手段,这也可以通过消息分流器实现,但是有点小问题:>
下面是一个使用了windowsx.h消息分流器的对话框及其处理过程:
……
intWINAPI_tWinMain(HINSTANCEhinstExe,HINS

TANCE,PTSTRpszCmdLine,int)
{
DialogBoxParam(
hinstExe,MAKEINTRESOURCE(IDD_PASSTHRU),NULL,(DLGPROC)Dlg_Proc,0);

return(0);
}
……

LRESULTCALLBACKDlg_Proc(HWNDhwnd,UINTmsg,
WPARAMwParam,LPARAMlParam)
{
switch(msg)
{
HANDLE_MSG(hwnd,WM_INITDIALOG,Cls_OnInitDialog);//不能直接使用HANDLE_MSG宏
HANDLE_MSG(hwnd,WM_COMMAND,Cls_OnCommand); //不能直接使用HANDLE_MSG宏
}

returnfalse;
}

以上程序中直接使用HANDLE_MSG可能导致错误;为什么呢?问题出在子类化的消息处理过程的返回值上,msdn中对于对话框消息处理过程的返回值有如下说明:

一般情况下,对话框过程函数应该在处理了消息的情况下返回TRUE,如果没有处理,则返回FALSE。如果对话框过程返回了FALSE,那么对话框管理器为这条消息准备默认的对话操作。

如果对话框处理了一个需要特定返回值的消息,则对话框的返回值应该被设置为调用SetWindowLong后的返回值,并在返回TRUE之前立即返回这个值。注意你必须立即调用SetWindowLong(这个函数用于调用窗口子类化的过程),这会导致DWL_MSGRESULT值被一个嵌套的对话框消息改写。返回值为特定值的消息有:
• WM_CHARTOITEM
• WM_COMPAREITEM
• WM_CTLCOLORBTN
• WM_CTLCOLORDLG
• WM_CTLCOLOREDIT
• WM_CTLCOLORLISTBOX
• WM_CTLCOLORSCROLLBAR
• WM_CTLCOLORSTATIC
• WM_INITDIALOG
• WM_QUERYDRAGICON
• WM_VKEYTOITEM
看到没有?我们的消息WM_INITDIALOG也在其中,对这个消息进行处理的过程不能简单的返回TRUE表示对消息进行了处理,而是另有其意;它将转化为:

case(WM_INITDIALOG):returnHANDLE_WM_INITDIALOG(hwnd,wParam,lParam,Cls_OnInitDialog);

宏HANDLE_WM_INITDIALOG定义如下:

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

对WM_INITDIALOG的处理,如果返回TRUE,则表示设置键盘焦点到对话框的默认控件,否则返回FALSE;这时好像还看不出什么问题,而对于我们的另外一个消息WM_COMMAND,HANDLE_MSG简单的把它变成:

case(WM_COMMAND):returnHANDLE_WM_COMMAND(hwnd,wParam,lParam,Cls_OnCommand);

宏HANDLE_WM_COMMAND定义如下:

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

问题出来了,我们的Cls_OnCommand由于是个void型的函数,是没有返回值的,因此windows默认这种消息处理过程必须返回一个0值,而返回0值不就表示我们的消息过程不处理这个消息么?这个矛盾是HANDLE_MSG无法解决的。怎么办才能使消息过程在处理完WM_COMMAND消息之后正确的返回一个TRUE呢?答案是使用另一个windowsx.h中的宏:SetDlgMsgResult(hwnd,msg,result)

这个宏定义如下:

#define SetDlgMsgResult(hw

nd,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中的消息,一度被转换为Win32API的一个消息;现在在最新的32位API中已经被删除了;保留它可能考虑到兼容性的问题,这里不做进一步讨论)
现在看到了,如果对话框过程处理的消息恰巧为返回特定值中的一个,则如实返回result;不要被前面的BOOL蒙蔽,BOOL在头文件中的定义实际上是一个int型,一旦需要返回非TRUE或FALSE的其他值,照样可以;这样,我们的Cls_OnInitDialog就能够正确的返回它的BOOL值了,而Cls_OnCommand在处理之后,也可以由后面的逗号表达式正确的返回一个TRUE表示消息已处理。

在《Windows核心编程》一书中,大牛Jeffrey自己定义了一个宏,使SetDlgMsgResult宏的使用更加方便:

#definechHANDLE_DLGMSG(hwnd,message,fn) \
case(message):return(SetDlgMsgResult(hwnd,uMsg, \
HANDLE_##message((hwnd),(wParam),(lParam),(fn))))

可见这个宏只是简单的对SetDlgMsgRseult宏进行了封装。

这样,我们最终的代码可以写成:

LRESULTCALLBACKDlg_Proc(HWNDhwnd,UINTmsg,
WPARAMwParam,LPARAMlParam)
{
switch(msg)
{
chHANDLE_DLGMSG(hwnd,WM_INITDIALOG,Cls_OnInitDialog);//使用大牛的chHANDLE_DLGMSG宏
chHANDLE_DLGMSG(hwnd,WM_COMMAND,Cls_OnCommand);
}

returnfalse;
}



下面把原来程序整个框架列出来:

LRESULTCALLBACKDlg_Proc(HWNDhwnd,UNITumsg,WPARAMwparam,LPARAMlparam)
{
switch(msg)
{
caseWM_COMMAND: //每个case都被一个messagecracker代替,这里使用大牛同志的
//dosomething; //chHANDLE_DLGMSG宏;这个宏负责对消息筛选,处理并返回相应的值
returntrue;

caseWM_INITDIALOG:
//dosomething;
returnxxxx;
}

returnfalse; //如果消息不在我们的DlgProc过程中被处理,则告诉调用这个DlgProc的消息,
} //告诉系统的对话框管理器,这个消息我们不处理,交给你了

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

以上只是介绍了消息分流器的部分应用,更多创造性的用法还等你自己在实践中发掘。

下面列出一些有用的参考资料:

https://www.doczj.com/doc/fc12084594.html,/default.aspx?scid=kb;en-us;83456介绍了STRICT宏定义以及windowsx.h
https://www.doczj.com/doc/fc12084594.html,/win32/msgcrackwizard.asp 提供messagecrackerwizard的下载,而且附有源代码
《windows核心编程》w

indows系统编程,就跟定大牛了:>他在自己的sample中大量使用了messagecracker


[此贴被EvilOctal在2004-12-1513:44重新编辑](c)Copyleft2003-2007,EvilOctalSecurityTeam.
ThisfileisdecompiledbyanunregisteredversionofChmDecompiler.
Regsiteredversiondoesnotshowthismessage.
YoucandownloadChmDecompilerat:https://www.doczj.com/doc/fc12084594.html,/


相关主题
文本预览
相关文档 最新文档