Win32编程之消息机制(二)
一、Windows的消息概念
1.消息组成:
- 窗口句柄
- 消息ID
- 消息的两个参数(两个附带信息)
- 消息产生的时间
- 消息产生时鼠标位置
1 2 3 4 5 6 7 8 | typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time ; POINT pt; } MSG |
2.消息的作用
当系统通知窗口工作时,就采用消息的方式派发给窗口
二、消息循环
1 2 3 4 5 6 | //消息循环 MSG nMsg = { 0 }; while (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); //将虚拟消息转换为字符消息 DispatchMessage(&nMsg); //将消息分发给窗口处理函数 } |
1.GetMessage():获取本进程的消息
1 2 3 4 5 6 | WINUSERAPI BOOL WINAPI GetMessageA( _Out_ LPMSG lpMsg, //存放获取到的消息BUFF _In_opt_ HWND hWnd, //窗口句柄 _In_ UINT wMsgFilterMin, //获取消息的最小ID _In_ UINT wMsgFilterMax //获取消息的最大ID<br>); |
lpMsg:当获取到消息后,将消息的参数存放到MSG结构中
hWnd:获取到hWnd所指定窗口的消息
wMsgFilterMin和wMsgFilterMax:只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围
2.TranslateMessage():翻译消息,将按键消息翻译成字符消息
1 2 3 4 | BOOL WINAPI TranslateMessage( _In_ CONST MSG *lpMsg //要翻译的消息地址 ); |
检查消息是否有按键消息,如果不是按键消息,不做任何处理,继续执行
3.DispatchMessage()函数执行流程:
1 2 3 4 5 6 | DispatchMessage(&nMsg) { nMsg.hwnd --> 通过窗口句柄找到保存窗口数据的内存 -->然后找到窗口消息处理函数 WndProc(nMsg.hWnd, nMsg.msgID, nMsg.wParam, nMsg.lParam) { //执行消息处理函数 } } |
每个窗口都必须具有窗口处理函数:
1 2 3 4 5 6 | LRESULT CALLBACK WindProc( HWND hWnd, //窗口句柄 UINT msgID, //消息ID WPARAM wParam, //消息参数 LPARAM lParam //消息参数 ); |
当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数,在窗口处理函数中,不处理的消息,使用缺省窗口处理函数,例如:DefWindowProc
三、创建消息
1.WM_DESTROY
- 产生时间:窗口被销毁时消息
- 附带消息:wParam:为0,IParam:为0
- 一般用法:常用于在窗口被销毁之前,做相应的善后处理,例如资源,内存等
2.WM_SYSCOMMAND消息
- 产生时间:当点击窗口的最大化,最小化,关闭等
- 附带消息:wParam:具体点击的位置,例如关闭SC_CLOSE等,lParam:鼠标光标的位置,LOWORD(lParam)为水平位置,HIWORD(lParam)为垂直位置,lParam低两字节是水平坐标,高两字节是垂直坐标
- 一般用法:常用在窗口关闭时,提示用户处理
3.WM_CREATE消息
- 产生时间:在窗口创建成功但还未显示时
- 附带信息:wParam:为0,lParam:为CREATESTRUCT类型的指针,通过这个指针可以获取CreateWindowEx中的全部12个参数
- 一般用法:常用于初始化窗口的参数,资源等等,包括创建子窗口等
4.WM_SIZE消息
- 产生时间:在窗口的大小发生变化后
- 附带消息:wParam:窗口大小变化的原因,lParam:窗口变化后的大小,LOWORD(lParam)为变化后的宽度,HIWORD(lParam)为变化后的高度
- 一般用法:常用于窗口大小变化后,调整窗口内各个部分的布局
5.WM_QUIT消息
- 产生时间:程序员发送
- 附带消息:wParam:PostQuitMessage函数传递的参数,lParam:0
- 一般用法:用于结束消息循环,当GetMessage收到这个消息后,会返回FALSE,结束while处理,退出消息循环
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | #include <Windows.h> #include <stdio.h> HANDLE g_hOutout = 0; //接收标准输出句柄 //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_SIZE: { short nWidth = LOWORD(lParam); short nHight = HIWORD(lParam); char szText[256] = { 0 }; sprintf (szText, "WN_SZIE:宽度:%d,高度:%d\n" , nWidth, nHight); WriteConsole(g_hOutout, szText, strlen (szText), NULL, NULL); break ; } case WM_CREATE: { CREATESTRUCT* pcs = (CREATESTRUCT*)lParam; char * pszText = ( char *)pcs->lpCreateParams; MessageBox(NULL, pszText, TEXT( "提示" ), MB_OK); CreateWindowEx(0, "EDIT" , "Hello" , WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 0, 200, 200, hWnd, NULL, 0, NULL); break ; } case WM_DESTROY: PostQuitMessage(0); break ; case WM_SYSCOMMAND: { if (wParam == SC_CLOSE) { int nRet = MessageBox(hWnd, TEXT( "是否退出" ), TEXT( "提示" ), MB_YESNO); if (nRet == IDYES) { //什么都不写 } else { return 0; } } break ; } default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } //入口函数 int CALLBACK WinMain( HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) { AllocConsole(); //增加DOS窗口 g_hOutout = GetStdHandle(STD_OUTPUT_HANDLE); //设计窗口类 WNDCLASS wc = { 0 }; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = ( HBRUSH )(COLOR_WINDOW + 1); wc.hCursor = NULL; wc.hIcon = NULL; wc.hInstance = hIns; wc.lpfnWndProc = WindProc; wc.lpszClassName = TEXT( "Main" ); wc.lpszMenuName = NULL; wc.style = CS_HREDRAW | CS_VREDRAW; //注册窗口类 RegisterClass(&wc); //将以上所有赋值全部写入操作系统 const char * pszText = "hello data" ; //在内存中创建窗口 HWND hWnd = CreateWindowEx(0, wc.lpszClassName, TEXT( "Window" ), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, ( LPVOID )pszText); <br> //显示窗口 ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); //消息循环 MSG nMsg = { 0 }; while (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); //将虚拟消息转换为字符消息 DispatchMessage(&nMsg); //将消息分发给窗口处理函数 } return 0; } |
6.WM_PAINT消息
- 产生时间:当窗口需要绘制的时候
- 附带消息:wParam:0,lParam:0
- 专职用法:用于绘图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /* 消息处理步骤 1.开始绘图 HDC BeginPaint( HWND hWnd; //绘制窗口 LPPAINTSTRUCT lpPaint //绘制参数的BUFF );//返回绘图设备句柄HDC 2.正式绘图 3.结束绘图 BOOL EndPaint( HWND hWnd, //绘制窗口 CONST PAINTSTRUCT *lpPaint //绘图参数的指针BeginPaint返回 ); */ //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_PAINT: { const char * pszText = "WM_PAINT\n" ; WriteConsole(g_hOutput, pszText, strlen (pszText), NULL, NULL); //下述绘图代码,必须放在处理WM_PAINT消息时调用 PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); TextOut(hdc, 100, 100, "hello" , 5); EndPaint(hWnd, &ps); break ; } case WM_LBUTTONDOWN: { /* 窗口无效区域:需要重新绘制的区域 BOOL InvalidateRect( HHWND hWnd, //窗口句柄 CONST RECT* lpRect, //区域的矩形坐标 BOOL bErase//重绘前是否先擦除 ); */ InvalidateRect(hWnd, NULL, TRUE); break ; } default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } |
四、消息循环原理
GetMessage:从系统获取消息,将消息从系统中移除,阻塞函数,当系统无消息时,会等候下一条消息
PeekMessage:以查看的方式从系统获取消息,可以不将消息从系统移除,非阻塞函数,当系统无消息时,返回FALSE,继续执行后续代码
WINUSERAPI BOOL WINAPI
PeekMessageA(
_Out_ LPMSG lpMsg,//message information
_In_opt_ HWND hWnd,//handle to window
_In_ UINT wMsgFilterMin,//first message
_In_ UINT wMsgFilterMax,//last message
_In_ UINT wRemoveMsg//不移除标识:PM_NOREMOVE
);
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //消息循环 MSG nMsg = { 0 }; while ( true ) { if ((PeekMessage(&nMsg, NULL, 0, 0, PM_NOREMOVE))) { //有消息 if (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); DispatchMessage(&nMsg); } else { break ; } } else { //空闲处理 WriteConsole(g_hOutout, "onIdle" , strlen ( "onIdle" ), NULL, NULL); } } |
SendMessage:发送消息,会等候消息处理的结果
PostMessage-投递消息,消息发出后立刻返回,不等厚消息执行结果
1 2 3 4 5 6 | BOOL SendMessage/PostMessage( HWND hWnd, //消息发送的目的窗口 UNIT Msg, //消息ID WPARAM wParam, //消息参数 LPARAM lParam, //消息参数 ); |
五、消息的分类
- 系统消息:ID范围(0-0x03FF),由系统定义好的消息,可以在程序中直接使用
- 用户自定义消息:ID范围(0x0400-0x7FFF),由用户自己定义,满足用户自己的需求,由用户自己发出消息。并处理,自定义消息宏:WM_USER
1.用户自定义消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #define WM_MYMESSAGE WM_USER + 1001 //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_CREATE: { PostMessage(hWnd, WM_MYMESSAGE, 1, 2); break ; } case WM_MYMESSAGE: { char szText[256] = { 0 }; sprintf (szText, "自定义消息消息被处理:wParam=%d, lParam=%d\n" , wParam, lParam); MessageBox(hWnd, szText, "提示" , MB_OK); break ; } default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } |
六、消息队列
1.消息队列概述
- 消息队列是用来存放消息的队列
- 消息在队列中先入先出
- 所有窗口程序都具有消息队列
- 程序可以从队列中获取信息
- 系统消息队列:由系统维护的消息队列,存放系统产生的消息,例如鼠标,键盘等
- 程序消息队列:属于每一个应用程序(线程)的消息队列,由应用程序(线程)维护
进程产生的消息会先发送到系统的消息队列中,然后系统消息队列根据消息中的窗口句柄,将消息下发到指定窗口的程序消息队列中,然后应用程序通过GeMessage(...)从程序消息队列中获取消息,再通过TranslateMessage(...)翻译,DispatchMessage(...)转发消息,最后执行消息函数WinProc(...);所以,所有的进程消息只要能进入消息队列中,那么一定会先进入系统消息队列中,需要注意的是,PostMessage(...)发送的消息是进入系统消息队列中的。
2.消息和消息队列的关系
当鼠标,键盘产生消息时,会将消息存放到系统消息队列,系统会根据存放的消息,找到对应程序的消息队列,将消息投递到程序的消息队列中。根据消息和消息队列之间使用关系,将消息分成两类:
(1).队列消息:消息的发送和获取,都是通过消息队列完成的,消息发送后,首先放入队列中,然后通过消息循环,从队列当中获取。
1 2 3 | GetMessage:从消息队列中获取消息 PostMessage:将消息投递到消息队列中 常见队列消息:WM_PAINT、键盘、鼠标、定时器等 |
(2).非队列消息:消息的发送和获取,是直接调用消息的窗口处理完成的,消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用处理函数,完成消息。
1 2 | SendMessage:直接将消息发送给窗口的处理函数,并等候处理结果 常见消息:WM_CREATE、WM_SIZE等 |
七、GetMessage工作原理
- 在程序(线程)消息队列查找消息,如果队列有消息,检测消息是否满足指定条件(HWND,ID范围),不满足条件就不会取出消息,否则从队列中取出消息并返回
- 如果程序(线程)消息队列中没有消息,向系统消息队列获取属于本程序的消息,如果系统队列的当前消息属于本程序,系统会将消息转发到程序消息队列中
- 如果系统消息队列中也没有消息,检测当前进程的所有窗口的需要重新绘制的区域,如果发现有需要重新绘制的区域,产生WM_PAINT消息,取得消息返回处理
- 如果没有重新绘制区域,检查定时器,如果有到时的定时器,产生WM_TIMER,返回处理执行
- 如果没有到时的定时器,整理程序的资源、内存等
- GetMessage会继续等候下一条消息,PeekMessage会返回FALSE,交出程序的控制权
- 注意:GetMessage如果获取到的是WM_QUIT,函数会返回FALSE
八、键盘消息
1.键盘消息分类
- WM_KEYDOWN:按键被按下时产生
- WM_KEYUP:按键被放开时产生
- WM_SYSKEYDOWN:系统键按下时产生,比如ALT、F10
- WM_SYSKEYUP:系统键放开时产生
附带信息:WPARAM:按键的Virtual Key,LPARAM:按键的参数,例如按下次数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_KEYDOWN: { char szText[256] = { 0 }; sprintf (szText, "WM_KEYDOWN:键码值=%d\n" , wParam); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } case WM_KEYUP: { char szText[256] = { 0 }; sprintf (szText, "WM_KEYUP:键码值=%d\n" , wParam); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } |
2.字符消息(WM_CHAR)
- TranslateMessage在转换WM_KEYDOWN消息时,对于可见字符可以产生WM_CHAR,不可见字符无此消息
- 附带信息:WPARAM:输入的字符的ASCII字符编码值,LPARAM:按键的相关参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_CHAR: { char szText[256] = { 0 }; sprintf (szText, "WM_CHAR:键码值=%d\n" , wParam); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } |
字符消息工作原理:
1 2 3 4 5 6 7 8 9 10 11 12 | TranslateMessage(&nMsg) { if (nMsg.message != WM_KEYDOWN) return .. 根据nMsg.wParam(键码值)可以获知哪个按键被按下 if (不可见字符的按键) return ... 根据CapsLock(大写锁定键)是否处于打开状态 if (CapsLock打开) PostMessage(nMsg.hwnd, WM_CHAR, 65, ...) //大写字符的ASCII码 else PostMessage(nMsg.hwnd, WM_CHAR, 97, ...) //小写字符的ASCII码 } |
九、鼠标消息
1.鼠标基本消息
1 2 3 4 5 | WM_LBUTTONDOWN:鼠标左键按下 WM_LBUTTONUP:鼠标左键抬起 WM_RBUTTONDOWN:鼠标右键按下 WM_RBUTTONUP:鼠标右键抬起 WM_MOUSEMOVE:鼠标移动消息 |
附带信息 :
1 2 3 | wPARAM:其他按键的状态,例如Ctrl/Shift等 IPARAM:鼠标的位置,窗口客户区坐标系,LOWORD:X坐标位置,HIWORD:Y坐标位置 一般情况下,鼠标的按下/抬起成对出现,在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息 |
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_MOUSEMOVE: { char szText[256] = { 0 }; sprintf (szText, "WM_MOUSEMOVE:其他按键状态:%d, X=%d, Y=%d\n" , wParam, LOWORD(lParam), HIWORD(lParam)); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } case WM_LBUTTONDOWN: { char szText[256] = { 0 }; sprintf (szText, "WM_LBUTTONDOWN:其他按键状态:%d, X=%d, Y=%d\n" , wParam, LOWORD(lParam), HIWORD(lParam)); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } case WM_LBUTTONUP: { char szText[256] = { 0 }; sprintf (szText, "WM_LBUTTONUP:其他按键状态:%d, X=%d, Y=%d\n" , wParam, LOWORD(lParam), HIWORD(lParam)); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } |
2.鼠标双击消息:
1 2 | WM_LBUTTONDBLCLK:鼠标左键双击 WM_RBUTTONDBLCLK:鼠标右键双击 |
附带信息:
1 2 | wPARAM:其他按键的状态,例如Ctrl/Shift等 IPARAM:鼠标的位置,窗口客户区坐标系,LOWORD:X坐标位置,HIWORD:Y坐标位置 |
消息产生顺序:
1 2 3 4 5 6 | 以左键双击为例: WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBLCLK WM_LBUTTONUP 使用时需要在注册窗口类的时候添加CS_DBLCLKS风格 |
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | #include <Windows.h> #include <stdio.h> HANDLE g_hOutput = 0; //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_LBUTTONDBLCLK: { char szText[256] = { 0 }; sprintf (szText, " WM_LBUTTONDBLCLK:\n" ); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } case WM_LBUTTONDOWN: { char szText[256] = { 0 }; sprintf (szText, "WM_LBUTTONDOWN:其他按键状态:%d, X=%d, Y=%d\n" , wParam, LOWORD(lParam), HIWORD(lParam)); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } case WM_LBUTTONUP: { char szText[256] = { 0 }; sprintf (szText, "WM_LBUTTONUP:其他按键状态:%d, X=%d, Y=%d\n" , wParam, LOWORD(lParam), HIWORD(lParam)); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } //入口函数 int CALLBACK WinMain( HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) { AllocConsole(); g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE); //设计窗口类 WNDCLASS wc = { 0 }; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = ( HBRUSH )(COLOR_WINDOW + 1); wc.hCursor = NULL; wc.hIcon = NULL; wc.hInstance = hIns; wc.lpfnWndProc = WindProc; wc.lpszClassName = TEXT( "Main" ); wc.lpszMenuName = NULL; wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; //注册窗口类 RegisterClass(&wc); //将以上所有赋值全部写入操作系统 //在内存中创建窗口 HWND hWnd = CreateWindowEx(0, wc.lpszClassName, TEXT( "Window" ), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL); //显示窗口 ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); //消息循环 MSG nMsg = { 0 }; while ( true ) { if ((PeekMessage(&nMsg, NULL, 0, 0, PM_NOREMOVE))) { //有消息 if (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); DispatchMessage(&nMsg); } else { break ; } } } return 0; } |
3.鼠标滚轮消息
1 | WM_MOUSEWHEEL:鼠标滚轮消息 |
附带信息:
1 2 3 | wPARAM:LOWORD:其他按键的状态,HIWORD:滚轮的偏移量,通过正负值表示滚动方向,正:向前滚动,负:向后滚动 IPARAM:鼠标当前的位置,屏幕坐标系,LOWORD:X坐标,HIWORD:Y坐标 使用:通过偏移量,获取滚动的方向和距离 |
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_MOUSEWHEEL: { short nDelta = HIWORD(wParam); //偏移量 char szText[256] = { 0 }; sprintf (szText, "WM_MOUSEWHEEL:nDetal=%d\n" , nDelta); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); break ; } default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } |
十、定时器消息
在程序中创建定时器,当到底时间间隔时,定时器会向程序发送一个WM_TIMER消息。时器的精度是毫秒,但是准确度很低,例如设置时间间隔为1000ms,但是会在非1000毫秒到底消息。附带消息:wPARAM:定时器ID,IPARAM:定时器处理函数的指针
1.创建定时器
1 2 3 4 5 6 7 | UINT_PTR SetTimer( HWND hWnd, //定时器窗口句柄 UINT_PTR nIDEvent, //定时器ID UINT uElapse, //时间间隔 TIMERPPOC lpTimerFunc //定时器处理函数指针(一般不使用,为NULL) ); 定时器创建成功后,返回非0 |
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //窗口处理函数(自定义,处理消息) LRESULT CALLBACK WindProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) { switch (msgID) { case WM_CREATE: { SetTimer(hWnd, 1, 1000, NULL); SetTimer(hWnd, 2, 2000, NULL); break ; } case WM_TIMER: { char szText[256] = { 0 }; sprintf (szText, "WM_TIMER:定时器ID=%d\n" , wParam); WriteConsole(g_hOutput, szText, strlen (szText), NULL, NULL); KillTimer(hWnd, wParam); //销毁定时器 break ; } default : break ; } return DefWindowProc(hWnd, msgID, wParam, lParam); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了