LRESULT DispatchMessage( const MSG *lpmsg );
LRESULT SendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
这两个函数有什么不同呢?
在写消息循环的时候,往往如下
for(;;)
{
while(bDoIdle && !::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE))
{
if(!OnIdle(nIdleCount++))
bDoIdle = FALSE;
}
bRet = ::GetMessage(&m_msg, NULL, 0, 0);
if(bRet == -1)
{
ATLTRACE2(atlTraceUI, 0, _T("::GetMessage returned -1 (error)\n"));
continue; // error, don't process
}
else if(!bRet)
{
ATLTRACE2(atlTraceUI, 0, _T("CMessageLoop::Run - exiting\n"));
break; // WM_QUIT, exit message loop
}
if(!PreTranslateMessage(&m_msg))
{
::TranslateMessage(&m_msg);
::DispatchMessage(&m_msg);
}
if(IsIdleMessage(&m_msg))
{
bDoIdle = TRUE;
nIdleCount = 0;
}
}
用的是DispatchMessage,如果换成SendMessage可不可以呢
反过来,如果用SendMessage的地方,替换成DispatchMessage行不行呢
先来看用windbg调试两个函数的结果
下面是SendMessage的调用栈
0047f324 76690d27 00000000 778c4128 005c148e USER32!UserCallWinProcCheckWow
0047f35c 76690d4d 778c4128 005c148e 0000036a USER32!CallWindowProcAorW+0xab
0047f37c 78457994 778c4128 005c148e 0000036a USER32!CallWindowProcW+0x1b
0047f3a0 78458b02 0000036a 00000000 00000000 mfc90ud!CWnd::DefWindowProcW+0x34
0047f3bc 78455f00 0000036a 00000000 00000000 mfc90ud!CWnd::WindowProc+0x52
0047f438 784564c6 0047f750 005c148e 0000036a mfc90ud!AfxCallWndProc+0xf0
0047f458 78451b0b 005c148e 0000036a 00000000 mfc90ud!AfxWndProc+0xa6
0047f494 766862fa 005c148e 0000036a 00000000 mfc90ud!AfxWndProcBase+0x5b
0047f4c0 76686d3a 78451ab0 005c148e 0000036a USER32!InternalCallWinProc+0x23
0047f538 7668965e 00000000 78451ab0 005c148e USER32!UserCallWinProcCheckWow+0x109
0047f57c 766896c5 00952ba0 00000000 78451ab0 USER32!SendMessageWorker+0x581
0047f5a0 785c5054 005c148e 0000036a 00000000 USER32!SendMessageW+0x7f
0047f5bc 7845fa61 0000036a 00000000 00000000 mfc90ud!CWnd::SendMessageW+0x44
0047f5f0 7849848c 00000004 34ba912f 0047f7e8 mfc90ud!CWnd::RunModalLoop+0x191
*** WARNING: Unable to verify checksum for e:\cpp\test\modalmsg\Debug\modalmsg.exe
0047f65c 002c22fd 34bda1cf 00000000 00000000 mfc90ud!CDialog::DoModal+0x1ec
0047f7f4 7846f704 cccccccc cccccccc cccccccc modalmsg!CmodalmsgApp::InitInstance+0xad [e:\cpp\test\modalmsg\modalmsg\modalmsg.cpp @ 64]
0047f818 002c748a 002b0000 00000000 00823f32 mfc90ud!AfxWinMain+0x84
0047f830 002c398b 002b0000 00000000 00823f32 modalmsg!wWinMain+0x1a [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\appmodul.cpp @ 34]
0047f8e0 002c36ef 0047f8f4 755c339a 7efde000 modalmsg!__tmainCRTStartup+0x28b [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 578]
0047f8e8 755c339a 7efde000 0047f934 77899ef2 modalmsg!wWinMainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]
0047f8f4 77899ef2 7efde000 682e76b7 00000000 KERNEL32!BaseThreadInitThunk+0xe
0047f934 77899ec5 002c17e4 7efde000 00000000 ntdll_77860000!__RtlUserThreadStart+0x70
0047f94c 00000000 002c17e4 7efde000 00000000 ntdll_77860000!_RtlUserThreadStart+0x1b
下面是DispatchMessage的调用栈
0047f520 766877c4 00000000 78451ab0 005c148e USER32!UserCallWinProcCheckWow
0047f580 7668788a 78451ab0 00000000 0047f5b0 USER32!DispatchMessageWorker+0x3bc
0047f590 784e7592 0086adc0 0047f5ac 78451cb9 USER32!DispatchMessageW+0xf
0047f5b0 784e8b4e 002d02d0 0047f5c8 784e75c1 mfc90ud!AfxInternalPumpMessage+0x102
0047f5bc 784e75c1 002d02d0 0047f5f0 7845faa7 mfc90ud!CWinThread::PumpMessage+0xe
0047f5c8 7845faa7 00000000 00000000 0047f750 mfc90ud!AfxPumpMessage+0x21
0047f5f0 7849848c 00000004 34ba912f 0047f7e8 mfc90ud!CWnd::RunModalLoop+0x1d7
0047f65c 002c22fd 34bda1cf 00000000 00000000 mfc90ud!CDialog::DoModal+0x1ec
0047f7f4 7846f704 cccccccc cccccccc cccccccc modalmsg!CmodalmsgApp::InitInstance+0xad [e:\cpp\test\modalmsg\modalmsg\modalmsg.cpp @ 64]
0047f818 002c748a 002b0000 00000000 00823f32 mfc90ud!AfxWinMain+0x84
0047f830 002c398b 002b0000 00000000 00823f32 modalmsg!wWinMain+0x1a [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\appmodul.cpp @ 34]
0047f8e0 002c36ef 0047f8f4 755c339a 7efde000 modalmsg!__tmainCRTStartup+0x28b [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 578]
0047f8e8 755c339a 7efde000 0047f934 77899ef2 modalmsg!wWinMainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]
0047f8f4 77899ef2 7efde000 682e76b7 00000000 KERNEL32!BaseThreadInitThunk+0xe
0047f934 77899ec5 002c17e4 7efde000 00000000 ntdll_77860000!__RtlUserThreadStart+0x70
0047f94c 00000000 002c17e4 7efde000 00000000 ntdll_77860000!_RtlUserThreadStart+0x1b
两者都走到了UserCallWinProcCheckWow
是不是两者就是一样的呢
不过SendMessage是可以不同线程和进程之间传递消息
而DispathMessage确不可以
通过查看reactos的代码
发现SendMessage多处理了以下几种情况
1. hwnd参数为HWND_TOPMOST HWND_BROADCASST的类型的窗口,这类窗口是要遍历出所有的窗口然后再调用对应的消息处理函数,而DispatchMessage却不会.
2. 对于hwnd注册的MessageQueue不在本线程的,需要跨线程去调用.而DispatchMessage却不会.
3. DispatchMessage会对WM_PAINT消息做额外的一些处理.而SendMessage不会.
贴几处代码,看的更详细
LRESULT WINAPI
SendMessageW(HWND Wnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam)
{
MSG UMMsg, KMMsg;
LRESULT Result;
PWND Window;
PTHREADINFO ti = GetW32ThreadInfo();
if ( Msg & ~WM_MAXIMUM )
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (Wnd != HWND_TOPMOST && Wnd != HWND_BROADCAST && (Msg < WM_DDE_FIRST || Msg > WM_DDE_LAST))
{
Window = ValidateHwnd(Wnd);
if ( Window != NULL &&
Window->head.pti == ti &&
// !IsThreadHooked(GetWin32ClientInfo()) && // Enable to test message system bug.
!ISITHOOKED(WH_CALLWNDPROC) &&
!ISITHOOKED(WH_CALLWNDPROCRET) &&
!(Window->state & WNDS_SERVERSIDEWINDOWPROC) )
{
/* NOTE: We can directly send messages to the window procedure
if *all* the following conditions are met:
* Window belongs to calling thread
* The calling thread is not being hooked for CallWndProc
* Not calling a server side proc:
Desktop, Switch, ScrollBar, Menu, IconTitle, or hWndMessage
*/
return IntCallMessageProc(Window, Wnd, Msg, wParam, lParam, FALSE);
}
}
UMMsg.hwnd = Wnd;
UMMsg.message = Msg;
UMMsg.wParam = wParam;
UMMsg.lParam = lParam;
if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, FALSE))
{
return FALSE;
}
Result = NtUserMessageCall( Wnd,
KMMsg.message,
KMMsg.wParam,
KMMsg.lParam,
(ULONG_PTR)&Result,
FNID_SENDMESSAGE,
FALSE);
MsgiUMToKMCleanup(&UMMsg, &KMMsg);
return Result;
}
LRESULT WINAPI
DispatchMessageW(CONST MSG *lpmsg)
{
LRESULT Ret = 0;
PWND Wnd;
BOOL Hit = FALSE;
if ( lpmsg->message & ~WM_MAXIMUM )
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (lpmsg->hwnd != NULL)
{
Wnd = ValidateHwnd(lpmsg->hwnd);
if (!Wnd) return 0;
}
else
Wnd = NULL;
if (is_pointer_message(lpmsg->message))
{
SetLastError( ERROR_MESSAGE_SYNC_ONLY );
return 0;
}
if ((lpmsg->message == WM_TIMER || lpmsg->message == WM_SYSTIMER) && lpmsg->lParam != 0)
{
WNDPROC WndProc = (WNDPROC)lpmsg->lParam;
if ( lpmsg->message == WM_SYSTIMER )
return NtUserDispatchMessage( (PMSG) lpmsg );
if (!NtUserValidateTimerCallback(lpmsg->hwnd, lpmsg->wParam, lpmsg->lParam))
{
WARN("Validating Timer Callback failed!\n");
return 0;
}
_SEH2_TRY
{
Ret = WndProc(lpmsg->hwnd,
lpmsg->message,
lpmsg->wParam,
GetTickCount());
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Hit = TRUE;
}
_SEH2_END;
}
else if (Wnd != NULL)
{
if ( (lpmsg->message != WM_PAINT) && !(Wnd->state & WNDS_SERVERSIDEWINDOWPROC) )
{
Ret = IntCallMessageProc(Wnd,
lpmsg->hwnd,
lpmsg->message,
lpmsg->wParam,
lpmsg->lParam,
FALSE);
}
else
Ret = NtUserDispatchMessage( (PMSG) lpmsg );
}
if (Hit)
{
WARN("Exception in Timer Callback WndProcW!\n");
}
return Ret;
}
查以上问题是由一个问题而引发出来的
就是一个窗口DoModal之后,其父窗口还能不能收到消息
答案是肯定的
延伸一下问题,如果用RegisterWindowsMessage注册了一个广播消息,然后用SendMessage广播出去
父窗口还能不能收到消息
答案也是肯定的
原因是SendMessage会直接调用其窗口处理函数,也就是不经过父窗口的消息循环和DoModal窗口的消息循环而直接被调用.
再延伸一点就是
用SendMessage替换DispatchMessage不会有问题,但是效率没有DispatchMessage高
但是用DispatchMessage替换SendMessage却不行
over