各种消息
在Windows中,消息分为以下三类:

这些消息可以在窗口处理函数中接受
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
case WM_DESTROY:
case WM_PAINT:
// ...
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
{
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
return 0;
}1.标准消息
| 类型 | 含义 |
|---|---|
| WM_NULL | 空消息,此消息将被接收窗口忽略 |
| WM_CREATE | 应用程序创建一个窗口 |
| WM_DESTROY | 一个窗口被销毁 |
| WM_MOVE | 移动—个窗口 |
| WM_SIZE | 改变一个窗口的大小 |
| WM_ACTIVATE | 一个窗口被激活或失去激活状态 |
| WM_SETFOCUS | 获得焦点后 |
| WM_PAINT | 要求一个窗口重绘自己 |
| WM_CLOSE | 当一个窗口或应用程序要关闭时发送一个信号 |
| WM_QUIT | 用来结束消息循环,可调用PostQuitMessage()函数来产生此消息 |
| WM_ERASEBKGND | 当窗口背景必须被擦除时 (例如在窗口改变大小时) |
| WM_MOUSEMOVE | 鼠标移动 |
| WM_LBUTTONDOWN | 鼠标左键按下 |
| WM_LBUTTONUP | 鼠标左键抬起 |
| WM_RBUTTONDOWN | 鼠标右键按下 |
| WM_RBUTTONUP | 鼠标右键抬起 |
| WM_LBUTTONDBLCLK | 双击鼠标左键 |
| WM_RBUTTONDBLCLK | 双击鼠标右键 |
| WM_MOUSEWHEEL | 鼠标滚轮信息 |
| WM_SIZING | 调整窗口大小时发送此消息给窗口,通过此消息应用程序可以监视或修 改窗口大小和位置 |
| WM_MOVING | 移动窗口时发送此消息给窗口,通过此消息应用程序可以监视或修改窗 口大小和位置 |
| WM_MDICREATE | 发送此消息给多文档的客户窗口来创建一个MDI子窗口 |
| WM_MDIDESTROY | 发送此消息给多文档的客户窗口来关闭一个MDI子窗口 |
| WM_MDIACTIVATE | 发送此消息给多文档的客户窗口通知客户窗口激活另一个MDI子窗口 |
| WM_MDINEXT | MDI客户窗口激活下一个或前一个窗口 |
| WM_HOTKEY | 当用户按下由RegisterHotKey()注册的热键时产生此消息 |
为了防止用户定义的消息ID与系统的消息ID冲突,Microsoft定义了一个宏WM_USER,小于WM_USER的ID被系统使用,大于WM_USER的ID被用户使用。
#define WM_USER 0x0400 // (十进制1024)以下是消息编号的范围。
| 范围 | 含义 |
|---|---|
0到WM_USER-1 | 消息保留供系统使用。 |
WM_USER到0x7FFF | 用于私有窗口类的整型消息 |
WM_APP(0x8000)到0xBFFF | 消息可供应用程序使用。 |
0xC000到0xFFFF | 字符串消息供应用程序使用。 |
大于0xFFFF | 由系统保留。 |
1.1 窗口的创建
在调用Windows的CreateWindow函数创建窗口时,一般会先发出WM_NCCREATE消息然后发出WM_CREATE消息。

1.1.1 WM_NCCREATE
使用CreateWindow函数创建窗口时,CreateWindow先直接发送WM_NCCREATE消息给窗口过程函数
NC,即non-client区域,包括标题栏、窗口边框、最大、最小按钮、滚动条等
- 如果应用程序处理此消息,则应该返回TRUE以继续创建窗口
- 如果应用程序返回
FALSE,则CreateWindow或CreateWindowEx函数将返回一个NULL句柄。不会发送WM_CREATE消息
#define WM_NCCREATE 0x00811.1.2 WM_CREATE
发送完WM_NCCREATE后,CreateWindow函数在窗口创建成功并具在返回之前直接发送WM_CREATE消息给窗口过程函数,窗口过程函数会在创建窗口后但在窗口变为可见之前接收到此消息(可以在此时机初始化滚动条,创建子窗口、控件等等)。
#define WM_CREATE 0x00011.1.3 CREATESTRUCT
在WM_NCCREATE和WM_CREATE中,wParam未使用,IParam为指向CREATESTRUCT结构的指针,其中包含有关正在创建的窗口的信息。
typedef struct tagCREATESTRUCT{
LPVOID IpCreateParams; //创建窗口的基本参数
HANDLE hlnstance; //拥有将创建的窗口的模块实例句柄
HMENU hMenu; //新窗口的菜单句柄
HWND hwndParent; //新窗口的父窗口句柄
int cy; //新窗口的高度
int cx; //新窗口的宽度
int y; //新窗口的左上角Y坐标
int x; //新窗口的左上角X坐标
LONG style; //新窗口的风格
LPCSTR IpszName; //新窗口的名称
LPCSTR IpszClass; //新窗口的窗口类名
DWORD dwExStyle; //新窗口的扩展参数
} CREATESTRUCT;使用CREATESTRUCT指针,注意在case中初始化指针需要使用{}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCCREATE:{
CREATESTRUCT *p = (CREATESTRUCT*)lParam;
return true;
}
case WM_CREATE:{
CREATESTRUCT *p = (CREATESTRUCT*)lParam;
SetWindowText(hWnd,"黑神话悟空"); // 修改窗口标题
return 0;
}
default:
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}1.2 窗口的关闭
当我们点击窗口右上角的关闭按钮,会发送WM_CLOSE消息,但此时程序并不会结束
需要我们使用DestoryWindos()函数销毁窗口
在调用DestoryWindos()把窗口从屏幕移除之后,会发送WM_DESTORY消息用来销毁窗口,此时此时可用于释放与窗口关联的已分配内存对象。
在处理WM_DESTORY消息时,需要调用PostQuitMessage()函数,发送WM_QUIT消息,让GetMessage()结束消息循环
发送WM_DESTORY之后,会发送WM_NCDESTORY通知窗口其非客户区域正在销毁

#define WM_CLOSE 0x0010
#define WM_DESTORY 0x0002
#define WM_NCDESTORY 0x0082注意
1.应用程序处理WM_CLOSE、WM_DESTORY、WM_NCDESTORY消息都应该返回0
2.在这三个消息中,wParam、lParam都未使用
在系统的默认窗口处理函数中,这三个消息逻辑大致如下
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
DestroyWindow(hWnd);
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_NCDESTROY:
return 0;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}1.3 窗口的刷新
1.3.1 WM_PAINT
在Winodws系统中,一切东西几乎都是绘制的,包括窗口、按钮、对话框、图片、以及一切文字,而WM_PAINT消息就是在绘制这些对象时系统发送的消息,它也是WIndows中最重要的消息之一。如果处理此消息,则应用程序返回零。
#define WM_PAINT 0x000F注意:在此消息中wParam与IParam未使用
以下操作会触发WM_PAINT消息
- 程序启动时,
UpdateWindow()函数发送一个WM_PAINT消息来更新指定窗口的客户区。 - 用鼠标调整窗口的大小时会连续触发
- 最小化时不会触发
WM_PAINT消息,但是从最小化还原时会进行触发 - 最大化时会触发
WM_PAINT消息 - 当向屏幕外拖动窗口时,不会触发
WM_PAINT消息,但是拉回到屏幕内时会不断的触发WM_PAINT消息 - 使用
InvalidateRect()函数触发WM_PAINT消息
备注:仅仅移动或者覆盖窗口,不会引I起WM_PAINT,因为窗口的客户区大小并未改变!
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_PAINT: {
OutputDebugString("窗口变化\n");
break;
}
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}OutputDebugString()函数可在vs控制台打印消息
注意
上述代码WM_PAINT的case中,若吧break换成了return 0,那么这段代码就会一直执行
原因是如果当前窗口用户区的某一部分变为无效,则Windows会将一个WM_PAINT消息放到消息队列中,如果不调用BeginPaint和EndPaint函数处理,(同时,也不调用ValidateRect函数),该无效区域不会变为有效,于是Windows将会发送另一个WMPAINT消息,并且会一直发送下去。默认消息处理函数DefWindowProc处理WM_PAINT消息的代码如下
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd, &ps);
EndPaint(hwnd,&ps);
return 0;
}1.3.2 BeginPaint()
BeginPaint函数使窗口无效区域变得有效,从而使windows不再发送WM_PAINT消息(直到窗口大小改变等,是窗口再次变得无效,如果窗口一直无效,则windows会不停的发送WM_PAINT消息)。同时填充PAINTSTRUCT结构,让程序员可以根据ps变量中的标志值进行某些操作。
HDC BeginPaint(
HWND hwnd,
LPPAINTSTRUCT lpPaint;
)参数:
- hwnd:标识要重画的窗口句柄
- lpPaint:指向绘图信息结构的指针
返回值:
- 如果函数成功,返回值是指定窗口的“显示设备描述表”句柄。
- 如果函数失败,返回值是NULL,表明没有得到显示设备的内容。
1.3.3 EndPaint()
EndPaint功能在指定的窗口中标记绘画结束。每个调用BeginPaint函数都需要此函数,但只有在绘制完成之后。
HDC EndPaint(
HWND hwnd,
const PAINTSTRUCT* Lppant;
)参数:
- hwnd:标识已重新绘制的窗口。
- Lppant:指向PAINTSTRUCT结构,其中包含BeginPaint检索到的绘画信息。
返回值:返回值始终为零。
1.3.4 PAINTSTRUCT
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, PPAINTSTRUCT*;- hdc:用于绘画的显示器DC的句柄
- fErase:指定背景是否必须被清除。
- 如果应用程序应该擦除背景,则此值不为零。
- 如果没有背景画笔创建一个窗口类,则该应用程序负责擦除背景。请参阅WNDCLASS结构看一看hbrBackgroundlb成员的说明。
- rcPaint:指定客户区的矩形
- fRestore:保留,由系统内部使用
- fIncUpdate:保留,由系统内部使用
- rgbReserved:保留,由系统内部使用
1.3.5 WM_ERASEBKGND
当窗口的移动,窗口的大小的改变时,必须擦除窗口背景时发送WM_ERASEBKGND消息被发送以准备用于绘画的无效部分的窗口。
Windows会给窗口发出先发出一次或者几次WM_ERASEBKGND消息、紧接着是WM_PAINT消息。
当窗口无效时,DefWindowProc函数默认通过使用WNDCLASS结构中指定的hbrBackground背景画笔擦除背景。
如果hbrBackground为NULL,则应用程序应处理WM_ERASEBKGND消息并清除背景。如果应用程序处理消息并擦除背景,应用程序应返回非零值以响应WM_ERASEBKGND;这表明不需要进一步擦除。如果应用程序返回零,窗口将保持标记为需要擦除(通常,这表示BeginPaint中获取的PAINTSTRUCT结构的fErase成员将为TRUE)。
备注
- wParam中传送设备上下文的句柄。
- IParam中此参数未使用。
LRESULT CALLBACKMyWindowProc(HWNDhWnd,UINTMsg,WPARAMwParam,LPARAM IParam){
switch (Msg){
case WM_ERASEBKGND:{
static int i=0;
char str[64];
sprintf(str,“第%d次接收WM_ERASEBKGND消息!\n",i++);
OutputDebugStringA(str);
return 1;
}
case WM_PAINT:{
static int j=0;
char str[64];
sprint(str,“第%d次接收WM_PAINT消息!\n",j++);
OutputDebugStringA(str);
break;
}
default:
returnDefWindowProc(hWnd,Msg,wParam,IParam);
}
returnDefWindowProc(hWnd,Msg,wParam,IParam);
}1.3.6 UpdateWindow()
UpdateWindow用来更新窗口客户区
BOOL WINAPI UpdateWindow(
HWND hWnd//窗口的句柄
);返回值:如果函数成功,返回值为非零。失败返回零。
TIP
如果窗口的更新区域不是空的,则UpdateWindow函数通过向窗口发送WMPAINT消息来更新指定窗口的客户区。该函数直接将WM_PAINT消息发送到指定窗口的窗口过程,绕过应用程序队列。如果更新区域为空,则不发送WM_PAINT消息。
1.3.7 InvalidateRect()
InvalidateRect函数添加一个矩形到指定窗口的更新区域(必须重绘的窗口客户区域部分)。
BOOL InvalidateRect(
HWND hWnd,
const RECT* IpRect,
BOOL bErase,
);参数:
- hWnd:窗口的句柄
- IpRect:矩形区域,NULL则将整个客户区添加到更新区域。
- bErase:指定更新区域内的背景在处理更新区域时是否要擦除。
- 如果此参数为TRUE,则在调用BeginPaint函数时将清除背景。
- 如果此参数为FALSE,则背景保持不变。
返回值:如果函数成功,返回值为非零。失败返回零。
TIP
无效区域在更新区域中累积,直到发生下一个WM_PAINT消息时处理该区域,或者直到使用ValidateRect或ValidateRgn函数验证该区域为止。只要更新区域不是空的,并且该窗口的应用程序队列中没有其他消息,系统 就会向窗口发送WM_PAINT消息。当参数bErase=TRUE时,WM_ERASEBKGND消息产生,当bErase为false 时WM_ERASEBKGND消息不产生。如果更新区域的任何部分的bErase参数为TRUE,则背景将在整个区域中擦 除,而不仅仅在指定部分中擦除。
1.3.8 RedrawWindow()
RedrawWindow函数更新窗口的客户区指定的矩形或区域。
BOOL RedrawWindow(
HWND hWnd,
const RECT* IprcUpdate,
HRGN hrgnUpdate,
UINT flags
);参数:
- hWnd:窗口的句柄,如果此参数为NULL,则更新桌面窗口。
- IprcUpdate:矩形区域
- hrgnUpdate:更新区域的句柄。如果hrgnUpdate和lprcUpdate参数都为NULL,则将整个客户区添加到更新区域。
- flags:一个或多个重绘标志。
- RDW_ERASE:窗口重新绘制时导致窗口收到WM_ERASEBKGND消息。RDW_INVALIDATE标志也必须指定,否则RDW_ERASE不起作用。
- RDW_FRAME:导致与更新区域相交的窗口的非客户区域的任何部分都收到WM_NCPAINT消息。RDW_INVALIDATE标志也必须指定;否则,RDW_FRAME不起作用。
- RDW_INTERNALPAINT:管窗口的任何部分是否无效,都会导致WM_PAINT消息被发布到窗口。
- RDW_INVALIDATE:使lprcUpdate或hrgnUpdate无效(只有一个可能为非NULL)。如果两者均为NULL,则整个窗口将失效。
UpdateWindow、InvalidateRect、RedrawWindow区别
| 类型 | 区别 |
|---|---|
| UpdateWindow | 如果窗口的更新区域不是空的,则UpdateWindow函数立即向窗口发送WMPAINT消息来更新指定窗口的客户区。 |
| InvalidateRect | 只增加一个无效区域在更新区域中,并不立刻进行重绘,直到发生下一个WM_PAINT消息时处理该区域是才重绘。如果希望立即刷新无效区域,可以在调用InvalidateRect之后调用UpdateWindow。 |
| RedrawWindow | RedrawWindowO则是具有lnvalidateO和UpdateWindowO的双特性。声明窗口的状态为无效,并立即更新窗口,立即调用WMPAINT消息处理。 |
1.3.9 WM_NCPAINT
当窗口客户区以外的部分(如窗口标题栏、菜单栏等)需要需要重画时,系统向程序发出WM_NCPAINT消息。因标准窗口的客户区以外部分为窗口必需部分,因而该消息将默认被发送到DefWindowProc函数进行默认处理。我们可通过截获该消息来实现窗口框架自定义绘制。
处理WM_NCPAINT消息时:wParam为窗口更新区域的句柄。更新区域被剪裁到窗口框架。IParam参数未使用
1.4 鼠标消息
| 消息类型 | 说明 |
|---|---|
| WM_MOUSEMOVE | 鼠标移动 |
| WM_LBUTTONDOWN | 鼠标左键按下 |
| WM_LBUTTONUP | 鼠标左键抬起 |
| WM_RBUTTONDOWN | 鼠标右键按下 |
| WM_RBUTTONUP | 鼠标右键抬起 |
| WM_LBUTTONDBLCLK | 双击鼠标左键 |
| WM_RBUTTONDBLCLK | 双击鼠标右键 |
| WM_MOUSEWHEEL | 鼠标滚轮信息 |
鼠标消息携带的参数:
WMPARAM—-其他按键的状态,例如Ctrl/Shift等
LPARAM鼠标的位置,窗口客户区的坐标系。
LOWORD(LPARAM):X坐标位置
HIWORD(LPARAM):Y坐标位置
注意:HIWORD宏一般用来取得LPARAM的高位(高16位),LOWORD返回值是指定值的低16位
#define HIWORD(I) ((WORD)(((DWORD)(I)>>16)&0xFFFF))
#define LOWORD(I) ((WORD)(((DWORD_PTR)(I))&OxFFFF))#include <windows.h>
#include <iostream>
#include <string>
using namespace std;
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
string str="鼠标左键按下"+to_string(x) +","+ to_string(y)+"\n";
OutputDebugString(str.c_str());
break;
}
case WM_RBUTTONDOWN:
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
string str="鼠标右键按下"+to_string(x) +","+ to_string(y)+"\n";
OutputDebugString(str.c_str());
break;
}
case WM_LBUTTONDBLCLK:
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
string str="鼠标左键双击"+to_string(x) +","+ to_string(y)+"\n";
OutputDebugString(str.c_str());
break;
}
case WM_MOUSEMOVE:
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
string str="鼠标移动:"+to_string(x) +","+ to_string(y)+"\n";
OutputDebugString(str.c_str());
break;
}
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}注意
想要窗口响应左键双击消息,需要在注册窗口时给style添加CS_DBLCLKS
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
{
WNDCLASS wc = { };
wc.style = CS_DBLCLKS;
// ...
RegisterClass(&wc);
}1.5 键盘信息
系统按键消息,与ALT键组合的组合键
| 消息类型 | 说明 |
|---|---|
| WM_SYSKEYDOWN | 按键按下 |
| WM_SYSKEYUP | 按键弹起 |
非系统按键消息
| 消息类型 | 说明 |
|---|---|
| WM_KEYDOWN | 按键按下 |
| WM_KEYUP | 按键弹起 |
注意
- 除Print键之外都有“按下”消息。
- 所有键都存在“弹起”消息。
- 根据MSDN说明,只有下面这些键才会产生字符消息:
- 任何字符键
- 回退键(BACKSPACE)
- 回车键建(carriage return)
- ESC
- SHIFT + ENTER(linefeed换行)
- TAB
键盘驱动程序为每个实体键对应都有一个统一的虚拟键,Windows定义的虚拟键都定义在WinUser.h这个头文件里面,都是以VK_作为前缀,比如回车键的虚拟键是VK_RETURN。
| 键位名称 | 描述 |
|---|---|
| VK_0 到 VK_9 | 表示键盘上数字“0”到“9”键(ASCII码为0x30 - 0x39) |
| VK_A 到 VK_Z | 表示键盘上字母“A”到“Z”键(ASCII码为0x41 - 0x5a) |
| VK_F1 到 VK_F12 | 表示键盘上“F1”到“F12”键 |
| VK_ENTER | 表示键盘上“ENTER”键 |
| VK_SHIFT | 表示键盘上“SHIFT”键 |
| VK_SPACE | 表示键盘上“SPACEBAR”键 |
| VK_CONTROL | 表示键盘上“CTRL”键 |
| VK_SUBTRACT | 表示键盘上“-”键 |
| VK_TAB | 表示键盘上“TAB”键 |
| VK_ALT | 表示键盘上“ALT”键 |
| VK_BACK_SPACE | 表示键盘上“BACKSPACE”键 |
| VK_CAPS_LOCK | 表示键盘上“CAPS LOCK”键 |
| VK_CLOSE_BRACKET | 表示“]”键 |
| VK_COMMA | 表示“,”键 |
| VK_DECIMAL | 表示数字键盘上的“.”键 |
| VK_DELETE | 表示键盘上“DELETE”键 |
| VK_DIVIDE | 表示“/”键 |
| VK_DOWN | 表示键盘上“向下箭头”键 |
| VK_END | 表示键盘上“END”键 |
| VK_ADD | 表示数字键盘的“+”键 |
| VK_EQUALS | 表示键盘上“=”键 |
| VK_ESCAPE | 表示键盘上“ESC”键 |
| VK_UP | 上键 |
| VK_DOWN | 下键 |
| VK_LEFT | 左键 |
| VK_RIGHT | 右键 |
根据不同按键处理不同逻辑
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN:
UINT vkCode = wParam;
switch (vkCode) {
case VK_UP:
OutputDebugString("按下了上箭头");
break;
case 'A':
OutputDebugString("按下了A");
break;
}
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}2.命令消息
以WM_COMMAND表示,来自菜单、加速键或工具栏按钮的消息。
#define WM_COMMAND 0x0111 // (十进制273)备注: LOWORD(wParam)是菜单或加速键的ID,菜单的分隔条的ID为0。对于系统菜单的菜单项和加速键,点击后,Windows会向所属的窗体发送WM_SYSCOMMAND,而不是WM_COMMAND消息。
3.通告消息
以WM_COMMAND或WM_NOTIFY形式呈现的。表示,由各种控件产生的消息,例如,按钮的单击等,为的是向其父窗口(通常是对话框)通知事件的发生。
备注:
LOWORD(wParam)是控件ID,HIWORD(wParam)是Notification Code,比如BN_CLICKED,BN_DBLCLK等,标志用户对控件的操作,双击,单击之类。
Notification Code根据不同控件的命名规律:
- Button按钮:BN_XXX
- ListBox列表框:LBN_XXX
- ComboBox组合框:CBN_XXX
- Tab框:TBN_XXX
- Edit编辑框:EN_XXX
- Static静态文本:STN_XXX
3.1 WM_COMMAND
当我们按下一个菜单选项,或者一个控件需要通知父窗口一个事件发生(如鼠标单击、双击等),或者快捷键被按下时,Windows将会发送一个WM_COMMAND消息给父窗口。
- 并通过wParam高位置1或0区分菜单、快捷键、或者控件事件Code,
- 通过wParam低位可知道菜单ID或控件ID,通过IParam知道控件句柄。
| WM_COMMAND消息来源 | WPARAM高位 | WPARAM低位 | LPARAM |
|---|---|---|---|
| 菜单 | 0 | 菜单ID | 0 |
| 快捷键 | 1 | 快捷键对应菜单ID | 0 |
| 控件 | 响应Code(如BN_CLICKED) | 控件ID | 控件句柄 |
3.2 WM_NOTIFY
WM_COMMAND可携带的信息较少,WM_NOTIFY比WM_COMMAND功能更强大,可以存储一些额外的信息
- Edit,Button,ListBox等发送WM_COMMAND消息
- 而ListView,Toolbar,Tree等控件发送WM_NOTIFY消息,因为需要提供的信息更多。
| 消息类型 | WPARAM | LPARAM |
|---|---|---|
| WM_NOTIFY | 发生WM_NOTIFY消息的控件ID | NMHDR指针 |
我们将所有附加信息都存放在NMHDR(Notify Message Handler)的一个公共结构体中,该结构体指针通过LPARAM通知到父窗口。NMHDR如下:
typedef struct tagNMHDR
{
HWND hwndFrom; // 控件句柄
UINT_PTR idFrom; // 控件ID
UINT code; // NM_code
} NMHDR;每个控件根据其自身特点来定义结构体,但要求第一个字段一定是NMHDR。如果我们需要知道ListView选中的行和列,那么可以这么定义:
typedef struct tagNMLISTVIEW
{
NMHDR hdr; // NMHDR.
int iltem; // 行号.
int iSubltem; // 列号.
UINT uNewState;
UINT uOldState;
UINT uChanged;
POINT ptAction;
LPARAM IParam;
}NMLISTVIEW,*LPNMLISTVIEW;一些Notification Code对所有新的Windows控件来说是通用的:
| Notification code | Sentbecause |
|---|---|
| NM_CLICK | 用户在控件内单击鼠标左键 |
| NM_DBLCLK | 用户在控件内双击鼠标左键 |
| NM_RCLICK | 用户在控件内单击鼠标右键 |
| NM_RDBLCLK | 用户在控件内双击鼠标右键 |
| NM_RETURN | 用户在控件具有输入焦点的时候按下回车 |
| NM_SETFOCUS | 控件获得输入焦点 |
| NM_KILLFOCUS | 控件失去输入焦点 |
| NM_OUTOFMEMORY | 控件因为没有足够的可用内存而不能完成一项操作 |
