文章目录
1.消息循环的原理
1. 消息循环的阻塞
· GetMessage - 从系统获取消息,将消息从系统中移除,阻塞函数。当系统无消息时,会等候下一个消息。
· PeekMessage - 以查看的方式从系统获取消息,可以不将消息从系统移除,非阻塞函数。当系统无消息时,返回FALSE,继续执行后续代码。
BOOL PeekMessage(
LPMSG lpMsg,//message information
HWND hWnd,//handle to window
UINT wMsgFilterMin,//first message
UINT wMsgFilterMax,//last message
UINT wRemoveMsg//移除标识
PM_REMOVE/PM_NOREMOVE
)
2. 发送消息
· SendMessage - 发送消息,会等候消息处理的结果
· PostMessage - 投递消息,消息发出后立刻返回,不等候消息的执行结果。
BOOL SendMessage/PostMessage(
HWND hWnd,//消息发送的目的窗口
UINT Msg,//消息ID
WPARAM wParam,//消息参数
LPARAM lParam//消息参数
)
SendMessage会阻塞,PostMessage不会阻塞
3.消息分类
系统消息 - ID范围 0 - 0x03FF
由系统定义好的消息,可以在程序中直接使用。
用户自定义消息 - ID范围 0x0400 - 0x7FFF(31743)
由用户自己定义,满足用户自己的需求,由用户自己发出消息并响应处理。
自定义消息宏:WM_USER
#define MY_MESS WM_USER + 100
可以尝试,将自己定义的消息放入在窗口创建时发送出来:
case WM_CREATE:
MyCreate(hwnd);
break;
void MyCreate(HWND hwnd)
{
PostMessage(hwnd, MY_MESS, 1, 2);
}
看下是否能抓住我们自己发送的消息:
case MY_MESS:
My_drive(hwnd, wParam, lParam);
break;
void My_drive(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
wchar_t text[256] = { 0 };
wsprintf(text, L"WPARAM的值为%d,LPARAM的值为%d", wParam, lParam);
MessageBox(hwnd, text,L"infor",MB_OK);
}
全部代码:
#include <windows.h>
#include <stdio.h>
#define MY_MESS WM_USER + 100
HANDLE g_out = 0;
void onsize(HWND hwnd,LPARAM lParam)
{
short nwidth = LOWORD(lParam);
short nhight = HIWORD(lParam);
wchar_t szText[256] = { 0 };
wsprintf(szText, L"宽度%d,高度%d", nwidth, nhight);
WriteConsole(g_out, szText, wcslen(szText), NULL, NULL);
}
void MyCreate(HWND hwnd)
{
PostMessage(hwnd, MY_MESS, 1, 2);
}
void My_drive(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
wchar_t text[256] = { 0 };
wsprintf(text, L"WPARAM的值为%d,LPARAM的值为%d", wParam, lParam);
MessageBox(hwnd, text,L"infor",MB_OK);
}
LRESULT CALLBACK test(HWND hwnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
switch (msgID)
{
case WM_SIZE:
onsize(hwnd, lParam);
break;
case WM_CREATE:
MyCreate(hwnd);
break;
case MY_MESS:
My_drive(hwnd, wParam, lParam);
break;
case WM_SYSCOMMAND:
if (wParam == SC_CLOSE)
{
if (MessageBox(hwnd, L"是否关闭?", L"infor", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK)
{
PostQuitMessage(0);
}
else
{
return 0;
}
}
break;
}
return DefWindowProc(hwnd, msgID, wParam, lParam);
}
int CALLBACK wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
AllocConsole();//增加dos窗口
g_out = 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 = hInstance;//实例句柄
wc.lpfnWndProc = test;//窗口处理函数
wc.lpszClassName = (LPCWSTR)L"main";//窗口类名称
wc.lpszMenuName = NULL;//不要菜单
wc.style = CS_HREDRAW | CS_VREDRAW;//窗口水平或竖直方向有变化就重绘窗口
RegisterClass(&wc);//将以上所有赋值写入操作系统
//在内存中创建窗口
HWND hwnd = CreateWindow((LPCWSTR)L"main", (LPCWSTR)L"hello world", WS_OVERLAPPEDWINDOW, 100,100, 500, 500, NULL, NULL, hInstance, NULL);
//显示窗口
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
//消息循环
MSG nMSG = { 0 };
while (GetMessage(&nMSG,NULL,0,0))
{
TranslateMessage(&nMSG);
DispatchMessage(&nMSG);//派发消息,将消息交给窗口处理函数处理
}
return 0;
}
运行结果:
4.消息队列
1.消息队列的概念
· 消息队列是用于存放消息的队列
· 消息在队列中先入先出
· 所有窗口程序都具有消息队列
· 程序可以从队列中获取消息
2.消息队列的分类
· 系统消息队列 - 由系统维护的消息队列,存放系统产生的消息,例如鼠标、键盘等。
· 程序消息队列 - 属于每一个应用程序(线程)的消息队列。由应用程序(线程)维护。
GetMessage只抓本进程的消息
系统消息队列会每隔一段时间更具窗口句柄,将消息派发到各个进程中去。
3.消息和消息队列的关系
例子:
a. 当鼠标、键盘产生消息时,会将消息存放到系统消息队列
b. 系统会根据存放的消息,找到对应程序的消息队列
c. 将消息投递到程序的消息队列中
根据消息和消息队列之间使用关系,将消息分为俩类:
队列消息 - 消息的发送和获取,都是通过消息队列完成
非队列消息 - 消息的发送和获取,是直接调用消息的窗口处理完成。消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用处理函数,完成消息。
例:SendMessage - 直接将消息发送给窗口处理函数,并等候处理结果。
常见消息:WM_CREATE、WM_SIZE等。
3.GetMessage函数
· 在程序(线程)消息队列查找消息,如果队列有消息,检查消息是否满足指定条件(HWND,ID范围),不满足条件就不会取出消息,否则从队列取出消息返回
· 如果程序(线程)消息队列没有消息,向系统消息队列获取属于本程序的消息。如果系统队列的当前消息属于本程序,系统会将消息转发到程序的消息队列中。
· 如果系统消息队列也没有消息,检查当前进程的所有窗口的需要重写绘制的区域,如果发现有需要绘制区域,产生WM_PAINT消息,取得消息返回处理。
· 如果没有重新绘制区域,检查定时器如果有到时的定时器,产生WM_TIMER,返回处理执行
· 如果没有到时的定时器,整理程序资源、内存等等。
· GetMessage会继续等候下一条消息。PeekMessage会返回FALSE,交出程序的控制权。
· 注意:GetMessage如果获取到的是WM_QUIT,函数会返回FALSE
5.WM_PAINT
产生时间:当前窗口需要绘制的时候,且GetMessage没有消息可抓时
附带信息:wParam : 0;lParam : 0
专职用法:用于绘图
//窗口无效区域:需要重新绘制的区域
BOOL InvalidateRect(
HWND hWnd,//窗口句柄
CONST RECT* lpRect,//区域的矩形坐标
BOOL bErase//重绘前是否先擦除
);
消息处理步骤:
· 开始绘图
HDC BeginPaint(
HWND hwnd,//绘图窗口
LPPAINTSTRUCT lpPaint//绘图参数的BUFF
)//返回绘图设备句柄HDC
· 正式绘图
· 结束绘图
BOOL EndPaint(
HWND hWnd,
CONST PAINTSTRUCT *lpPaint)
//绘图参数的指针BeginPaint返回