13

为什么如果我MessageBox()在消息循环中调用看似同步的 Windows 函数,循环本身不会像我调用Sleep()(或类似的函数)一样冻结?为了说明我的观点,请使用以下骨骼WndProc

int counter = 0;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CREATE:
             SetTimer(hwnd, 1, 1000, NULL); //start a 1 second timer
             break;
        case WM_PAINT:
             // paint/display counter variable onto window
             break;
        case WM_TIMER: //occurs every second
             counter++;
             InvalidateRect(hwnd, NULL, TRUE); //force window to repaint itself
             break; 
        case WM_LBUTTONDOWN: //someone clicks the window
             MessageBox(hwnd, "", "", 0);
             MessageBeep(MB_OK); //play a sound after MessageBox returns
             break;
        //default ....
    }
    return 0;
}

在上面的例子中,程序的主要功能是运行一个定时器并每秒显示一次计数器的值。但是,如果用户单击我们的窗口,程序会显示一个消息框,然后在该框关闭后发出哔哔声。

这里是有趣的地方:我们可以看出MessageBox()它是一个同步函数,因为MessageBeep()在消息框关闭之前不会执行。但是,计时器会继续运行,并且即使在显示消息框时也会每秒重新绘制一次窗口。因此,虽然MessageBox()显然是一个阻塞函数调用,但仍然可以处理其他消息( WM_TIMER/ )。WM_PAINT没关系,除非我将 MessageBox 替换为另一个阻塞调用,例如Sleep()

    case WM_LBUTTONDOWN:
         Sleep(10000); //wait 10 seconds
         MessageBeep(MB_OK);
         break;

这完全阻止了我的应用程序,并且在 10 秒内没有发生任何消息处理(WM_TIMER/WM_PAINT未处理,计数器未更新,程序“冻结”等)。那么为什么MessageBox()允许消息处理继续而Sleep()不允许呢?鉴于我的应用程序是单线程的,它MessageBox()允许这个功能是什么?系统是否“复制”了我的应用程序线程,这样它就可以在完成WM_LBUTTONDOWN代码后完成代码MessageBox(),同时仍然允许原始线程在此期间处理其他消息?(那是我没有受过教育的猜测)

提前致谢

4

2 回答 2

11

The MessageBox() and similar Windows API functions are not blocking the execution, like an IO operation or mutexing would do. The MessageBox() function creates a dialog box usually with an OK button - so you'd expect automatic handling of the window messages related to the message box. This is implemented with its own message loop: no new thread is created, but your application remains responsive, because selected messages (like for painting) are handled calling recursively your WndProc() function, while other messages are not transmitted, because of the modal type of the created window.

Sleep() and other functions (when called directly from your WndProc() handling a window message) would actually block the execution of your single threaded message loop - no other message would be processed.

于 2009-08-10T20:21:02.813 回答
3

MessageBox 运行自己的 Win32 消息循环(以免冻结调用应用程序)。

小心在不可重入函数中使用它......

编辑:详细说明:Windows 上的消息循环是这样的(从 msdn 窃取):

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
} 

DispatchMessage 将调用它需要的任何窗口过程。该窗口进程可以启动它自己的循环(在同一个线程上),它会调用 DispatchMessage 本身,它会调用任何消息处理程序。

如果您想查看它,请在调试器中启动您的应用程序,弹出消息框并中断。您将被丢弃在其循环中的某个位置。查看调用堆栈,看看是否可以找到父循环。

于 2009-08-10T20:18:38.323 回答