http://c.biancheng.net/cpp/html/2960.html

MSG结构体和WndProc窗口过程对于Windows编程非常重要,如果不了解它们,可以说就没有学会Windows编程。

MSG结构体

MSG 结构体用来表示一条消息,各个字段的含义如下:

typedef struct tagMSG{
    HWND hwnd;  //窗口句柄
    UINT message;  //消息类型
    WPARAM wParam;  //附加消息1
    LPARAM lParam;  //附加消息2
    DWORD time;  //消息被传递时候的时间
    POINT  pt;  //消息被传递时光标在屏幕上的位置
} MSG;

对各个字段的说明:
1) 最后两个字段 time 和 pt 一般由系统使用,我们很少用到。

2) message 为消息类型,也就是以 WM 开头的消息(WM 是 Window Message 的缩写 ),例如 WM_CREATE、WM_PAINT、WM_DESTROY、WM_COMMAND 等。

2) wParam 和 lParam 是要重点说明的,它们都表示附加消息。例如,当收到一个字符消息的时,message 的值为 WM_CHAR,但用户到底输入的是什么字符,那么就由 wParam 和 lParam 来说明。wParam、lParam 表示的信息随消息类型的不同而不同,具体细节可以到MSDN中查看。

WPARAM 和 LPARAM 这两种数据类型的定义分别为:

typedef  UINT_PTR  WPARAM;
typedef  LONG_PTR  LPARAM;

在现代操作系统中(32位和64位操作系统),它们一般都表示 32 位的整数。

但在16位操作系统中,WPARAM 表示16位整数,而 LPARAM 表示32位整数,根据匈牙利命名法,16 位的变量通常以W开头,32 位变量通常以L开头。升级到32位操作系统后,WPARAM也被扩展到32位,此时 WPARAM 和 LPARAM 的大小完全相同。

在 Win32 API 的早期,由于还有很多 Win16 API 的软件,为了保证和 Win16 API 的代码可移植性,微软依然保留了 WPARAM 和 LPARAM 两个宏。

不过16位系统早已成为历史,现在你可以认为这两个参数的长度相同。读者可以在 VC / VS 中通过 F12 键或者“转到定义”菜单查看。

wParam 和 lParam 到底表示什么信息

可以肯定的是,消息类型 message 不同,wParam 和 lParam 两个字段表示的附加消息一般也不同。那么,它们到底表示什么呢?其实这个真的不一定,没有什么规律,它们本来就是一个历史遗留问题,也不知道微软到底怎么安排的,只能根据不同的消息类型去 MSDN 中查找。

一般约定,wParam 用来表示控件的ID,或者高 16 位和低 16 位组合起来分别表示鼠标的位置,如果发送消息时需要附带某种结构的指针或者是某种类型的句柄时,习惯上用 lParam。

不过这也不一定是对的,例如对于一个鼠标左键按下的消息 WM_LBUTTONDOW,就用 lParam 来表示鼠标的坐标:

//point 是一个结构体,表示鼠标坐标
//msg 表示一条消息
point.x = LOWORD( msg.lParam );
point.y = HIWORD( msg.lParam );

LOWORD 和 HIWORD 都是宏定义:LOWORD 用来取一个 32 位数的低 16 位,HIWORD 用来取一个32 位数的高 16 位。对于 WM_LBUTTONDOW 消息,低 16 位存放的是 x 坐标,而高 16 位存放的是 y 坐标。

WndProc 窗口过程

窗口过程一般定义为如下的形式:

LRESULT CALLBACK WndProc(
    HWND hwnd,  //窗口句柄
    UINT message,  //消息类型
    WPARAM wParam,  //附加消息1
    LPARAM lParam  //附加消息2
){
// TODO
}

WndProc 的各个参数和 MSG 结构体的前四个字段是一一对应的。需要铭记的是:每产生一条消息,都会调用一次 WndProc 函数。

当用户点击按钮、编辑框、下拉列表框等控件的时候,会产生WM_COMMAND消息。对于不同来源的 WM_COMMAND 消息,wParam、lParam 参数也不同,见下表:

消息来源wParam (高16位)wParam (低16位)lParam
菜单0菜单ID0
快捷键1快捷键ID0
控件控件通知码控件ID控件句柄


注意:

  • 上面说的菜单是指窗口标题下方、客户区上方的下拉菜单,而不是客户区的下拉列表框控件。

  • 控件通知码用来识别控件的操作。例如 Button 控件一般有两种通知码,BN_CLICKED 和 BN_DOUBLECLICKED,前者表示 Button 被单击,后者表示 Button 被双击。


对于 Button 控件,我们可以通过LOWORD(wParam)来获取它的 ID,这就是上节最后一个例子的原理,请参考上节的代码体会。