小山日志
读书,学习与思考.
posts - 9,comments - 14,trackbacks - 0
本文译自codeproject.com,原文链接与工具及其源代码下载点击这里
-----------------------------------
 导引:WINDOWSX.H头文件简化Win32 SDK编程

许多的新手或者调试程序员在编写C/C++的Windows API程序时都要面对像意大利面一样的switch...case代码块。当你需要加入一个消息处理到你的窗口处理过程中时,在些代码块中查找例如:WM_COMMANDWM_CHAR,是相当让人恐惧的事情。

早在Windows 3.1时代的Windows软件开发工具包和C/C++7.0编译器就附带一个包含一千多行Windows处理代码的头文件。这个头文件是<windowsX.h>,它包含
了许多有用的宏。微软的对这个头文件的介绍如下:

  • 对C程序做更严格的类型检查的STRICT宏。
  • 一些简化Windows编程的通用操作的宏。
  • 一些简化与Windows控件交流的控件宏。
  • 消息分流器(一种方便,轻便并且类型安全的处理消息的方法)以及它们在Windows环境下的参数和返回值。

在Message Cracker Wizard被设计出现之前,我就从使用这些的宏中获得了效率。如果你对Windows.h的简单描述感兴趣,你可以参见MS知识库的文章#8356

好了,让我们来介绍消息分流器的便利,以及,为什么这里发布的工具可以你编写代码的效率。

当你在编写Win32 SDK程序时,你处理窗口消息通过一个窗口函数,通常命名为WndProc。在Windows C 程序中常见的是窗口函数通过关键字switch和分支标签case处理所有你需要处理的消息。

可以料想的是我们通常需要在主窗口中处理WM_COMMANDWM_KEYUPWM_CLOSEWM_DESTROY消息。理论上我们会把窗口函数写成这样:
LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg,   WPARAM wParam, LPARAM lParam)
{
  
switch
(msg)
  {
    
case
 WM_COMMAND:
    
// 

    break;    
     
    
case
 WM_KEYUP:
    
// 

    break;

    
case
 WM_CLOSE:
    
// 

    break;       
     
    
case
 WM_DESTROY:
    
//

    break;      
        
    
default
:   
       
return
 DefWindowProc(hwnd, msg, wParam, lParam);
  }
}
这是自Windows 1.0时代以来处理Windows消息使用最多的,确实地,它工作。但是问题是当你开始向你程序中加入更多复杂的特性,例如 MDI,OLE,通用控件,等等,你会获得一个行数以千记的窗口函数。你开始反复使用PageDn和PageUp键跳跃,来寻找你需要修改的消息。

这是使用消息分流器的第一个好处:它们提供了处理函数来简化case标签意大利面,就像MFC做的那样。

第二个好处是正确的参数规范化了你的处理函数的使用。你可以简单地使用switch(id)来替代switch(LOWORD(wparam)),因为你把id作为一个“分流器”的参数传递给消息函数时,等同于LOWORD(wparam)

消息处理宏HANDLE_MSG定义在windows.h中,如下:

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

如你从上面宏定义中可以想到的是,将你的代码转换成“消息分流器”版本,你必须支持分流宏,HANDLE_MSG,并且使用函数来处理消息。现在将HANDLE_MSG宏加入到窗口函数中来。这个宏需要三个参数:一个窗口句柄(hwnd),你要处理的消息(WM_xxxx),以及你用来处理此消息的函数。为了更好的说明,我把之前的窗口函数替换成下面的消息分流版:
LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  
switch(msg)
  {
    HANDLE_MSG (hwnd, WM_COMMAND, OnCommand);
    HANDLE_MSG (hwnd, WM_KEYUP,   OnKeyup);
    HANDLE_MSG (hwnd, WM_CLOSE,   OnClose);
    HANDLE_MSG (hwnd, WM_DESTROY, OnDestroy);
    
default:
        
return DefWindowProc(hwnd, msg, wParam, lParam);
  }
}


哇!这是更好的,简洁的并且容易控制的窗口函数。现在你需要定义的的消息处理函数(OnKeyUp, OnCloseOnDestroy)。更加便利的是你可以使用Visual Studio IDE跳到你消息处理函数:


问题是每一次你增加一个消息处理你都必须搜寻WINDOWS.H里的定义,以匹配你的消息处理函数的参数类型,因为你不能随意的使用参数类型:处理函数的格式是清楚定义的。在头文件中反复的查找是单调乏味的任务,且容易出错。消息分流器Wizard工具来搭救你了:它允许你粘贴任何你需要的消息处理的正确的参数。如果你刚刚开始编写代码,它还可以生成一个窗口模板或者对话框函数作为你处理窗口消息的开始。

Message forwarding宏:另一个WINDOWS.H特色

头文件windowsx.h的另一个特色大约是message forwarding。它用来“拆除”消息处理函数的参数为有效的WPARAMLPARAM的值,以调用诸如PostMessageSendMessageCallWindowProc等等,此类的函数。


假设你你希望使用SendMessage函数来发送WM_COMMAND消息给父窗口,通过以一个通知码BN_DBLCLK来“模拟”双击一个命名为IDC_USERCTL的控件。你通常会这样写:

SendMessage (hwndParent, WM_COMMAND,
    MAKEWPARAM(IDC_USERCTL, BN_DBLCLK),
     (LPARAM)GetDlgItem(hwnd, ID_USERCTL));

这是一个复杂的的语法:函数SendMessage期望WPARAM参数的底字节是控件的ID,高字节是通知码;并且我们要通过API函数GetDlgItem获得控件句柄,传给LPARAM参数。

上面的代码可以用Windows.h的message forwarding宏替换,FORWARD_WM_XXXX。对于每一个消息,forwarding宏都使用和消息分流器生成的消息处理函数相同的“捆扎”参数,增加你希望调用的函数并且传给它“拆除”后的LPARAM/WPARAMs。例如,消息分流器Wizard为WM_COMMAND消息和窗口IDmyWnd生成如下的函数原型:
void myWnd_OnCommand (HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
好了,这些分流器参数也同样被用于forwarding宏--因此,如你所期望的,我们之前调用SendMessage函数时展现的混乱可以减少了:
FORWARD_WM_COMMAND (hwndParent, IDC_USERCTL, 
GetDlgItem(hwnd, ID_USERCTL), BN_DBLCLK, SendMessage);
这种方式非常简便,并且可以为所有消息分流器Wizard支持的消息工作。


使用消息分流Wizard工具

当你运行消息分流器Wizard时,你可以看到下面的窗口:



Wizard在左上角的列表框里为你提供了WINDOWS.H里有的消息处理,你可以点击一个或者多个消息。你可以在窗口ID编辑框里指定一个窗口标识,以指定你要发送消息的
窗口。通用的ID有
MainWnd ,About (关于对话框),等等。这个标识会体现在消息处理函数中,和 HANDLE_MSG 宏中,如果你刚刚开始编写代码,它还可以体现在窗
口/对话框处理函数中。“ Make Window Procedure”选定框的作用是:允许你通过选定的消息分流器的宏来生成一个窗口/对话框函数的框架。以这种方式来开始一
个Windows API项目,你可以清晰的编写和组织你的代码,当然还有,避免错误。在窗口下面的两个编辑框会包含分流器的宏和处理所选消息的函数(只是原型)生成的代
码。要注意的是,当你选择“Make Window Procedure”时窗口处理函数的模板代码不出现在这里:它仅体现在你点击了“Copy Macro”时复制到你的C++编辑器中
的代码里。

让我们通过例子来快速的浏览消息分流器Wizard工具的特性。记住你必须先通过#include <windowsx.h>将头文件<windowsx.h>包含到你.C/.CPP文件中。

快速浏览消息分流器Wizard特性

让我们开始吧。假设你已经编写了你的 WinMain 的基本代码:已经成功填充了 WNDCLASS 结构,注册了窗口类,并且编写了消息循环。现在你需要一个为你主函数编写
的消息处理函数。

打开消息分流器Wizard。我们需要为我们的窗口选择消息,因为MCW需要用它来生成我们的消息处理函数。如你所知,Windows程序非常常见的处理消息是
WM_CLOSE
WM_DESTROY WM_CREATE ,所以让我们选择消息分流器处理这些消息来创建窗口函数。然后,我们创建窗口函数的主体消息处理函数。

在列表框中选择
WM_CLOSE , WM_DESTROY WM_CREATE 。因为此窗口是我们程序的主窗口,我们选用main做为窗口的ID。这个窗口ID表识了我们的窗口/对话框,并且
使分流宏和出理函数作为后缀。当然,你要使特定窗口的所有消息处理保持一致。观察下面的编辑框。它们显示 HANDLE_MSG 分流宏和关联的消息处理函数的原型。



但是,等一下......我们说我们需要一个准备好的窗口处理函数。所以单击“Make Window Procedure”选定框,并且确认Window 单选按钮已经被选择了。现在
我们准备好了。要注意的是,对话框的工作也像这样,但是要改变处理函数为对话框类型。

首先,我们需要我们源代码的窗口处理函数。点击“Copy Macro”按钮(或者使用Ctrl-M),最小化Wizard(或者把它放到一边),回到你的IDE并且从剪贴板粘贴
Ctrl-V)代码到你的窗口函数的位置。
Voilá(阿根廷拉丁语,认识的通知下)!你可以得到像下面的代码:
//
// main  Window Procedure
//
LRESULT CALLBACK main_WndProc (HWND hwnd, UINT msg, WPARAM wParam,   LPARAM lParam)
{
  
switch
(msg)
  {
    HANDLE_MSG (hwnd, WM_CLOSE, main_OnClose);
    HANDLE_MSG (hwnd, WM_CREATE, main_OnCreate);
    HANDLE_MSG (hwnd, WM_DESTROY, main_OnDestroy);
      
//// TODO: Add window message crackers here

  defaultreturn DefWindowProc (hwnd, msg, wParam, lParam);
  }
}


这个窗口处理函数以三个消息分流宏展开工作!并且,通过TODO注释提示你记得必须在这里添加消息分流器宏。当你只是要添加一个 HANDLE_MSG 宏到窗口函数中时,
记得取消“Mke Window Procedure”选定框的选择。


但是这些代码现在还什么都做不了,因为我们还需要添加三个我们需要的消息处理函数。回到消息分流器Wizard工具的界面,并且单击“Copy Function”按钮。切
换到你的代码,用鼠标定位到你需要函数主体插入的位置,然后用Ctrl+V或者菜单Edit/Paste粘贴。Wizard自动生成函数,使用 main 标识窗口ID,并修正参数类型
使之符合 WINDOWSX.H 头文件的宏:

//
//Process WM_CLOSE message for window/dialog: main
//
void
 main_OnClose(HWND hwnd)
{
  
// TODO: Add your message processing code here

}
//

//  Process WM_CREATE message for window/dialog: main
//
BOOL main_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
  
// TODO: Add your message processing code here

}
//

//  Process WM_DESTROY message for window/dialog: main
//
void
 main_OnDestroy(HWND hwnd)
{
  
// TODO: Add your message processing code here

}




Wizard还可以自动生成开头注释,和TODO行以提醒你添加代码。现在你可以添加消息处理了,可以是简单的处理处理逻辑,或者复杂窗口函数。你也可以通过选定框移除
注释。

更多的消息分流器Wizard特性

消息过滤

你可以过滤你暂时不打算处理的消息。单击“Filters..”按钮(或者Ctrl+L),你会打开下面对话框。列表的消息按类型分类(分类标准来自Microsoft Spy++的效果),
你可以取消选定你不处理的消息。


要注意的是在 v2.0 当前的问题是,当你打开消息过滤对话框时所有类型的消息都是选定的,当你点击OK,之前选定的消息会丢失(这并不意味着你粘贴到目标代码的
内容会消失)。



简洁窗口模式

你可能需要减小消息分流Wizard窗体的大小。这可以通过取消菜单 View 的 “Show Target Code”选项的选定(或者使用Ctrl+F11)。主窗口会取消目标代码区域:

窗体透明,取消注释和置顶

(译注:Sorry,暂不做翻译了,你可以通过尝试菜单 View 的其它几个选项自己体会^_^)



未来特性

下面的特性会在下一次发布时实现:
  • 帮助文件。
  • 所有消息分流的参数和消息的完整帮助。
  • 支持WTL!:)
  • 窗口ID和配置保存(这个会在下一个2.x版本实现)。
  • 项目配置和“消息映射”(a la MFC)(译注:好像是西班牙语,不认识:P)(这个也会在之后的2.5版本实现)。

快乐的编程!

我希望这个小工具可以让每个 Windows SDK 程序员感兴趣,并且干净的编写 Win32 API 程序。我乐于接受改进这个工具的主意。如果你发现这个程序很好用,请给我发邮件,因为我非常高兴听到任何好的意见。

谢谢所有支持!!You know who you are!(译注:不太明白这个要表达什么意思,“你知道你是谁!”乎?)

任何时候,打开我的主页,你可以在那里找到这个程序的最新更新。

历史:
  略...

关于作者:
Hernán Di Pietro

业余程序员。

开始于1986年,在Commodore 64上编写BASIC程序。
在1990年第一台 8086 PC 上开始编写 QB 4.x。
1995-1996年转移到Visual Basic。
2002年,23岁时开始编写C++程序,并对 Windows API 上瘾。

主页: http://usuarios.lycos.es/hernando



posted on 2006-11-02 00:00 小山日志 阅读(1247) 评论(3)  编辑 收藏 引用 所属分类: windows program

FeedBack:
# re: Message Crack Wizard for Win32 SDK Developer
2006-11-02 19:15 | 刀剑如梦
嗯~~,不错不错。还有这么个好东西。  回复  更多评论
  
# re: Message Crack Wizard for Win32 SDK Developer
2007-02-05 20:34 | Lidi
很好,谢谢  回复  更多评论
  
# re: Message Crack Wizard for Win32 SDK Developer
2007-12-06 20:39 | oldnwind
Programming application for Microsoft Windows 的附录中有详细说明。
应该是很方便的,关键是要有个熟悉过程。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理