Analysis of mappings in MFC
Condition lookup mapping
A large number of begin_xxx_map is used in the MFC, and the mapping is optimized, such as message mapping, OLE command mapping, and interface, etc. Each map includes a pointer to a mapping of a base class. This way, when a class needs to find an object based on certain conditions, it will find this object, if not found, then the base class is found until the foundation class. Such lookups include the distribution of Windows messages, commands, events, and OLE commands, and queries for interfaces implemented.
Here is some code of the function BOOL CWND: ONDMSG (Uint Message, WPARAM WPARAM, LPARAM LPARAM, LRESULT * PRESULT) demonstrated how to find a function based on the message ID.
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 && PMSSAGEMAP == PMSGCACHE-> PMESSAGEMAP)
{
// cache hit
LpenTry = pmsgcache-> lpendry;
AfXunlockGlobals (crit_winmsgcache);
IF (LpenTry == Null)
Return False;
// cache hit, And it Needs to be Handled
IF (Message <0xC000)
Goto ldispatch;
Else
Goto ldispatchregistered;
}
Else
{
// Not in cache, Look for IT
PMSGCache-> nmsg = message;
PMSGCache-> PMSSAGEMAP = PMESSAGEMAP;
#ifdef _AFXDLL
For (/ * pimentagemap already initàed * /; pimentbasemap! = null;
PMessageMap = (* pMessagemap-> pfNgetBasemap) ())
#ELSE
FOR (/ * pimentagemap already initàed * /; pimentagemap! = null;
PMessageMap = PMessageMap-> PBaseMAP)
#ENDIF
{
// NOTE: CATE: Catch Not So Common But Fatal Mistake !!
// begin_Message_Map (CMYWND, CMYWND)
#ifdef _AFXDLL
Assert (PMessageMap! = (* PimentBaseMap) ());
#ELSE
Assert (PMessageMap! = PMESSAGEMAP-> PBASEMAP);
#ENDIF
IF (Message <0xC000)
{
// Constant Window Messageif ((LpenTry = AFXFindMessagentry (pMessageMap-> LpenTries,
Message, 0, 0))! = NULL)
{
PMSGCache-> LpenTry = LPENTRY;
AfXunlockGlobals (crit_winmsgcache);
Goto ldispatch;
}
}
Else
{
// Registered Windows Message
LpenTry = pimentagemap-> lpenince;
While ((LpenTry = AFXFindMessagentry (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 ONE
}
}
}
PMSGCache-> LpenTry = NULL;
AfXunlockGlobals (crit_winmsgcache);
Return False;
}
Ldispatch:
note
The cache for findings can improve the efficiency of the lookup. Don't deceive the names from MFC. From the data structure, lookup is the order, rather than using the hash technology used by the CMAP class, so the above code uses hash technology, caches the most recent lookup results and the most common use The mapping item is placed in front usually helps to improve efficiency.
The advantage of using the lookup map is convenient to expand and override mapping in derived classes (such as re-implement idispatch) without rewriting / reloading the search function (the distribution of messages and commands, or the query of the interface); Use a virtual function table that consumes a large resource consumption. (Although this, the CWND class has no number of virtual functions, and it is not yet seen that there is an increase in the upgrade of MFC6 to MFC7).
Using the bad place to find maps, of course, it is an understanding of the problem and performance loss.
Handle mapping
The MFC has spared no effort to encapsulate the handle into an object, in order to ensure the same thread, the <-> handle mapping is one-to-one, created a wide variety of handle mappings, windows, GDI objects, and menus, and the like. In order to encapsulate the temporary handle returned by the API such as getdlgitem, SelectObject, the MFC also produces a temporary object <-> handle map. The handle map makes the function such as getParentFrame implement.
CframeWnd * CWnd :: getParentFrame () const
{
IF (getsafehwnd () == null // no window attached
Return NULL;
Assert_Valid (this);
CWnd * pParentWnd = getParent (); // start with one Parent Up
While (pParentWnd! = null)
{
IF (pParentWnd-> isframeWnd ()) return (cframewnd *) pParentWnd;
PparentWnd = pParentWnd-> getParent ();
}
Return NULL;
}
_AFXWIN_INLINE CWND * CWND :: getParent () const
{Assert (:: iswindow (m_hwnd)); return cWnd :: fromHandle (:: getparent (m_hwnd));}
See it, it first calls the API getParent, then goes to the window of this thread <-> handle map to find the object pointer, then call CWnd :: isframeWnd to determine if the object is a framework. (Thank you, this function is used in virtual functions instead of cobject :: iskindof, or it has passed the runtime information). Since the corresponding uniqueness of the object and handle, the unique parent frame window object can be found.
This mapping is also used in some frequently called functions.
LResult Callback
AfxWndProc (HWND HWND, UINT NMSG, WPARAM WPARAM, LPARAM LPARAM)
{
// Special Message Which Identifies The Window As Using AfxWndProc
IF (NMSG == WM_QUERYAFXWNDPROC)
Return 1;
// All Other Messages Route Through Message Map
CWND * PWND = CWND :: fromHandlepermanent (hwnd);
Assert (PWND! = Null);
Assert (pWnd-> m_hwnd == hwnd);
IF (PWND == Null || PWND-> M_HWND! = HWND)
Return :: DefWindowProc (HWND, NMSG, WPARAM, LPARAM);
Return AFXCallWndProc (PWND, HWND, NMSG, WPARAM, LPARAM);
}
That is, it is necessary to traverse the permanent handle mapping inside an object returned by AFXMAPHWND (). And this function is called when each message arrives. This is one of the reasons for the performance loss of MFC applications.
Similarly, since these objects are owned by the thread, the storage mode of these handle maps of the MFC is the thread local storage (TLS). That is, for the same handle, the corresponding object in the handle map can be inconsistent. This will cause some problems in multithreading programs, see Microsoft Knowledge Base Articles Q147578 CWnd Derived MFC Objects and Multi-Threaded Applications.
to sum up
MFC has made a lot of work in order to quickly and conveniently, such as the above two mappings, but in terms of performance. When developing applications, we need to trade out quickly and easily and performance loss. (The words said this, but according to Moore's law, I have been nonsense for two years.)