请考虑以下代码段,其中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()
尚未返回 1>} strong>在最后一次调用DialogProc()
?
P.S。请注意,我不是在问如何完成所需的行为。我只是在寻找洞察力,以了解“幕后”发生的事情,从而导致实际行为。
答案 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;
}