在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()的,这中间具体发生了哪些事情?网上和书上都没找到详细的处理过程……
我想应该还是跟消息队列有关,不知道系统在这种情况下怎样处理消息队列中的消息的?求大神们解答  ,谢谢啦

解决方案 »

  1.   

    每个窗口、线程都有自己的消息队列,消息是投递到消息队列里的
    while (GetMessage (&msg, NULL, 0, 0))
      {  
      TranslateMessage (&msg) ;  
      DispatchMessage (&msg) ;  
      }
    GetMessage是从消息队列里获取消息,因此,消息传过来的时候,是不占用这个循环的
      

  2.   

    谢谢解答!可是在DispatchMessage (&msg)函数中调用wndproc()窗口过程时,while ()消息循环是停顿掉的,发出消息B后,这时系统是跳过while()消息循环直接到消息队列里取的消息吗?是怎样调用第二个wndproc()窗口过程的?用到了哪些函数呢?
      

  3.   

    SendMessage 的话相当于直接调用 WndProc
      

  4.   

    不是所有消息都会进消息队列,系统会有额外处理,对于SendMessage,类似于直接调用WinProc来实现.
      

  5.   

    谢谢了,我是担心SendMssage()可能会影响测试结果,所以用PostMessage()发送的消息
      

  6.   

    PostMessage的话 消息进消息队列下次取消息的时候才会取到消息。
      

  7.   


    不是,虽然PostMessage只是投递消息,但是在WndProc窗口过程函数中必须处理完这个新消息后才继续执行WndProc()函数是可重入的,就是说:WndProc()函数虽然没结束,但一个API调用(SendMessage()、PostMessage等)会再次调用WndProc()所以我想知道系统这时是怎样再次调用WndProc()窗口过程函数的
      

  8.   

    SendMessage给自己发才会先处理完这个新消息后才继续执行SendMessageW->SendMessageWorker
    SendMessageWorker判断目标窗口的线程是不是当前线程,如果不是就进入内核调用NtUserMessageCall
    否则就在用户模式调用窗口过程
      

  9.   

    PostMessage的话只是把消息放到消息队列中,下次GetMessage的时候才取到。
    SendMessage( hwnd , WM_MESS , w , l );内部调用WndProc( hwnd , WM_MESS , w , l );你可以下个断点看看调用栈。
      

  10.   

    谢谢各位的解答,在此特别感谢 Lactoferrin qman007 等,感谢你们的解答
    由于之前测试消息的选择和测试方式的错误,导致我测试的结果不可信,我重新进行了测试,根据测试结果,对于WinProc消息循环的机制我是这样理解的:
    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()如果有理解不对的地方,还请多多指教
    测试结果如下:Use SendMessage()  Result :LBUTTONDOWN Start
    MSG_A Start
    MSG_B Start
    MSG_B Over
    MSG_A Over
    LBUTTONDOWN Over
    Use SendMessage()  Result :LBUTTONDOWN Start
    LBUTTONDOWN Over
    MSG_A Over
    MSG_A Start
    MSG_B Start
    MSG_B Over测试代码如下,测试时注意把PostMessage和SendMessage互换下 #include <windows.h>#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.cbClsExtra    = 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);
    }
      

  11.   

    不好意思,上面测试结果打错了,CSDN没法修改,又不能直接贴图,手工输入的测试结果一: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