在win32程序的消息循环函数中
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
DispatchMessag()会调用WndProc()窗口过程函数,直到WndProc()处理完后DispatchMessag()才返回,继续消息循环检索下一条消息,在此之前while()消息循环会停顿掉
在WndProc()函数处理过程中如果又产生了另一个消息B,则必须等这个消息B处理完后wndProc()函数才会返回,而这个消息B也是要由WndProc()函数处理的,即在WndProc()调用的过程中系统接受到消息B后嵌套调用了WndProc(),但此时while消息循环已经停顿了
根据试验,在WndProc()处理过程中发出消息B后,系统通过某种方式跳过while()消息循环获取了消息B,并且又调用了一个WndProc()来处理消息B,等消息B处理完后第二个WndProc()才返回,然后第一个WndProc()继续执行,等第一个WndProc()执行完毕后DispatchMessage()才会返回,然后继续下一轮while()循环
那么,系统是怎样接受到这个消息B的呢?系统是怎样绕过while消息循环获取消息B并且调用第二个WndProc()的,这中间具体发生了哪些事情?网上和书上都没找到详细的处理过程……
请记住这关键的一点:
WndProc()是可重入的,就是说:
WndProc()函数虽然没结束,但一个
API调用(SendMessage()等)会立即
再次调用WndProc()。
明白这一点对于理解windows的消息循环
很重要
getmessage取完之后就返回,peekmessage会挂着直到结束
SendMessage是直接发送到窗口过程的,借助系统的User32模块,并不进入队列
我不知道你是怎么试的
你可以运行下面的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
#include #include #include #define msgv (WM_USER+123) LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case msgv: puts ( "enter msgv" ); PostMessageW(hwnd,msgv+1,0,0); //SendMessageW(hwnd,msgv+1,0,0); puts ( "leave msgv" ); return 0; case msgv+1: puts ( "enter msgv+1" ); puts ( "leave msgv+1" ); return 0; case WM_CLOSE:DestroyWindow(hwnd); return 0; case WM_DESTROY:PostQuitMessage(0); return 0; default : return DefWindowProcW(hwnd,uMsg,wParam,lParam); } } int main() { static WNDCLASS.EXW WindowClass={ sizeof (WNDCLASS.EXW),0,WindowProc,0,0,0,0,0,0,0,L "aa" ,0};MSG msg; HWND hwnd; WindowClass.hInstance=GetModuleHandleW(0); hwnd=CreateWindowExW(0,( wchar_t *)(unsigned long )RegisterClass.ExW(&WindowClass),0,WS_VISIBLE|WS_OVERLAPPEDWINDOW,200,200,200,200,0,0,0,0); PostMessageW(hwnd,msgv,0,0); while (GetMessageW(&msg,0,0,0))DispatchMessageW(&msg); return 0; } |
PostMessage时输出是
enter msgv
leave msgv
enter msgv+1
leave msgv+1
SendMessage时输出是
enter msgv
enter msgv+1
leave msgv+1
leave msgv
1、在win32程序的while消息循环中,DispatchMessag()会调用WndProc()窗口过程函数,直到WndProc()处理完后DispatchMessag()才返回,继续消息循环检索下一条消息,在这之前while()消息循环会停顿掉
2、在WndProc()函数处理过程中又产生了另一个消息B,如果消息B是由PostMessage()发送的,则消息B会发送到消息队列中,然后WndProc()继续执行,执行完毕后返回到DispatchMessag()函数,继续消息循环检索下一条消息,检索到消息B后才开始执行消息B,
3、在WndProc()函数处理过程中又产生了另一个消息B,如果消息B是由SendMessage()发送的,则必须等这个消息B处理完后wndProc()函数才会返回,这时SendMessage()会调用 SendMessageWorker(),SendMessageWorker()判断目标窗口的线程是不是当前线程,如果不是就进入内核调用NtUserMessageCall(),否则就在用户模式调用窗口过程函数WndProc()
虽然此时while()消息循环已经停顿了,但在WndProc()调用的过程中通过SendMessageWorker()间接嵌套调用了WndProc()
测试代码如下:
#include
#define MSG_A (WM_USER+123)
#define MSG_B (WM_USER+321)
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("Hello Windows");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if(!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("Register Class Failed !"), "Error", MB_ICONERROR);
return 0;
}
hwnd = CreateWindow( szAppName,
TEXT("Hello Windows"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
static int i;
switch(message)
{
case WM_LBUTTONDOWN:
i = 0;
hdc=GetDC(hwnd);
TextOut(hdc,100, 100 + (i++)*20, TEXT("Use SendMessage( ) Result :"),strlen(TEXT("Use PostMessage( ) Result :")));
i++;
TextOut(hdc,100, 100 + (i++)*20, TEXT("LBUTTONDOWN Start"),strlen(TEXT("LBUTTONDOWN Start")));
SendMessage(hwnd, MSG_A, 0, 0);
TextOut(hdc,100, 100 + (i++)*20, TEXT("LBUTTONDOWN Over"),strlen(TEXT("LBUTTONDOWN Over")));
ReleaseDC(hwnd,hdc);
return 0;
case MSG_A:
hdc=GetDC(hwnd);
TextOut(hdc,100, 100 + (i++)*20, TEXT("MSG_A Start"),strlen(TEXT("MSG_A Start")));
SendMessage(hwnd, MSG_B, 0, 0);
TextOut(hdc,100, 100 + (i++)*20, TEXT("MSG_A Over"),strlen(TEXT("MSG_A Over")));
ReleaseDC(hwnd,hdc);
return 0;
case MSG_B:
hdc=GetDC(hwnd);
TextOut(hdc,100, 100 + (i++)*20, TEXT("MSG_B Start"),strlen(TEXT("MSG_B Start")));
TextOut(hdc,100, 100 + (i++)*20, TEXT("MSG_B Over"),strlen(TEXT("MSG_B Over")));
ReleaseDC(hwnd,hdc);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
测试结果一:
Use SendMessage() Result :
LBUTTONDOWN Start
MSG_A Start
MSG_B Start
MSG_B Over
MSG_A Over
LBUTTONDOWN Over
测试结果二:
Use PostMessage() Result :
LBUTTONDOWN Start
LBUTTONDOWN Over
MSG_A Over
MSG_A Start
MSG_B Start
MSG_B Over
DispatchMessage只分发队列消息
文章评论(0条评论)
登录后参与讨论