This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


June 1999

Microsoft Systems Journal Homepage

C++ Q&A

Code for this article: June99CQA.exe (3KB)

Paul DiLascia is the author of Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at askpd@pobox.com or http://pobox.com/~askpd.

Q I have been reading MSJ to unravel how MFC encapsulates the Win32® SDK, but I am not sure where AfxWndProc becomes the default window procedure. I was able to trace into AfxEndDeferRegisterClass (see Figure 1), but here WndProc is set to DefWindowProc instead of AfxWndProc. Am I missing something?
Ravi Tolani

A Yes, you're missing something. AfxEndDeferRegisterClass is not where MFC sets the window proc. So where does it? The answer is a little confusing, and there's more than one place it can happen. It's crucial to understand how and why MFC sets up its own window proc since this AfxWndProc lies at the heart of MFC. The entire message map mechanism whereby MFC routes window messages (such as WM_SETFOCUS) to your handler functions (like OnSetFocus) depends on subclassing each MFC-controlled window with the MFC universal window proc, AfxWndProc. So forgive me while I digress a bit before answering your question.

    You can think of AfxWndProc as a function with a big switch statement that routes WM_XXX messages to your window class's OnXXX handler functions. This is a first-order approximation of how AfxWndProc works:


 // This is a gross simplification
 LRESULT AfxWndProc(HWND hwnd, UINT msg, ...)
 {
     CWnd* pWnd = CWnd::FromHandle(hwnd);
     switch (msg) {
     case WM_CREATE:
         pWnd->OnCreate(...);
         return 0;
     case WM_SETFOCUS:
         pWnd->OnSetFocus(...);
         return 0;
 .
 .
 .
     // etc.
     }
     return 0L;
 }
      The true picture is, of course, much more complicated. For one thing, AfxWndProc doesn't do the dispatching. It calls another function, AfxCallWndProc, which calls CWnd:: WindowProc, which calls OnWndMsg, which does the dispatch—unless the message is WM_COMMAND or WM_ NOTIFY, in which case OnWndMsg calls OnCommand or OnNotify, both of which call CCmdTarget::OnCmdMsg to do the dispatch. Furthermore, MFC does not switch on the message ID (WM_CREATE, WM_SETFOCUS, and so on), but rather on the signature of the message handler function. (I told you it was complicated!)

    When OnWndMsg gets a message, it searches your window object's message map for an entry with a message ID that matches the received message. Suppose your message map has an ON_WM_SETFOCUS entry. ON_WM_ SETFOCUS is a macro that generates an entry in your message map.


 #define ON_WM_SETFOCUS() \
     { WM_SETFOCUS, 0, 0, 0, \
         AfxSig_vW, &OnSetFocus },
Among the members in the message map entry is the special code AfxSig_vW that identifies the signature of the handler function. In this case, OnSetFocus takes a CWnd pointer (W) and returns void (v)—hence AfxSig_vW. OnWndMsg uses this code to dispatch to the handler function:

 switch (nSig) { // nSig = signature code
 case AfxSig_vW:
     (*pEntry->m_pfn)(CWnd::FromHandle((HWND)wParam));
     break;
Here, pEntry->m_pfn is your handler function (for example, OnSetFocus). Other message map entries will have different signature codes. For example, ON_WM_PAINT and ON_WM_NCPAINT generate message map entries with AfxSig_vv (the handler function has no arguments and returns void), whereas ON_WM_ENDSESSION generates an entry with AfxSig_vb: BOOL argument returning void:

 void OnEndSession(BOOL bEnding);
      Why does MFC dispatch on the signature code instead of the message ID? Because one of the things MFC must do before calling your handler function is package its arguments—that is, convert WPARAM and LPARAM into more typesafe objects such as CWnd* or BOOL. It's more efficient to lump together all handler functions with a given signature so MFC can convert the arguments before calling the handler function. There are about 50 or 55 different codes defined in afxmsg_.h (see Figure 2).

    It's important to understand how MFC dispatches WM_XXX messages to your handler functions—particularly when you find yourself immersed in debugging—but it's equally important to realize that the message-routing code is universal for all window classes. MFC uses the same AfxWndProc for all window objects.

    Which leads to your original question: how does MFC subclass your window? That is, how does it get Windows® to use AfxWndProc? There are several ways it can happen, but for most purposes there are only two, depending on whether you create the window from scratch or by subclassing an existing window such as a dialog control. If you start from scratch, you create your window by calling CWnd::Create or some overloaded version, such as CView::Create or CButton::Create. Or—as in the case of views—MFC calls Create for you. In all these cases, control eventually arrives at CWnd::CreateEx.


 // simplified
 BOOL CWnd::CreateEx(/* lots of args */)
 {
     CREATESTRUCT cs;
     // initialize cs from args
     AfxHookWindowCreate(this);
     HWND hWnd = ::CreateWindowEx(/* lots of args */);
     AfxUnhookWindowCreate();
     if (hWnd == NULL) return FALSE;
     return TRUE;
 }
      Before calling ::CreateWindowEx to create the window, MFC calls AfxHookWindowCreate; and after creating the window, it calls AfxUnhookWindowCreate. These functions set up and remove a WH_CBT (computer-based training) hook. Recall from Windows 202 (one semester beyond Windows 101) that a hook is an application callback function that Windows calls whenever certain events happen. There are several kinds of hooks; AfxHookWindowCreate uses a WH_CBT hook because, among other events, Windows calls it whenever creating or destroying a window.

    Figure 3 shows how AfxHookWindowCreate sets up the WH_CBT hook. MFC installs a hook function, _AfxCbtFilterHook, then sets a thread-global variable m_pWndInit, which points to the CWnd object being created or hooked. MFC uses a global because the Windows hook mechanism doesn't provide any way of passing an argument to the hook function.

    So far, so good. Once the hook is set up, control passes into CWnd::CreateEx, which calls ::CreateWindowEx, a black box from MFC's point of view. Windows creates the window, la-di-dah. But immediately afterward, Windows looks at its internal structures, scratches its weary head, and says, "Oh, this app has a WH_CBT hook installed. I'd better call the callback." So Windows calls _AfxCbtFilterHook, passing information about the window being created. _AfxCbtFilterHook is really quite messy—not the kind of thing to read if you can avoid it. Figure 4 shows a simplified version.

    I've omitted some gnarly DLL stuff to highlight the main points. _AfxCbtFilterHook attaches the HWND (sets m_hWnd) to the window object being created (pWndInit), then subclasses the window by installing the generic AfxWndProc. CWnd::GetSuperWndProcAddr returns an address where the old window proc can be saved. This virtual function has a simple default implementation:


 WNDPROC* CWnd::GetSuperWndProcAddr()
 {
     return &m_pfnSuper;
 }
      In earlier releases of MFC, you had to supply this function yourself when creating new window classes because the MFC storage police were too stingy to allocate four bytes of m_pfnSuper. Admittedly, it's a waste to have m_pfnSuper in every window object when you need only one instance for the entire class—but what the heck, the Redmondtonians decided to put m_pfnSuper in CWnd so you wouldn't have to bother with GetSuperWndProcAddr every time you define a new class. After saving the old window proc, MFC calls PreSubclassWindow, another CWnd virtual function you can override if you want to do stuff just before the window is subclassed. Why not?

    Finally, MFC installs AfxWndProc as the window proc—that's the answer to your question. But astute readers have already noticed that _AfxCbtFilterHook doesn't use AfxWndProc directly as the window proc; it calls a function AfxGetAfxWndProc to get the window proc.


 WNDPROC AFXAPI AfxGetAfxWndProc()
 {
 #ifdef _AFXDLL
     return AfxGetModuleState()->m_pfnAfxWndProc;
 #else
     return &AfxWndProc;
 #endif
 }
In a non-DLL app, AfxGetAfxWndProc simply returns AfxWndProc, but in a DLL the window proc is stored in the module state. That's because the DLL must use the window proc of whichever application is calling it at any given time, not its own.

    Once _AfxCbtFilterHook is finished, the window is hooked up. Control flows out of _AfxCbtFilterHook, out of the black box ::CreateWindowEx, and back to CWnd::CreateEx, which now calls AfxUnhookWindowCreate to remove the CBT hook. The raison d'etre of the CBT hook is to trap the window's creation so MFC can subclass the window—that is, install AfxWndProc.

    Why does MFC go through such contortions? Why not just set the window proc in WNDCLASS::lpfnWndProc when the window class is registered, the way any Windows programmer who read Petzold would do it? Good question. There are two reasons. First, the install-a-hook method works for window classes that are not yours or MFC's—such as the built-in classes Button, ListBox, ToolbarWindow32, and so on—as well as window classes that belong to third-party libraries. OK, so why not just call SetWindowLong to subclass the window (change the window proc) after the window is created? Because—and this is the real reason MFC must set a hook—in that case your window object would never get a chance to handle WM_CREATE and WM_NCCREATE. These messages are sent by Windows itself, from within ::CreateWindowEx when Windows is creating the window.
       Remember when I said ::CreateWindowEx is a black box to MFC? Once MFC calls CreateWindowEx, it can only wait until control returns. So in order to handle messages like WM_NCCREATE, MFC must hook up your window before it calls CreateWindowEx. But how can MFC do that when it doesn't know the HWND until CreateWindowEx returns. Catch-22. The only way out of this conundrum is to set a hook. Windows calls the CBT hook after it's created the window (so the hook function gets a valid HWND), but before it sends any messages to the window proc. Whew!

    If you're still with me, relax. The hard part is over. Earlier I said that there are two ways MFC can subclass a window. The first is when you call some Create function as described previously; the second happens when you call SubclassWindow or SubclassDlgItem. Fortunately, things are much simpler in this case because the window already exists. SubclassWindow and SubclassDlgItem subclass the window directly by calling ::SetWindowLong with GWL_WNDPROC, without using any hook subterfuge.

    There are other ways MFC can subclass a window (for example, the common dialogs require special handling), but generally they're all just variations on the set-a-hook game that I described. If you can wrap your head around that, you'll have no trouble understanding the variations. The important things to understand are why MFC needs to subclass each window with AfxWndProc in the first place (to map WM_XXX messages to your OnXXX handler functions), and how MFC uses a CBT hook to install AfxWndProc before Windows sends any messages to the window, so your app can handle messages like WM_CREATE and WM_NCCREATE. If you understand these two points well enough to explain them to your grandmother while simultaneously juggling three beanbags and a Tinky Winky doll, you're more than halfway to becoming a certified MFC guru.

Q In the Flyby program (MSJ, October and December 1998), you use the API function GetCursorPos to retrieve the position of the cursor (mouse). How likely is it that the point yielded by this function really reflects the original position of the cursor?

    For example, say a mouse move triggers your OnLButtonDown, which calls CMyView::DoHighlight, which eventually calls ::GetCursorPos. Let's assume that the OS suspends the process after the mouse message was dispatched, but before the call to GetCursorPos, and that the PC is slow and the user is quick. When the process is resumed, could it be possible that the mouse is not positioned at the same place any more?

    You can simulate this scenario by setting a breakpoint on CMouse::GetPos. After pressing F10, the mouse is no longer at the same position. In Flyby, this is harmless because all you're doing is highlighting a hot spot, but imagine what could happen when you use GetCursorPos handling a CTreeCtrl that stores a directory tree. Getting the wrong position would cause a disaster when you delete the wrong file or directory! So is there a way to get the exact mouse position that was valid when a certain Windows message was triggered?

Franz Resenicek
Austria

A Good point! Perhaps no user in the world can move a mouse that fast under normal conditions—but hey, it could happen! And in the old days of Windows 3.1, it may have been more likely. This is why Windows does, in fact, provide a way to get the mouse coordinates at the time any given message was sent.

    Normally, a message arrives at your app when Windows calls your app's window proc. This callback function receives the HWND, message ID, WPARAM, and LPARAM. But actually, it's a little more complicated than that. Your app doesn't just get messages like a thunderbolt from the blue; it must fetch them by calling ::GetMessage. That's why 16-bit Windows (Windows 3.1) is called a cooperative, nonpreemptive multitasking operating system. In those days this held true globally; nowadays we know 32-bit versions of Windows are true (preemptive) multitasking systems. Nevertheless, within a process or thread, you still must call ::GetMessage and ::DispatchMessage to process messages in an orderly, synchronized fashion. Most every process or User Interface thread contains a central Get/Dispatch loop. This loop is called the message pump.

    GetMessage retrieves the next message in the queue by filling a special MSG structure.


 struct MSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
 };
The first four members are the familiar window proc arguments; MSG.time is (what else?) the time the message was sent, and MSG.pt holds the mouse position in screen coordinates when the message was sent.

    MSG.pt is just what you want. The only problem is, how do you get it? When an app (MFC or otherwise) calls ::DispatchMessage to dispatch the message, Windows calls the appropriate window proc with hwnd, message, wParam, and lParam. The time and pt get lost in the shuffle. In a C app, you could make the MSG a global, so your window proc and message handler functions could access it. But with MFC, the message pump is buried in the framework, in CWinThread::PumpMessage. So how can you get the rest of the information in the MSG?

    Fortunately, CWinThread retrieves the MSG into a CWinThread data member, m_curMsg. You can get the MSG from this public member, but before you go racing to the keyboard, there's an easier way: just call ::GetMessagePos. (There's also ::GetMessageTime to get the time.) GetMessagePos returns the position of the cursor when the last message was retrieved using GetMessage.

    Just remember: not all messages are queued. When you or someone else calls SendMessage to send a message to a window, Windows calls the window's window proc directly—without queueing the message. Only truly external events such as user input (mouse or keyboard), hardware interrupts, and messages sent with ::PostMessage are queued. This is generally not a problem, since you are usually not interested in knowing the mouse position or time when some part of your app called SendMessage. (What meaning could it have?) Rather, you're usually interested in the mouse position or time when some real event occurred such as a mouse click or keystroke.

    MFC itself uses GetMessageTime and GetMessagePos to implement a function you can use to get the current MSG: CWnd::GetCurrentMessage.


 const MSG* PASCAL CWnd::GetCurrentMessage()
 {
     // fill in time and position when asked for
     _AFX_THREAD_STATE* pThreadState = 
         _afxThreadState.GetData();
     pThreadState->m_lastSentMsg.time = 
         ::GetMessageTime();
     pThreadState->m_lastSentMsg.pt = 
         CPoint(::GetMessagePos());
     return &pThreadState->m_lastSentMsg;
 }
All messages sent—and now I mean sent, not queued—to your window go through AfxCallWndProc (see the previous question). This function remembers the current message in the thread state, in a m_lastSentMsg member.

 LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, 
     HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
 {
   _AFX_THREAD_STATE* pThreadState =  
   _afxThreadState.GetData();
   MSG oldState = pThreadState->m_lastSentMsg;   
   // save for nesting
   pThreadState->m_lastSentMsg.hwnd = hWnd;
   pThreadState->m_lastSentMsg.message = nMsg;
   pThreadState->m_lastSentMsg.wParam = wParam;
   pThreadState->m_lastSentMsg.lParam = lParam;
     .
     .
     .
   // process message
   pThreadState->m_lastSentMsg = oldState;
   return lResult;
 }
      GetCurrentMessage simply returns a pointer to m_lastSentMsg (const, so you can't munge it!) after updating the time and pt fields with GetMessageTime and GetMessagePos. AfxCallWndProc doesn't bother to do this because it's a waste of CPU cycles. Very rarely do you care what the message time and cursor position are, so why copy those extra bytes for every message sent? If you need these values, you can call GetCurrentMessage. But once again you have to be careful. GetCurrentMessage returns the current message—whether queued or sent; if for some reason you want to know what the currently queued message was, you have to look in CWinThread::m_curMsg. In either case, the time and pt fields will be the same, since GetMessageTime and GetMessagePos return their respective values for the last message retrieved with GetMessage—that is, the last queued message.

    If all that seems confusing, it is. The short answer is: call ::GetMessagePos or CWnd::GetMessage, whichever tickles your fancy. Ta-ta!

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


From the June 1999 issue of Microsoft Systems Journal.