Win32编程之消息机制(二)

一、Windows的消息概念

1.消息组成:

  • 窗口句柄
  • 消息ID
  • 消息的两个参数(两个附带信息)
  • 消息产生的时间
  • 消息产生时鼠标位置
typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
} MSG  

2.消息的作用

当系统通知窗口工作时,就采用消息的方式派发给窗口

二、消息循环

//消息循环
MSG nMsg = { 0 };
while (GetMessage(&nMsg, NULL, 0, 0)) {
	TranslateMessage(&nMsg);//将虚拟消息转换为字符消息
	DispatchMessage(&nMsg);//将消息分发给窗口处理函数
}

1.GetMessage():获取本进程的消息

WINUSERAPI BOOL WINAPI
GetMessageA(
    _Out_ LPMSG lpMsg,//存放获取到的消息BUFF
    _In_opt_ HWND hWnd,//窗口句柄
    _In_ UINT wMsgFilterMin,//获取消息的最小ID
    _In_ UINT wMsgFilterMax//获取消息的最大ID
);  

lpMsg:当获取到消息后,将消息的参数存放到MSG结构中

hWnd:获取到hWnd所指定窗口的消息

wMsgFilterMin和wMsgFilterMax:只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围

2.TranslateMessage():翻译消息,将按键消息翻译成字符消息

BOOL WINAPI
TranslateMessage(
    _In_ CONST MSG *lpMsg //要翻译的消息地址
);

检查消息是否有按键消息,如果不是按键消息,不做任何处理,继续执行

3.DispatchMessage()函数执行流程:

DispatchMessage(&nMsg) {
    nMsg.hwnd --> 通过窗口句柄找到保存窗口数据的内存 -->然后找到窗口消息处理函数
    WndProc(nMsg.hWnd, nMsg.msgID, nMsg.wParam, nMsg.lParam) {
       //执行消息处理函数  
    }    
}

 每个窗口都必须具有窗口处理函数:

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处理,退出消息循环

示例代码:

#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);
	
   //显示窗口 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.开始绘图
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
);

示例代码:

//消息循环
	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-投递消息,消息发出后立刻返回,不等厚消息执行结果

BOOL SendMessage/PostMessage(
        HWND hWnd,//消息发送的目的窗口
        UNIT Msg,//消息ID
        WPARAM wParam,//消息参数
        LPARAM lParam,//消息参数
);

五、消息的分类

  • 系统消息:ID范围(0-0x03FF),由系统定义好的消息,可以在程序中直接使用
  • 用户自定义消息:ID范围(0x0400-0x7FFF),由用户自己定义,满足用户自己的需求,由用户自己发出消息。并处理,自定义消息宏:WM_USER

1.用户自定义消息

#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).队列消息:消息的发送和获取,都是通过消息队列完成的,消息发送后,首先放入队列中,然后通过消息循环,从队列当中获取。     

GetMessage:从消息队列中获取消息
PostMessage:将消息投递到消息队列中
常见队列消息:WM_PAINT、键盘、鼠标、定时器等  

(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:按键的参数,例如按下次数

//窗口处理函数(自定义,处理消息)
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:按键的相关参数
//窗口处理函数(自定义,处理消息)
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);
}

字符消息工作原理:

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.鼠标基本消息

WM_LBUTTONDOWN:鼠标左键按下
WM_LBUTTONUP:鼠标左键抬起
WM_RBUTTONDOWN:鼠标右键按下
WM_RBUTTONUP:鼠标右键抬起
WM_MOUSEMOVE:鼠标移动消息  

附带信息 :

wPARAM:其他按键的状态,例如Ctrl/Shift等
IPARAM:鼠标的位置,窗口客户区坐标系,LOWORD:X坐标位置,HIWORD:Y坐标位置 
一般情况下,鼠标的按下/抬起成对出现,在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息 

 示例代码:

//窗口处理函数(自定义,处理消息)
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.鼠标双击消息:

WM_LBUTTONDBLCLK:鼠标左键双击
WM_RBUTTONDBLCLK:鼠标右键双击

附带信息:

wPARAM:其他按键的状态,例如Ctrl/Shift等
IPARAM:鼠标的位置,窗口客户区坐标系,LOWORD:X坐标位置,HIWORD:Y坐标位置 

消息产生顺序:  

以左键双击为例:
  WM_LBUTTONDOWN
  WM_LBUTTONUP
  WM_LBUTTONDBLCLK
  WM_LBUTTONUP
使用时需要在注册窗口类的时候添加CS_DBLCLKS风格

示例代码:

#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.鼠标滚轮消息

WM_MOUSEWHEEL:鼠标滚轮消息  

附带信息: 

wPARAM:LOWORD:其他按键的状态,HIWORD:滚轮的偏移量,通过正负值表示滚动方向,正:向前滚动,负:向后滚动
IPARAM:鼠标当前的位置,屏幕坐标系,LOWORD:X坐标,HIWORD:Y坐标
使用:通过偏移量,获取滚动的方向和距离

示例代码:

//窗口处理函数(自定义,处理消息)
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.创建定时器

UINT_PTR SetTimer(
	HWND hWnd,//定时器窗口句柄
	UINT_PTR nIDEvent,//定时器ID
	UINT uElapse,//时间间隔
	TIMERPPOC lpTimerFunc//定时器处理函数指针(一般不使用,为NULL)
);
定时器创建成功后,返回非0

示例代码:

//窗口处理函数(自定义,处理消息)
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);
}  

       

posted @ 2023-09-06 23:10  TechNomad  阅读(368)  评论(0)    收藏  举报