热度 14
2012-12-2 22:00
1437 次阅读|
0 个评论
在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模块,并不进入队列 我不知道你是怎么试的 你可以运行下面的代码 C/C++ code ? ? 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只分发队列消息