我试图了解Windows应用程序的工作原理。
有一个WndProc函数,其中发生了消息处理。
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) {
if (MessageBox(0, L"Are you sure?", L"Exit?", MB_YESNO | MB_ICONQUESTION) == IDYES)
//Release the windows allocated memory
DestroyWindow(hwnd);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
可以在两种情况下调用此函数:
A)在消息循环周期中由DispatchMessage(& msg)函数调用:
while (true){
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
B)在收到非排队消息时由Windows调用。
这是如何工作的? Windows如何在不使用并行性的情况下立即调用WndProc函数?能否请详细描述函数调用的机制?
官方MSDN文档说:
非排队消息立即发送到目标窗口过程,绕过系统消息队列和线程消息队列。系统通常发送非排队消息以通知窗口影响它的事件。例如,当用户激活新的应用程序窗口时,系统会向窗口发送一系列消息,包括WM_ACTIVATE,WM_SETFOCUS和WM_SETCURSOR。这些消息通知窗口它已被激活,键盘输入被定向到窗口,并且鼠标光标已在窗口的边界内移动。当应用程序调用某些系统函数时,也会产生非排队消息。例如,系统在应用程序使用SetWindowPos函数移动窗口后发送WM_WINDOWPOSCHANGED消息。
事实证明,非排队消息仅在窗口初始化期间出现,并且所有后续的非排队消息只能是在我的程序中调用WinAPI函数的结果?
答案 0 :(得分:4)
没有什么特别神奇的,如果从创建窗口的同一线程调用SendMessage,则SendMessage直接调用窗口过程,否则请求排队,SendMessage()等待消息循环处理请求。这是记录在案的行为:
如果指定的窗口是由调用线程创建的,则窗口过程将作为子例程立即调用。如果指定的窗口是由其他线程创建的,则系统会切换到该线程并调用相应的窗口过程。 线程之间发送的消息仅在接收线程执行消息检索代码时处理。发送线程被阻塞,直到接收线程处理消息。
调度传入的已发送邮件,检查线程邮件队列中是否有已发布的邮件,并检索邮件(如果存在)。
...
在此调用期间,系统会使用SendMessage,SendMessageCallback,SendMessageTimeout或SendNotifyMessage函数发送待处理的非排队消息,即发送到调用线程拥有的窗口的消息。然后检索与指定过滤器匹配的第一个排队消息。系统也可以处理内部事件。
从调用线程的消息队列中检索消息。 该函数调度传入的已发送消息,直到发布的消息可供检索。
...
在此调用期间,系统会使用SendMessage,SendMessageCallback,SendMessageTimeout或SendNotifyMessage函数发送待处理的非排队消息,即发送到调用线程拥有的窗口的消息。然后检索与指定过滤器匹配的第一个排队消息。系统还可以处理内部事件。
唯一的魔力在于某些消息并非“真正”排队,但如果没有更好的事情可以通过GetMessage合成(=>重绘,鼠标移动消息,定时器等)。
不涉及并行性,窗口过程的优点在于它们总是从创建窗口的线程中调用。