为什么消息框不阻止线程?

Why doesn't message box block the thread?

本文关键字:线程 消息 为什么      更新时间:2023-10-16

考虑下面的代码片段,其中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()在对DialogProc()的最后一次调用中尚未返回

附言:请注意,我并不是在问如何完成期望的行为。我只是想了解实际行为中"幕后"发生了什么。

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

如果它不这样做,那么像WM_PAINT这样的事件就不会得到处理,你的应用程序看起来就像死了一样(除了消息框)。因为计时器仍在运行,所以WM_TIMER事件会在适当的时间排队。

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;
}