Click Here to Install Silverlight*
United StatesChange|All Microsoft Sites
MSDN
|Developer Centers|Library|Downloads|Code Center|Subscriptions|MSDN Worldwide
Search for


Advanced Search
MSDN Home > MSJ > September 1997
September 1997


Code for this article: CQA0997.exe (28KB)
Paul DiLascia is a freelance software consultant specializing in training and software development in C++ and Windows. He is the author of Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992).

Q In the June 1996 issue, you mentioned the temporary/permanent handle map mechanism in MFC. When I tried to browse though the source code about it, I just got confused. Could you throw some light on this confusing topic?
Joe Chau
Altera Corporation
A When you program Windows® with MFC, it's important to have a clear understanding of the difference between the window that lives on the screen and the C++ CWnd object that represents it. This topic has come up in my column from time to time over the years, but it's the kind of thing you can never understand too well, so why not review it again now?
Centuries ago, when people programmed Windows using mere C, there were just windows, which were manipulated using HWNDs. An HWND is, as the name suggests, a handle to a WND. WND is a private, Windows-internal structure that holds information the operating system needs about a window, like its position, title, and so on. You can't access WND yourself (unless you're using techniques from one of those "undocumented secrets" books that Andrew Schulman and Matt Pietrek write). Instead, you call API functions like GetWindowRect and GetWindowText, passing the HWND as an argument. From your perspective, the HWND is just a magic cookie that identifies the window; the fact that it is actually a memory handle to a WND struct is irrelevant.
Windows—that is, HWNDs—behave like objects in the sense that they come in different classes—such as Button, List Box, and so on—and you can send them messages via ::SendMessage to make them do things like paint themselves or move. So even before C++ was invented, the Windows API was loosely object-oriented.
When C++ came along, the question became: how do you program this stuff in C++? The first thing you need is a C++ class to represent a window. In MFC, that class is CWnd. (In MFC, instead of dealing with HWNDs, you deal with CWnds.) Inside every CWnd is a member, m_hWnd, that holds the HWND. MFC uses m_hWnd to translate MFC calls into C API calls. Most Win32 API functions have MFC counterparts written as thin wrappers—small inline functions that merely pass the buck to Win32. For example:

 inline void CWnd::SetWindowText(LPCTSTR lpsz)
 { 
     ::SetWindowText(m_hWnd, lpszString); 
 }
So when you write

 pWnd->SetWindowText("Mumble");
the compiler generates exactly the same code as if you'd written this:

 ::SetWindowText(pWnd->m_hWnd, "Mumble");
MFC initializes CWnd::m_hWnd when you create the window. In the plain vanilla scenario, you have some window like CMyWnd derived from CWnd and you call CWnd::Create or CreateEx to create it. MFC calls ::CreateWindow or ::CreateWindowEx to create the window and stores the HWND returned in m_hWnd. Now the CWnd points to its window. But how does MFC go the other way? That is, given an HWND, how does MFC find the corresponding CWnd and why is that important?
There are many times MFC needs to get the CWnd for a given HWND. In particular, MFC has buried inside it a universal window procedure, AfxWndProc, that handles window messages by routing them to the appropriate CWnd objects and message map function. Remember, underneath all the object-oriented fluff, everything reduces to ordinary Windows C API programming. Every window still has a window procedure that handles messages like WM_ CREATE and WM_PAINT. But the window procedure that MFC uses—AfxWndProc—translates these messages into calls to the appropriate CWnd-derived class member functions like CYourWnd::OnCreate and CYourWnd::OnPaint. But when the message arrives at AfxWndProc, MFC doesn't get a CWnd object; all it gets is the standard arguments: HWND, message code, wParam, and lParam. So how does MFC find the CWnd?
There are many ways of doing it. One way would be to store the CWnd pointer as a window property. Windows lets you store any 32-bit values you want as a property using functions ::GetProp and ::SetProp. For example, the CWnd::Create function, after creating the window, could do something like this:

 // hypothetical
 CWnd::Create()
 {
 •
 •
 •
     m_hWnd = ::CreateWindowEx(...);
     ::SetProp(m_hWnd, "CWndProp", (HANDLE)this);
 •
 •
 •
 }
Now, to get the CWnd, MFC's window procedure could do something like this:

 // hypothetical
 AfxWndProc(HWND hWnd, UINT, uMsg, WPARAM wp, LPARAM lp)
 {
     CWnd* pWnd = 
         (CWnd*)::GetProp(hWnd, "CWndProp");
 
     // Dispatch message through pWnd
 •
 •
 •
 }
In this hypothetical example, CWndProp is an arbitrary string that names the property. It can be anything as long as the code setting and getting the property uses the same string. The property mechanism works fine. In fact, I once wrote a C++ framework—in my book Windows++ (Addison-Wesley, 1992)—that uses properties to hold the CWnd pointer. But the folks who built MFC felt the property mechanism might be too slow, particularly if the programmer's application also uses lots of properties for something else. (To be honest, I don't think I've ever seen a program that uses window properties.) Keep in mind that AfxWndProc is called every time a message is processed, which is practically all the time. So it's extremely important that whatever mechanism the framework uses to get the CWnd be fast.
The fastest and simplest alternative to using properties would be to ask the operating system folks to add another 32 bits to the WND structure (the internal OS structure that HWND is a handle to), which MFC could then use to store its CWnd pointer. I'm sure the developers in MFC-land would love to get their own field in the WND structure. Personally, I don't know why they haven't done this. Perhaps because multiple processes might communicate with the same window through multiple CWnd objects, so a single 32-bit field isn't enough; there would have to be one per thread.
In any case, MFC does not have its own field in the WND structure, so something more inventive is required to map HWNDs to CWnds. This is where the permanent and temporary maps come in. MFC maintains a global map that's essentially just a dictionary or association table that associates HWNDs and CWnds. Conceptually, it works like this. CWnd::Create sets up the association:

 // pseudo-code
 CWnd::Create()
 {
 •
 •
 •
     m_hWnd = ::CreateWindowEx(...);
     globalMap.Add(m_hWnd, this);
 •
 •
 •
 }
Once the association is added to the global map, all AfxWndProc has to do is look it up:

 // pseudo-code
 AfxWndProc(HWND hWnd, UINT, uMsg, WPARAM wp, LPARAM lp)
 {
     CWnd* pWnd = globalMap.Lookup(hWnd);
     // Dispatch message using CWnd message map
 •
 •
 •
 }
Of course, the actual implementation is trickier than I've made it look. In real life, MFC doesn't add the association after ::CreateWindowEx returns; that would be too late because ::CreateWindowEx sends a few messages like WM_ NCCREATE, WM_CREATE, and WM_GETMINMAXINFO, which would be lost if the association was added after CreateWindowEx returns. So instead, MFC sets up a windows hook to trap WM_NCCREATE, then sets up the link in the hook procedure. That way, CWnd objects can handle the early creation messages like WM_NCCREATE and WM_CREATE as they would any other message.
For the purpose of understanding what's going on, the details of how globalMap is implemented are unimportant. The important thing to know is that there is such a map, and MFC uses it to find the CWnd associated with an HWND.
What I have called globalMap in the pseudo-code above corresponds to what MFC calls the permanent map. In fact, there are two maps, permanent and temporary. All of what I've just described applies to windows that your application explicitly creates—your main frame window (CMainFrame), your views and dialogs, and so on. With all these windows, you call some MFC create function to explicitly create a window. Usually it's CWnd::Create or CWnd::CreateEx, but it could be something else like CDialog::DoModal in the case of a modal dialog, SubclassDlgItem in the case of a dialog control, or some other overloaded Create function. MFC adds the HWND/CWnd association to its permanent window map, and everything flies.
This works fine and dandy for windows you have explicitly created, but sometimes you need a CWnd corresponding to a window you didn't create. For example, suppose your app wants to see whether there's a desktop window called Fooble Window, and if so, send it a message.

 CWnd *pWnd = 
     CWnd::FindWindow(NULL,"Fooble Window");
 if (pWnd)
     pWnd->SendMessage(WM_MYMESSAGE);
If there's a top-level window with the title Fooble Window, MFC returns a CWnd that represents this window. But where does the CWnd come from? Your app hasn't created it! Fooble Window might be another application entirely, one you didn't even write, one that isn't even written in MFC! So where does the CWnd come from?
This is where temporary maps come in. MFC's implementation of CWnd::FindWindow looks something like the following:

 CWnd* CWnd::FindWindow(LPCTSTR lpszClassName, 
                        LPCTSTR lpszWindowName)
 { 
     return CWnd::FromHandle(::FindWindow(lpszClassName,
                                          lpszWindowName)); 
 }
CWnd::FindWindow calls the global ::FindWindow, then passes the HWND to CWnd::FromHandle to create a CWnd for it. CWnd::FromHandle is the interesting function here. Once again, I'm paraphrasing the code, but essentially it looks something like this:

 // pseudo-code
 CWnd* CWnd::FromHandle(HWND hWnd)
 {
     CWnd* pWnd = globalMap.Lookup(hWnd);
     if (!pWnd) {
         // not in permanent map
         pWnd = tempMap.Lookup(hWnd);
         if (!pWnd) {
             pWnd = new CWnd(hWnd);
             tempMap.Add(hWnd, pWnd);
         }
     }
     return pWnd;
 }
If the HWND doesn't exist in the permanent map (globalMap), MFC looks in the temporary map. If the HWND isn't there, MFC creates a CWnd on-the-fly, hooks it up to the HWND, adds it to the temporary map, and returns it. CWnd::FromHandle is guaranteed to return a CWnd whether your program created it or not.
Astute readers will be wondering: why is this other map I've described called the temporary map? Answer: because the objects in it exist only temporarily. Duh. One of the things MFC does as part of its normal idle processing (in CWinThread::OnIdle) is delete all the temporary objects.

 // pseudo-code
 BOOL CWinThread::OnIdle(LONG lCount)
 {
 •
 •
 •
     if (lCount >= 0)
         tempMap.DeleteAll();
 }
Why does MFC do this? For one thing, to reclaim memory. But also because your program might later decide to create a permanent window for one that was once temporary, which won't work because MFC can have only one CWnd object for each HWND. You might think: fine, just use the temporary one. But that won't work because temporary windows are always CWnds, no matter what the actual window is. For example, if you have a dialog with an edit control and you call CWnd::GetDlgItem to get a pointer to the edit control, then—assuming you haven't subclassed the edit control with your own CEdit object—the MFC object returned is a CWnd, not a CEdit!
This is a common source of confusion to beginning MFC programmers. I've often seen code like this:

 CMyDialog::SomeFn(...)
 {
     CEdit* pEdit = 
         (CEdit*)GetDlgItem(ID_EDIT1); // NOT!!
     pEdit->GetSel(...);
 •
 •
 •
 }
Technically, this code is incorrect because—once again, assuming you haven't created a CEdit for the ID_EDIT1 control—GetDlgItem returns a CWnd, not a CEdit. You can cast to CEdit all you like, but the object is still a CWnd. In practice, however, this code works because all CEdit::GetSel does is send EM_GETSEL to the window, which, being a true edit control, it responds to. But if GetSel was a virtual function instead of a wrapper, or if it used some data member that was specific to CEdit, this code would crash.
In short, MFC creates temporary window objects only so you can then pass them immediately to other MFC functions that require CWnds. What does this mean to you? As a practical matter, it means that you should never store a pointer to a temporary window.

 // NOT!!
 m_pMyWnd = CWnd::FindWindow("Fooble Window");
If the object returned from FindWindow is a temporary window, MFC will delete it on the next idle cycle, leaving your m_pMyWnd pointing to outer space. The only thing you can do with a temporary window object is use it right away; that is, pass it to some other function that requires a CWnd. If you want a permanent CWnd for Fooble Window, you can create your own with new and Attach it.
Some of you might be wondering: how do I know if a CWnd returned by an MFC function is temporary or not? Answer: read the documentation. The reference manual will tell you when it's not safe to hold on to a CWnd pointer. Generally speaking, however, it's obvious. For example, you may be iterating over all the child windows of a window. Unless you've created your own window objects for these windows they don't exist, and CWnd::GetWindow and CWnd::GetNextWindow may return temporary objects. If for some reason you need to get a window object only if it's permanent, you can call CWnd::FromHandlePermanent. As you would expect from the name, this function returns a pointer to the CWnd object corresponding to an HWND, but only if that object is in the permanent map. Figure 1 shows some other functions that you may find useful for dealing with temporary/permanent handle maps.
Notice I said "handle maps," not "window handle maps." In fact, MFC uses the same handle map mechanism for CDC, CGdiObject (which is the base class for many objects like CPen, CBrush, CFont, and so on), CMenu, and CImageList, as well as CWnd. All these classes have FromHandle functions that create a temporary object on- the-fly if a permanent one doesn't exist. And, as with CWnd, the temporary maps for all these objects are flushed on every idle cycle.
I haven't described the details of how the maps are implemented because there's no reason you should ever have to know. In fact, the maps are implemented using CMapPtrToPtr, one of the standard MFC collection classes. Because CMapPtrToPtr uses a hash table, lookup is quick. If you really want to know how MFC implements handle maps, look in the files winhand_.h and winhand.cpp in the MFC source code directory (\MSVC\VC\MFC\SRC). Figure 2 shows the definition of CHandleMap, the class MFC uses to hold a map for one kind of object like CWnd or CMenu. The module thread state holds a pointer to each of the maps. For example, to get the window map, you can write:

 AFX_MODULE_THREAD_STATE* pState = 
     AfxGetModuleThreadState();
 CHandleMap* pMap = pState->m_pmapHWND;
Notice that each handle map is a thread global. This explains a whole category of problems that arise when you try to write multithreaded apps: you can't pass a CWnd object from one thread to another, and MFC advises against it. Sometimes it will work, but in general it fails because, among other things, the AssertValid function for CWnds checks that the CWnd object exists in the (temporary or permanent) handle map associated with the same HWND as m_hWnd. But the CWnd only exists in the thread where it was created, or the thread from which CWnd::FromHandle was called in the case of a temporary object. That CWnd will not exist in the handle map of any other thread, and if you pass it to another thread, the code is sure to crash as soon as it runs into one of the zillions of ASSERT_VALID checks sprinkled throughout MFC.
Figure 4 ViewMaps
Figure 4 ViewMaps

I wrote a program called ViewMaps (see Figures 3 and 4) that dumps the contents of the permanent and temporary window maps to the diagnostic (TRACE) stream. There are two commands, Dump Window Maps and Dump with Toplevel Windows. The first command dumps the contents of the temporary and permanent window maps.
Figure 5 Dump Window Maps
Figure 5 Dump Window Maps

Figure 5 shows the results in my TRACEWIN utility (you can also see them by running under the debugger in Visual C++® 5.0). As expected, the temporary map is empty. That's because whenever an app is sitting idle, there should be no temporary objects—any that might have existed are immediately destroyed on the first idle cycle.
Figure 6 Dump with Toplevel Windows
Figure 6 Dump with Toplevel Windows

The second command works just like the first, except that before dumping the contents of the maps, ViewMaps calls CWnd::GetTopWindow/GetNextWindow to get the CWnd for each top-level child window on the desktop. My program doesn't do anything with these CWnds; all it does is call CWnd::GetNextWindow. But this causes MFC to create a temporary CWnd for each of these windows, and these appear in the dump, as you can see in Figure 6. If you then do an ordinary dump, the temporary objects are gone, proving that temporary objects are, in fact, fleeting.

Have a question about programming in C or C++? Send it to askpd@pobox.com

From the September 1997 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe.

© 1997 Microsoft Corporation. All rights reserved.
Terms of Use
.

© 2016 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy & Cookies
Microsoft