为什么消息框没有阻塞线程?

时间:2017-10-23 13:22:11

标签: c++ multithreading winapi

请考虑以下代码段,其中WM_TIMER消息上显示消息框。

#define IDT_TIMER1 1001

INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch(message) {

        //...

        case WM_INITDIALOG:
        {
            //...

            SetTimer(hWnd, IDT_TIMER1, 1000, (TIMERPROC)NULL);

            break;
        }
        case WM_TIMER:
        {
            int ret = MessageBox(hWnd, L"Cancel operation?", NULL, MB_YESNO);
            if(ret == IDYES) {
                EndDialog(hWnd, 0);
            } else {
                // no-op: keep going
            }

            break;
        }

        //...

        default:
            return FALSE;
    }

    return FALSE;
}

我希望此代码在初始计时器时显示一个消息框,并阻止该线程,直到用户单击一个按钮。 实际发生的是,消息框会在每个计时器刻度显示,即使用户点击任何按钮。

当我检查线程的调用堆栈时,我看到多次调用DialogProc(),所有调用都被调用MessageBox()被调用(即所有等待用户输入)。

考虑到调用堆栈的状态,DialogProc()如何在相同的线程中调用MessageBox() 尚未返回 } strong>在最后一次调用DialogProc()

P.S。请注意,我不是在问如何完成所需的行为。我只是在寻找洞察力,以了解“幕后”发生的事情,从而导致实际行为。

2 个答案:

答案 0 :(得分:8)

MessageBox启动一个新的Message Loop,除其他外,它可以通过正常的Windows回调机制访问并调用DialogProc

如果它没有这样做,那么WM_PAINT之类的事件将无法得到处理,而您的应用看起来已经死亡(除了消息框)。由于计时器仍在运行,WM_TIMER事件会在适当的时间排队。

答案 1 :(得分:2)

MessageBox进入渲染窗口,处理按钮所需的嵌套消息循环。

MessageBox调用中指定为第一个参数的窗口,EnableWindow禁用了输入,但这并未禁用所有消息,因此您仍然会收到WM_PAINT,WM_TIMER等。通常它会禁用用户输入:鼠标,键盘,还有鼠标窗口大小。

此伪代码显示了如何可能实现MessageBox模拟:

int MessageBox( HWND owner_hwnd, ... )
{
    ...
    HWND box_hwnd = CreateWindowEx( ..., owner_hwnd, ... );
    ...
    EnableWindow( owner_hwnd, FALSE );
    ...
    while ( !done )
    {
        MSG msg;
        GetMessage( &msg, ... );
        if ( IsDialogMessage( box_hwnd, &msg ) )
            continue;
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    ...
    EnableWindow( owner_hwnd, TRUE );
    ...
    DestroyWindow( box_hwnd );
    ...
    return button;
}