MFC message route (Part TWO)

类别:编程语言 点击:0 评论:0 推荐:

消息的流动
直线上溯的消息
LRESULT CALLBACK
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
 // …

 // all other messages route through message map
 CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
 // …
 return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}

LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
 WPARAM wParam = 0, LPARAM lParam = 0)
{
 // …
 // Catch exceptions thrown outside the scope of a callback
 // in debug builds and warn the user.
 LRESULT lResult;
 // …
// delegate to object's WindowProc
 lResult = pWnd->WindowProc(nMsg, wParam, lParam);
 // …
return lResult;
}
最后,消息被传到了WindowProc中,这是一个CWnd类中的虚函数,在MFC中,重载次函数的类有很多,但是由于一般的窗口都派生于CWnd(例如CFrameWnd),所以,来看CWnd::WndProc
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
 // OnWndMsg does most of the work, except for DefWindowProc call
 LRESULT lResult = 0;
 if (!OnWndMsg(message, wParam, lParam, &lResult))
  lResult = DefWindowProc(message, wParam, lParam);
 return lResult;
}
首先利用OnWndMsg处理消息,如果OnWndMsg没能处理消息,调用DefWindowProc。先来看OnWndMsg,这是一个CWnd类的虚函数,这个函数的逻辑很简单,如果消息是WM_COMMAND或WM_NOTIFY,则把消息分别交给OnCommand和OnNotify处理,否则首先在MFC内建的消息缓存中查找消息,如果命中但没有相应的处理函数,则返回FALSE(这样的话会交由CWnd::DefWindowProc处理),如果命中,则进一步判断是用户自己注册的消息还是标准Windows消息,如果是前者,就跳到标签LDispatchRegistered处理,调用相应的消息处理函数,否则就跳到LDispatch处理,调用正确的消息处理函数。
如果消息不在缓存中,那么就沿着某个类的继承路线,由AfxFindMessageEntry在每一个类的消息映射表中查找,如果找到匹配项,同样按照是否是用户注册的消息的逻辑对消息进行处理,如果始终没有找到匹配项,则返回FALSE,交由CWnd::DefWindowProc处理。
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
 LRESULT lResult = 0;
 union MessageMapFunctions mmf;
 mmf.pfn = 0;

 // special case for commands
 if (message == WM_COMMAND)
 {
  if (OnCommand(wParam, lParam)) // 对WM_COMMAND交给OnCommand处理
  {
   lResult = 1;
   goto LReturnTrue;
  }
  return FALSE;
 }

 // special case for notifies
 if (message == WM_NOTIFY)
 {
  NMHDR* pNMHDR = (NMHDR*)lParam;
  if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
   goto LReturnTrue;
  return FALSE;
 }
 // … …
const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap(); // 获取类的消息映射表
 UINT iHash;
iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1);
 AfxLockGlobals(CRIT_WINMSGCACHE);
 AFX_MSG_CACHE* pMsgCache;
pMsgCache = &_afxMsgCache[iHash];
 const AFX_MSGMAP_ENTRY* lpEntry;
 // 判断消息是否在缓存中
 if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)
 {
  // cache hit
  lpEntry = pMsgCache->lpEntry;
  AfxUnlockGlobals(CRIT_WINMSGCACHE);
  if (lpEntry == NULL) // 在缓存中,但是没有相应的表项,返回FALSE
   return FALSE;

  // cache hit, and it needs to be handled
  if (message < 0xC000) // 否则,按照是否是用户自定义的消息, 跳转到相应的位置
   goto LDispatch; // 标准Windows消息
  else
   goto LDispatchRegistered; // 用户自定义消息
 }
 else // 如果消息不在缓存中,就只好挨家挨户检查一番
 {
  // not in cache, look for it
  pMsgCache->nMsg = message;
  pMsgCache->pMessageMap = pMessageMap;
  // 下面这个for循环从派生类到基类检查每一个类的消息映射表
  for (/* pMessageMap already init'ed */; pMessageMap != NULL;
   pMessageMap = pMessageMap->pBaseMap)
{
if (message < 0xC000) // 如果消息是Windows标准消息
   {
    // constant window message
    if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
     message, 0, 0)) != NULL)
    {
     pMsgCache->lpEntry = lpEntry;
     AfxUnlockGlobals(CRIT_WINMSGCACHE);
     goto LDispatch; // 如果找到对应项,就转去处理
    }
   }
   else // 如果是用户自定义消息
   {
    // registered windows message
    lpEntry = pMessageMap->lpEntries;
    while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)
    {
     UINT* pnID = (UINT*)(lpEntry->nSig);
     ASSERT(*pnID >= 0xC000 || *pnID == 0);
      // must be successfully registered
     if (*pnID == message)
     {
      pMsgCache->lpEntry = lpEntry;
      AfxUnlockGlobals(CRIT_WINMSGCACHE);
      goto LDispatchRegistered; // 如果找到就转去处理
     }
     lpEntry++;      // keep looking past this one
    }
   }
  }
  // 即不在缓存中,所有的类也都对此消息置之不理,那么就返回FALSE,交由CWnd::DefWndProc
// 处理
  pMsgCache->lpEntry = NULL;
  AfxUnlockGlobals(CRIT_WINMSGCACHE);
  return FALSE;
 }
 // 下面是对Windows标准消息和用户自定义消息的处理
LDispatch:
 ASSERT(message < 0xC000);

 mmf.pfn = lpEntry->pfn;

 switch (lpEntry->nSig)
 {
 default:
  ASSERT(FALSE);
  break;

 case AfxSig_b_D_v:
  lResult = (this->*mmf.pfn_b_D)(CDC::FromHandle(reinterpret_cast<HDC>(wParam)));
  break;

 case AfxSig_b_b_v:
  lResult = (this->*mmf.pfn_b_b)(static_cast<BOOL>(wParam));
  break;

 case AfxSig_b_u_v:
  lResult = (this->*mmf.pfn_b_u)(static_cast<UINT>(wParam));
  break;

 case AfxSig_b_h_v:
  lResult = (this->*mmf.pfn_b_h)(reinterpret_cast<HANDLE>(wParam));
  break;
 // … …
}
 goto LReturnTrue;
LDispatchRegistered:    // for registered windows messages
 ASSERT(message >= 0xC000);
 ASSERT(sizeof(mmf) == sizeof(mmf.pfn));
 mmf.pfn = lpEntry->pfn;
 lResult = (this->*mmf.pfn_l_w_l)(wParam, lParam);

LReturnTrue:
 if (pResult != NULL)
  *pResult = lResult;
 return TRUE;
}
写到这里,对于直线上溯的消息的处理过程,应该是很清楚了,概括一下,就是先准备好两个处理过程,一个用来处理标准Windows消息,一个用来处理用户自定义消息,之后,根据消息是不是在缓存中,进行不同的查找,如果找到,根据消息的类型转到不同的处理过程中去,如果处理过了,就返回TRUE,否则返回FALSE,交由CWnd::DefWindowProc处理。
最后要补充的就是用来查找消息的函数AfxFindMessageEntry
const AFX_MSGMAP_ENTRY* AFXAPI
AfxFindMessageEntry(const AFX_MSGMAP_ENTRY* lpEntry,
 UINT nMsg, UINT nCode, UINT nID)
{
_asm
 {
   MOV     EBX,lpEntry
   MOV     EAX,nMsg
   MOV     EDX,nCode
   MOV     ECX,nID
 __loop:
   CMP     DWORD PTR [EBX+16],0        ; nSig (0 => end)
   JZ      __failed
   CMP     EAX,DWORD PTR [EBX]         ; nMessage
   JE      __found_message
 __next:
   ADD     EBX,SIZE AFX_MSGMAP_ENTRY
   JMP     short __loop
 __found_message:
   CMP     EDX,DWORD PTR [EBX+4]       ; nCode
   JNE     __next
 // message and code good so far
 // check the ID
   CMP     ECX,DWORD PTR [EBX+8]       ; nID
   JB      __next
   CMP     ECX,DWORD PTR [EBX+12]      ; nLastID
   JA      __next
 // found a match
   MOV     lpEntry,EBX                 ; return EBX
   JMP     short __end
 __failed:
   XOR     EAX,EAX                     ; return NULL
   MOV     lpEntry,EAX
 __end:
 }
 return lpEntry;
 // …
}
到此,关于直线上溯消息的处理就结束了,很简单,无非就是从派生类到基类的一个比较操作,真正复杂些的是MFC对于WM_COMMAND消息的处理,这个消息可以被Document/View处理。

本文地址:http://com.8s8s.com/it/it23085.htm