Windows常用消息处理与自定义消息
Windows消息简介
windows消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做TMsg。
Windows程序都是通过消息机制驱动运行的。
消息分类:
一般根据消息是否进入消息队列可分为两类:
不进队列消息:指由Windows直接调用消息处理函数,把消息直接交给其处理。如 WM_CREATE,WM_SIZE等。
//WS: Window Style HWND hWnd = CreateWindow( szAppClassName, //窗口类型名 TEXT("这是我的第一个Windows窗口程序"), //窗口左上角标题 WS_BORDER | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU, //窗口的风格 200, 300, //窗口左上角坐标 800, 600, //窗口的宽和高 NULL, //父窗口句柄 NULL, //菜单句柄 hInstance, //应用程序实例句柄 (void*)100 //创建窗口的附加参数,WM_CREATE消息,lParam来接受这个参数 ); if (hWnd == NULL) { MessageBox(NULL, TEXT("创建窗口失败"), TEXT("提示"), MB_OK); return 0; }
case WM_CREATE://窗口处理消息,发生在什么时候:窗口还没有显示的时候,通过调用CreateWindow函数或者CreateWindowEx函数,发出这个消息 { MessageBox(NULL, L"这是WM_CREATE消息", L"提示", MB_OK);//阻塞函数,不点不往下走 CREATESTRUCT* pcs = (CREATESTRUCT*)lParam; break; }
当执行完CreateWindow后才发出WM_CREATE消息,此时窗口还没显示出来。
进队列消息:指Windows将消息放入到程序中的消息队列中取,并通过程序中的消息循环,循环把消息取出,经过一定处理(如例子中经过translate),然后由函数
DispathMessage函数将消息分发给消息处理函数处理,这一类消息为进队列消息。常见的击键的消息(WM_KEYDOWN、WM_KEYUP)键盘输入产生字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)、
鼠标键(WM_LBUTTONDOWN)、计时消息(WM_TIMER)、刷新消息(WM_PAINT)和退出消息(WM_QUIT),这类消息一般是由用户输入引发的。
按照功能划分,消息分为两大类:
系统定义消息
(1)标准消息
除WM_COMMAND之外,所有以WM_开头的消息都是标准消息,例如鼠标单击、移动,键盘左击、右击。
(2)命令消息
来自菜单,加速键或工具栏按钮的消息。这类消息都以WM——COMMAND形式呈现。WM_COMMAND, LOWORD(wParam)表示菜单项,工具栏按钮或一般控件的ID如编辑框,按钮等。如果是控件, HIWORD(wParam)表示控件消息类型
(3)通告消息
由复杂控件产生的消息。这类消息也是以WM——COMMAND形式呈现。 这是最灵活的消息格式, 其Message, wParam, lParam分别为:WM_NOTIFY, 控件ID,指向NMHDR的指针。NMHDR包含控件通知的内容, 可以任意扩展。
用户自定义消息
WM_USER ,1024以下的定义的是系统消息,1024以上的是用户自定消息。
消息发送
提供了两种方式来发送消息:SendMessage和PostMessage。
两者联系与区别:
1.系统定义消息和用户自定义消息均可发送。
2.SendMessage: 是一个阻塞函数,如果发送的消息没有没处理完,则不返回,处理完成,返回结果,并且结果就是SendMessage的返回值
PostMessage:是一个非阻塞函数,只负责把消息发过去,不会等待消息的处理完成的结果。
3.SendMessage返回值为LRESULT长整型(long),PostMessage为bool值。
4.跨进程跨线程发送消息,推荐使用PostMessage。
常用消息举例
消息名称 | 消息参数 | 备注说明 |
WM_CREATE消息 |
WPARAM: 没有使用的 LPARAM: CREATESTRUCT结构体指针 |
|
WM_CLOSE消息 |
WPARAM: 没有使用的 LPARAM: 没有使用的 |
窗口关闭消息 , 此时还能还原出来窗口,发生在:点击关闭按钮的时候 |
WM_DESTROY消息 |
表示窗口销毁消息, 凡是执行到了WM_DESTROY消息了,那么此时此时已经没有回头路了,不能还原窗口了,因为界面已经被销毁了 发生在什么时候:程序退出,进行清理工作了 |
|
WM_LBUTTONDOWN | 鼠标左键点击客户区消息 | |
WM_LBUTTONUP | 鼠标左键抬起 | |
WM_RBUTTONDOWN | 鼠标右键按下 | |
WM_RBUTTONUP | 鼠标右键抬起 | |
WM_LBUTTONDBLCLK | 鼠标左键双击消息。createwindows的时候style属性要加上CS_DBLCLKS,不然就只是单纯的 两次单击,不会发送此消息 | |
WM_MOUSEMOVE | 鼠标移动消息 | |
WM_KEYDOWN | 键盘按下消息 | |
WM_KEYUP | 按键抬起消息 | |
WM_CHAR | 字符消息 |
Demo附录
#include <Windows.h> #include "resource.h" #define UM_TEST WM_USER+1 //窗口处理函数 //第一个参数:当前窗口句柄 //第二个参数:消息类型 //第三个参数:附加信息/附加参数 //第四个参数:附加信息/附加参数 LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow) { //创建一个窗口 //流程 //1.设计窗口类 wchar_t szAppClassName[] = TEXT("WINDOWS"); WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; //窗口类的风格 wc.lpfnWndProc = WindowProc; //窗口回调函数/窗口处理函数 wc.cbClsExtra = 0; //窗口类的附加内存大小 wc.cbWndExtra = 0; //窗口附加内存大小 wc.hInstance = hInstance; //当前应用程序实例句柄 wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1)); //图标句柄 wc.hCursor = LoadCursor(NULL, IDC_ARROW); //光标句柄 wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 0)); //红,绿,蓝 0->255 0:最暗, 255:最亮, RGB(0,0,0):黑色, RGB(255,255,255):白色 wc.lpszMenuName = NULL; //菜单名 wc.lpszClassName = szAppClassName; //窗口类型名 Spy++ //2.注册窗口类 //ATOM //ATOM if (0 == RegisterClass(&wc)) { MessageBox(NULL, TEXT("此程序不能运行在Windows NT平台."), TEXT("提示"), MB_OK); return 0; } //3.创建窗口 //WS: Window Style HWND hWnd = CreateWindow( szAppClassName, //窗口类型名 TEXT("这是我的第一个Windows窗口程序"), //窗口左上角标题 WS_BORDER | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU, //窗口的风格 200, 300, //窗口左上角坐标 800, 600, //窗口的宽和高 NULL, //父窗口句柄 NULL, //菜单句柄 hInstance, //应用程序实例句柄 (void*)100 //创建窗口的附加参数,WM_CREATE消息,lParam来接受这个参数 ); if (hWnd == NULL) { MessageBox(NULL, TEXT("创建窗口失败"), TEXT("提示"), MB_OK); return 0; } //4.显示窗口 //SW_SHOW:原来在什么位置,就在什么位置显示 //SW_SHOWMAXIMIZED:最大化显示 //SW_SHOWMINIMIZED:最小化显示 //SW_SHOWNORMAL:正常显示 //SW_HIDE:隐藏运行 ShowWindow(hWnd, SW_SHOW); //5.更新窗口 //窗口显示的时候,重新绘制一下 UpdateWindow(hWnd); //6.消息循环 //Windows程序都是通过消息机制驱动运行 //GetMessage:什么时候返回FALSE //当获取到WM_QUIT消息时,返回FALSE,退出循环,没有获取到这个消息,返回非0,不退出循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) //0 { //将虚拟键消息转化为字符消息 //一个按下消息+抬起消息=》字符消息,WM_KEYDOWN和WM_KEYUP=>WM_CHAR TranslateMessage(&msg); //将消息分发给窗口处理函数 DispatchMessage(&msg); } return 0; } //窗口处理函数 //Windows Message缩写 //消息分为两类: //不进队列消息 WM_CREATE //进队列消息 :大部分消息时属于这一类 //WM_CREATE消息 //WPARAM: 没有使用的 //LPARAM: CREATESTRUCT结构体指针 //WM_CLOSE消息 窗口关闭消息 , 此时还能还原出来窗口 //发生在:点击关闭按钮的时候 //WPARAM: 没有使用的 //LPARAM: 没有使用的 //WM_DESTROY消息 表示窗口销毁消息, 凡是执行到了WM_DESTROY消息了,那么此时此时已经没有回头路了,不能还原窗口了,因为界面已经被销毁了 //发生在什么时候:程序退出,进行清理工作了 //WM_LBUTTONDOWN鼠标左键点击客户区消息 //WPARAM: //LPARAM: //WM_LBUTTONUP:鼠标左键抬起 //WM_RBUTTONDOWN:鼠标右键按下 //WM_RBUTTONUP:鼠标右键抬起 //WM_LBUTTONDBLCLK://鼠标左键双击消息 //WM_MOUSEMOVE:鼠标移动消息 //WM_KEYDOWN:键盘按下消息 //WPARAM: //LPARAM: //WM_KEYUP:按键抬起消息 //WM_CHAR:字符消息 //后续讲解 //WM_COMMAND消息 //WM_SYSCOMMAND消息 //消息的分类:0=>65535(消息范围) //1、系统消息 , 刚才介绍的这一些都是系统消息 //WM_USER 1024以下的定义的是系统消息 //1024以上的是用户自定消息 //2、自定义消息 //如果自定义消息 //宏定义 //3.发送消息 //既可以发送系统消息,又可以发送用户自定义消息 //SendMessage: 是一个阻塞函数,如果发送的消息没有没处理完,则不返回,处理完成,返回结果,并且结果就是SendMessage的返回值 //PostMessage:是一个非阻塞函数,只负责把消息发过去,不会等待消息的处理完成的结果 //SendMessage:用于自己进程发送消息 //PostMessage:向进程外发送信息 LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hDC; //wchar_t szMsg[200]; ////sprintf字符串格式化函数,窄字符 //wsprintf(szMsg, L"窗口句柄:0X%x\t消息:0X%x\twParam:%d\tlParam:%d\n", hWnd, uMsg, wParam,lParam);//字符串格式化函数,宽字符 //OutputDebugString(szMsg);//仅限于调试 switch (uMsg) { case WM_SIZE: { MessageBox(NULL, L"这是WM_SIZE消息,执行ShowWindow后创建该消息", L"提示", MB_OK);//阻塞函数,不点不往下走 break; } case WM_CREATE://窗口处理消息,发生在什么时候:窗口还没有显示的时候,通过调用CreateWindow函数或者CreateWindowEx函数,发出这个消息 { MessageBox(NULL, L"这是WM_CREATE消息", L"提示", MB_OK);//阻塞函数,不点不往下走 CREATESTRUCT* pcs = (CREATESTRUCT*)lParam; break; } case WM_LBUTTONDOWN://鼠标左键点击客户区消息 { //MessageBox(hWnd, L"WM_LBUTTONDOWN", L"提示", MB_OK); //int xPos = LOWORD(lParam); //WORD:字,unsigned short , 0-》65535 //int yPos = HIWORD(lParam); //wchar_t szText[100]; //wsprintf(szText, L"当前鼠标点击坐标(%d,%d)", xPos, yPos); //SetWindowText(hWnd, szText); //if (wParam & MK_CONTROL) //{ // wcscat(szText, L"按下了ctrl"); //} //if (wParam & MK_MBUTTON) //{ // wcscat(szText, L"按下了鼠标中键"); //} //if (wParam & MK_RBUTTON) //{ // wcscat(szText, L"按下了鼠标右键"); //} //if (wParam & MK_SHIFT) //{ // wcscat(szText, L"按下了鼠标Shift键"); //} //if (wParam & MK_LBUTTON) //{ // wcscat(szText, L"按下了鼠标左键;"); //} // //////把坐标设置到窗口的标题 //SetWindowText(hWnd, szText); //发送系统消息 //SendMessage(hWnd, WM_CLOSE, 0, 0); //发送一个用户自定义消息 int result = SendMessage(hWnd, UM_TEST, 45, 50);//阻塞 BOOL b = PostMessage(hWnd, UM_TEST, 45, 50);//非阻塞 int a = 20; /*HWND hNotepad = FindWindow(L"Notepad", L"无标题 - 记事本"); if (hNotepad == NULL) { MessageBox(NULL, L"没有查找到窗口", L"提示", MB_OK); break; }*/ //发送一个关闭消息 //PostMessage(hNotepad, WM_CLOSE, 0, 0); //SendMessage(hNotepad, WM_CLOSE, 0, 0); break; } case WM_LBUTTONDBLCLK://createwindows的时候style属性要加上CS_DBLCLKS,不然就只是单纯的 两次单击,不会发送此消息) { SetWindowText(hWnd, L"双击"); break; } case UM_TEST://用户自定义消息 { int a = (int)wParam; int b = (int)lParam; int result = a + b; return result; } case WM_MOUSEMOVE://鼠标移动消息 { int xPos = LOWORD(lParam); //WORD:字,unsigned short , 0-》65535 int yPos = HIWORD(lParam); wchar_t szText[100]; wsprintf(szText, L"当前鼠标点击坐标(%d,%d)", xPos, yPos); if (wParam & MK_CONTROL) //按位& { wcscat(szText, L"按下了Ctrl;"); } //把坐标设置到窗口的标题 SetWindowText(hWnd, szText); break; } case WM_KEYDOWN://键盘按下消息 { //MessageBox(hWnd, L"提示", L"OK", MB_OK); switch (wParam) { case VK_RETURN://回车键 SetWindowText(hWnd, L"按下了回车键"); break; } break; } case WM_CHAR://字符消息 { char chCharCode = (TCHAR)wParam; wchar_t szText[100]; wsprintf(szText, L"当前字符:%c", chCharCode); SetWindowText(hWnd, szText); break; } case WM_PAINT://客户区绘图消息, GDI绘图技术 { //开始绘图 HDC hDC = BeginPaint(hWnd, &ps); //Rectangle(hDC, 0, 0, 200, 200);矩形 //绘制椭圆 Ellipse(hDC, 0, 0, 300, 200); //结束绘图 EndPaint(hWnd, &ps); break; } case WM_CLOSE://窗口关闭消息 { //ShowWindow(hWnd, SW_HIDE);//隐藏 //return 1;//表示我已经处理了 if (IDYES == MessageBox(hWnd, L"您确定要关闭吗?", L"温馨提示", MB_YESNO)) { DestroyWindow(hWnd); //销毁窗口, 干掉界面,不会发出WM_QUIT,会发出一个WM_DESTROY消息 } else { //break;//还是执行了DefWindowProc系统默认处理,界面销毁,不会退出进程 //return 0;//没有处理,没有人去处理 return 1;//我已经处理了, } break; } case WM_DESTROY://窗口销毁消息 //ShowWindow(hWnd, SW_SHOW); //无力回天了 PostQuitMessage(0); //发出WM_QUIT break; } return DefWindowProc(hWnd, uMsg, wParam, lParam);//交给操作系统默认处理 }