消息机制
上述的过程被称为消息机制
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
把用户事件转变为消息
MSG msg;
//消息的结构体组成
typedef struct tagMSG {
HWND hwnd; //窗口句柄
UINT message; //消息ID,到底是什么消息
WPARAM wParam; //消息辅助参数
LPARAM lParam; //消息辅助参数
DWORD time; //消息产生的时间
POINT pt; //消息产生是鼠标所在的位置
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
传递消息除了windows传递给应用程序之外,还需要应用程序主动去接。
windows为每一个正在运行的应用程序维护一个消息队列,所以应用程序只需要和自己对应的消息队列去接。
GetMessage()函数,从应用程序的消息队列去得到消息,该函数为阻塞函数,如果没有消息一直阻塞,有消息,又分两种情况
GetMessageW(
_Out_ LPMSG lpMsg, //收到的消息放入msg中
_In_opt_ HWND hWnd, //得到的消息是哪个窗口的,如果是给主窗口的,给nullptr
_In_ UINT wMsgFilterMin, //消息过滤,指第一个消息
_In_ UINT wMsgFilterMax); //消息过滤,指最后一个消息
//消息过滤给0表示任何消息都接收
MSG msg;
//实例演示
while (GetMessage(&msg, nullptr, 0, 0))
{
// Translate 翻译 Accelerator快捷键
//如果不是快捷键消息
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg); //翻译消息
DispatchMessage(&msg); //投递消息,窗口主程序将消息投递给消息处理函数
}
}
PeekMessage()函数,从应用程序的消息队列去得到消息,该函数为非阻塞函数,分两种情况
PeekMessage(
_Out_ LPMSG lpMsg, //收到的消息放入msg中
_In_opt_ HWND hWnd, //得到的消息是哪个窗口的,如果是给主窗口的,给nullptr
_In_ UINT wMsgFilterMin, //消息过滤,指第一个消息
_In_ UINT wMsgFilterMax, //消息过滤,指最后一个消息
_In_ UINT wRemoveMsg); //取完消息是否移除消息
MSG msg;
//实例演示
ZeroMemory(&msg,sizeof(msg));//内存清零,类似memset
while(msg,message != WM_QUIT)
{
//如果有消息 PM_REMOVE 表示移除消息 NO_REMOVE 表示不移除消息
if(PeekMessage(&msg, nullptr, 0, 0,PM_REMOVE))
{
//如果不是快捷键消息
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg); //翻译消息
DispatchMessage(&msg); //投递消息,窗口主程序将消息投递给消息处理函数
}
}
}
SendMessage()函数,投递消息函数
相当于插队,直接把消息发送给当前窗口的消息处理函数WndProc
SendMessage(
_In_ HWND hWnd, //投递给哪个窗口消息,如果是不同的窗口,就需要知道对应的窗口句柄
_In_ UINT Msg, //投递的是什么样的消息
_Pre_maybenull_ _Post_valid_ WPARAM wParam, //辅助信息
_Pre_maybenull_ _Post_valid_ LPARAM lParam);//辅助信息
//相当于插队,直接把消息发送给当前窗口的消息处理函数WndProc,优先处理SendMessage投递的消息。
case WM_KEYDOWN:
{
//投递消息
SendMessage(hWnd,WM_LBUTTONDOWN,0,0);
static int j=0;
//获取窗口dc
hdc=GetDC(hWnd);
TextOut(hdc,0,j,_T("按键按下"),4);
//释放DC
ReleaseDC(hWnd,hdc);
j+=20;
}
PostMessage()函数,投递消息函数
排队,把消息发送到当前窗口的消息处理队列,依次处理
PostMessage(
_In_opt_ HWND hWnd, //投递给哪个窗口消息,如果是不同的窗口,就需要知道对应的窗口句柄
_In_ UINT Msg, //投递的是什么样的消息
_In_ WPARAM wParam, //辅助信息
_In_ LPARAM lParam); //辅助信息
//排队,把消息发送到当前窗口的消息处理队列,依次处理,即当前消息处理完成后才会处理PostMessage投递的消息
case WM_KEYDOWN:
{
//投递消息
PostMessage(hWnd,WM_LBUTTONDOWN,0,0);
static int j=0;
//获取窗口dc
hdc=GetDC(hWnd);
TextOut(hdc,0,j,_T("按键按下"),4);
//释放DC
ReleaseDC(hWnd,hdc);
j+=20;
}
windows的消息处理函数
WndProc(HWND hWnd, //接受消息的窗口
UINT message, //接受到的消息
WPARAM wParam, //辅助信息
LPARAM lParam) //辅助信息
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
//命令消息,菜单项的id都会在这里
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
//点击退出
case IDM_EXIT:
//销毁窗口DestroyWindow(hWnd);
DestroyWindow(hWnd);
break;
default:
//默认的窗口处理函数DefWindowProc
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
//gdi绘制消息
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
//销毁消息
case WM_DESTROY:
PostQuitMessage(0);//post一个WM_QUIT消息
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
windows的消息,WM开头,以下介绍一些常见的中重要消息。
**WM_CREATE 创建消息 **
创建窗口消息,理解为构造,这个消息是窗口已经产生,在显示之前响应,这个消息由CreateWindow产生
//WndProc消息处理函数中
switch (message)
{
//命令消息,菜单项的id都会在这里
case WM_CREATE:
{
//通常在这做资源加载,或者数据的初始化
MessageBox(hWnd,_"我在窗口之前",0,0);
}
break;
}
**WM_DESTORY 销毁消息 **
销毁消息,理解为析构,可以由DestroyWindow(hWnd);函数来产生。
//WndProc消息处理函数中
switch (message)
{
//命令消息,菜单项的id都会在这里
case WM_CREATE:
{
//通常在这做资源加载,或者数据的初始化
MessageBox(hWnd,_"我在窗口之前",0,0);
}
break;
//销毁消息
case WM_DESTORY:
{
//通常在这做资源加载,或者数据的初始化
MessageBox(hWnd,_"我在窗口之前",0,0);
}
break;
}
WM_ACTIVATE 激活消息
窗口激活消息
//WndProc消息处理函数中
switch (message)
{
//命令消息,菜单项的id都会在这里
case WM_CREATE:
{
//通常在这做资源加载,或者数据的初始化
MessageBox(hWnd,_T("我在窗口之前"),0,0);
}
break;
//销毁消息
case WM_DESTORY:
{
//通常在这做资源释放
MessageBox(hWnd,_T("你将关闭窗口"),0,0);
}
break;
case WM_ACTIVATE:
{
//窗口没激活之前,可以用此消息减少当前窗口的资源占用
switch(wParam) //匹配wParam辅助消息
{
case WA_CLICKACTIVE: //鼠标激活
break;
case WA_ACTIVE: //非鼠标激活
break;
case WA_INACTIVE: //取消激活,窗口不处于激活状态
break;
}
}
break;
}
WM_KEYDOWN 按键按下消息
WM_KEYUP 按键抬起消息
按键的值 虚拟键值,存放在wParam中,无符号的int;
在ASCII码表中就有的值就不再额外设定虚拟键值,会依照ASCII码表,不存在的才有虚拟键值;
虚拟键值都是以VK开头
VK_KEYDOWN响应的只是按键按下消息,如果有上档键Shift是不会响应的,只响应大写的字符
WM_CHAR 字符消息
主消息循环中,翻译消息
TranslateMessage(&msg);
如果有WM_KEYDOWN消息进入,会post出一个WM_CHAR消息
字符消息可以响应大小写字母以及上档键shift
当按键连续按下的时候,window会做一个判断是否是连续按键
//WndProc消息处理函数中
switch (message)
{
//字符消息
case WM_CHAR:
{
//字符的消息,也存放在wParam中,无符号的int
switch(wParam)
{
case 'a':
MessageBox(hWnd,_T("a键按下"),0,0);
break;
//通过shift来输入大写
case 'B':
MessageBox(hWnd,_T("shift+B键按下"),0,0);
break;
//特殊字符的获取
case '*':
MessageBox(hWnd,_T("shift+8键按下"),0,0);
break;
}
}
//按键按下
case WM_KEYDOWN:
{
//按键的值 虚拟键值,存放在wParam中,无符号的int
switch(wParam)
{
case VK_LEFT:
{
MessageBox(hWnd,_T("方向键左键按下"),0,0);
}
break;
case VK_F1:
MessageBox(hWnd,_T("F1键按下"),0,0);
break;
//匹配ASCII码表中的字符 //只响应大写的字符
case 'A':
{
MessageBox(hWnd,_T("A键按下"),0,0);
}
break;
//响应的只是按键按下消息,如果有上档键Shift是不会响应的
}
}
break;
//按键抬起,同KEYDOWN,只有按键按下才会有按键抬起消息
case WM_KEYUP:
{
}
break;
}
WM_SYSKEYDOWN 系统按键
WM_SYSKEYUP 系统按键
//WndProc消息处理函数中
switch (message)
{
//系统按键按下,WM_SYSKEYUP系统按键抬起同按下消息
case WM_SYSKEYDOWN:
{
//系统按键的消息,也存放在wParam中,无符号的int
switch(wParam)
{
//alt键按下
case VK_MENU:
MessageBox(hWnd,_T("alt键按下"),0,0);
break;
//F10键按下
case VK_F10:
MessageBox(hWnd,_T("F10键按下"),0,0);
break;
//组合按键的获取
case 'S':
MessageBox(hWnd,_T("alt+S键按下"),0,0);
break;
}
}
}
WM_SYSCOMMAND 系统命令消息
WM_COMMAND 系统命令消息
//WndProc消息处理函数中
switch (message)
{
//系统命令消息 //拦截了系统的消息
case WM_SYSCOMMAND:
{
//系统命令的消息,也存放在wParam中,无符号的int
switch(wParam)
{
//拦截系统关闭的消息
case SC_CLOSE:
MessageBox(hWnd,_T("系统消息被拦截"),0,0);
break;
}
}
}
WM_MOUSEFIRST
WM_MOUSEMOVE 鼠标移动
WM_LBUTTONDOWN 鼠标左键按下
WM_LBUTTONUP 鼠标左键抬起
WM_LBUTTONDBLCLK 鼠标左键双击
WM_RBUTTONDOWN 鼠标右键按下
WM_RBUTTONUP 鼠标右键抬起
WM_RBUTTONDBLCLK 鼠标右键双击
WM_MBUTTONDOWN 鼠标中键按下
WM_MBUTTONUP 鼠标中键抬起
WM_MBUTTONDBLCLK 鼠标中键双击
鼠标的坐标信息都保存在lParam中
//WndProc消息处理函数中
switch (message)
{
//鼠标的左键按下
case WM_LBUTTONDOWN:
{
//x取低位2字节
int x = LOWORD(lParam);
//y取高两位2字节
int y = HIWORD(lParam);
TCHAR arr[128] = {};
_stprintf_s(arr,128,_T("X=%d Y=%d"),x,y);
hdc = GetDC(hWnd);
//鼠标位置信息窗口0,0位置输出
TextOut(hdc,0,0,arr,_tcslen(arr));
ReleaseDC(hWnd,hdc);
}
break;
//鼠标移动消息
case WM_MOUSEMOVE:
{
//x取低位2字节
int x = LOWORD(lParam);
//y取高两位2字节
int y = HIWORD(lParam);
TCHAR arr[128] = {};
_stprintf_s(arr,128,_T("X=%d Y=%d"),x,y);
hdc = GetDC(hWnd);
//鼠标位置信息窗口0,0位置输出
TextOut(hdc,0,0,arr,_tcslen(arr));
ReleaseDC(hWnd,hdc);
}
break;
}
一个简单的绘制实例
//全局变量
bool isDown = false;
int BeginX;
int BeginY;
//WndProc消息处理函数中
switch (message)
{
//鼠标的左键按下
case WM_LBUTTONDOWN:
{
//获取鼠标的位置
BeginX = LOWORD(lParam);
BeginY = HIWORD(lParam);
isDown = true;
}
break;
//鼠标的左键抬起
case WM_LBUTTONUP:
{
isDown = false;
}
break;
//鼠标移动消息
case WM_MOUSEMOVE:
{
if(isDown)
{
//移动时获取鼠标的xy信息
int x = LOWORD(lParam);
int y = HIWORD(lParam);
hdc = GetDC(hWnd);
//划线的时候把起点移动到某个位置
MoveToEx(hdc,BeginX,BeginY,nullptr);
//划线到某个位置
LineTo(hdc,x,y);
ReleaseDC(hWnd,hdc);
//再次更新起始的鼠标x和y的信息
BeginX = x;
BeginY = y;
}
}
break;
}
WM_MOUSEWHEEL 鼠标滚轮的滚动
鼠标滚轮滚动的信息放在wParam中
//WndProc消息处理函数中
switch (message)
{
//鼠标滚轮滚动
case WM_MOUSEWHEEL:
{
int loVal = LOWORD(wParam);
//确定鼠标滚轮滚动的信息放在wParam的高位中,通过hiVal的正负,可以判断鼠标是朝前滚动还是朝后滚动,正数朝前滚动,复数朝后滚动
short hiVal = HIWORD(wParam);
}
break;
}
WM_TIMER 计时消息
窗口默认不戴计时器,需要主动的设置SetTimer(hWnd,1001,1000,nullprt);
使用完计时器需要手动销毁KillTimer(hWnd,1001);
//设置计时器
SetTimer(
_In_opt_ HWND hWnd, //哪个窗口开启计时器
_In_ UINT_PTR nIDEvent, //计时器的id
_In_ UINT uElapse, //计时器的时长,毫秒为单位
_In_opt_ TIMERPROC lpTimerFunc);//时间回调函数,如果为null响应WM_TIMER消息,如果不为空,将直接调用该函数
//销毁计时器
KillTimer(
_In_opt_ HWND hWnd, //计时器所在窗口
_In_ UINT_PTR uIDEvent); //计时器的id
//计时器回调函数
void my_time(HWND hwnd)
{
HDC hdc = GetDC(hwnd);
static int j = 0;
//x为400的位置输出
TextOut(hdc,400,j,_("timer_3 id=1003"),7);
ReleaseDC(hwnd,hdc);
j+=20;
}
//WndProc消息处理函数中
switch (message)
{
//窗口创建时启动计时器
case WM_CREATE:
{
my_time(hWnd);
SetTimer(hWnd,1001,1000,nullprt); //第一个计时器
SetTimer(hWnd,1002,2000,nullprt); //第二个计时器
SetTimer(hWnd,1002,3000,(TIMERPROC)my_time); //第三个计时器,通过回调函数来开启计时器,无需WM_TIMER来响应
}
break;
//开启计时器
case WM_TIMER:
{
switch(wParam)
{
//第一个计时器
case 1001:
{
static int j = 0;
hdc=GetDC(hWnd);
//x为0的位置输出
TextOut(hdc,0,j,_("timer_1 id=1001"),7);
ReleaseDC(hWnd,hdc);
j+=20;
}
break;
//第二个计时器
case 1002:
{
static int j = 0;
hdc=GetDC(hWnd);
//x为200的位置输出
TextOut(hdc,200,j,_("timer_2 id=1001"),7);
ReleaseDC(hWnd,hdc);
j+=20;
}
break;
}
}
break;
//关闭窗口时候销毁计时器
case WQ_QUIT:
{
//销毁第一个计时器
KillTimer(hWnd,1001);
//销毁第二个计时器
KillTimer(hWnd,1002);
//销毁第二个计时器
KillTimer(hWnd,1003);
}
break;
}