参考Using Messages and Message Queues - Win32 apps | Microsoft Learn
creating message loop
系统不会自动为每个线程都创建消息队列,而是只是为需要消息队列的线程创建。如果一个线程创建了一个或多个窗口,则线程必须要有message loop,此message loop 从各线程的消息队列中获取消息并将消息分配正确地window procedure。
HINSTANCE hinst;
HWND hwndMain;
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
WNDCLASS wc;
UNREFERENCED_PARAMETER(lpszCmdLine);
// Register the window class for the main window.
if (!hPrevInstance)
{
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL,
IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL,
IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MainWndClass";
if (!RegisterClass(&wc))
return FALSE;
}
hinst = hInstance; // save instance handle
// Create the main window.
hwndMain = CreateWindow("MainWndClass", "Sample",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL,
(HMENU) NULL, hinst, (LPVOID) NULL);
// If the main window cannot be created, terminate
// the application.
if (!hwndMain)
return FALSE;
// Show the window and paint its contents.
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
// Start the message loop.
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Return the exit code to the system.
return msg.wParam;
}
Examining a message queue
应用程序偶尔需要在message loop 外查看message queue中内容。例如,一个应用程序的window procedure正在执行一个耗时的绘图任务,你希望用户能打断此任务。除非应用程序在此绘图任务执行期间周期性的检测message queue 中的鼠标、键盘相关message,否则,在此绘图任务期间,应用是不会对键盘、鼠标进行响应。原因是,message loop中DispatchMessage函数在window procedure返回之前是不会返回的。
应用可以在一个耗时的操作执行期间使用PeekMessage来检测消息队列的情况。PeekMessage与GetMessage的功能类似,都是检测消息队列中的消息是否符合filter设置的条件,如果有就将消息复制到MSG结构。两者的最大不同是,只有在消息队列中找到符合条件的消息,GetMessage才会返回,而PeekMessage不管消息队列中有没有符合条件的消息就直接返回。
HWND hwnd;
BOOL fDone;
MSG msg;
// Begin the operation and continue until it is complete
// or until the user clicks the mouse or presses a key.
fDone = FALSE;
while (!fDone)
{
fDone = DoLengthyOperation(); // application-defined function
// Remove any messages that may be in the queue. If the
// queue contains any mouse or keyboard
// messages, end the operation.
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
switch(msg.message)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
//
// Perform any required cleanup.
//
fDone = TRUE;
}
}
}
还有其他函数能够对消息队列中的内容进行检测。例如GetQueueState、GetInputState。GetQueueState返回一个flag数组,这些flag用以说明消息队列中消息的类型。当消息队列中有鼠标、键盘消息,则GetInputState会返回TRUE。
Posting a Message
PostMessage函数将消息放入线程的消息队列的队尾后便立即返回,而不等待线程对消息做处理。此函数的参数包括 窗口handle, 消息id和两个消息参数。 系统将这几个参数值拷贝到MSG结构里,然后填充time和位置信息消息,之后将此结构放入消息队列。应用 可以调用PostThreadMessage函数来将消息post到特定的线程消息队列中。
sending a messsage
SendMessage 用于将消息直接发送到window procedure, 它会调用一个 window procedure,并等待window procedure 处理完消息并返回。