sendmessage diapatchmessage不同

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值