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 > June 1997
June 1997


Code for this article: C++0697.exe (25KB)
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).

Every so often, as most people do, I have a major brain lapse. Unlike most people, I have thousands of readers checking up on me all the time, ready to point out my failings. In my March 1997 "More Fun with MFC" article, after lamenting MFC's single-inheritance class model, I made the following sweeping claim: "Unfortunately, MFC doesn't support multiple inheritance this way. I don't know any framework that does. Most Windows®-based C++ application frameworks are alike in this respect. They all use a single-inheritance model that makes it difficult to write reusable library extensions."
I've made some boo-boos over the years, but this is the worst. As intrepid reader Eddie Diener pointed out: "OWL uses multiple inheritance and you can easily derive from more than one window type to create another type of window. The OWL application framework does this in a number of situations. All the DIB stuff you did for MFC is and has been in OWL for a long time now plus added DIB functionality. That doesn't take away from the value of your work but I thought you should know. Either you are so dedicated to MFC that you know nothing about OWL or you prefer to just ignore it when you make a statement like the one above (it's not Microsoft, after all). Nonetheless I enjoyed the article, but try to open up your eyes to MFC alternatives a little more, even if you make a large part of your living via MFC and Microsoft."
Ouch! It would be forgivable if Eddie were calling my attention to some obscure library from Zippy Tools Corporation with shareware sales of 123 units—but OWL? I wish I could say it was the former of Eddie's suggested explanations, but unfortunately the head-in-the-sand theory is more accurate, and I have only myself to blame. All I can say is, that's what happens when you're under pressure from Acme's Maximum Leader at the same time you're trying to get a column out under deadline. I'll try to do better next time. Also, I owe an apology to Borland—sorry, Philippe! (Just kidding, I do know that Mr. Kahn has left Borland.)
Looking on the bright side, this gives me the perfect segue to explain something I am asked about often: why don't I write about other frameworks besides MFC? In particular, why don't I answer OWL questions?
Since the word Microsoft appears in the title of this magazine, it isn't surprising that I have some bias toward products from Redmond. But my focus on MFC honestly doesn't reflect any big-brother plan for world framework domination. In the ever-growing trend toward specialization, it's hard to be an expert in all things. And the simple fact is that more programmers use MFC. This is not to say MFC is necessarily better! MFC certainly has its problems, which I am always happy to point out. It's too bloated, for one thing. But like it or not, MFC has become the de facto standard for Windows programming in C++. Borland fans will have to look elsewhere—though I hope they'll find useful ideas here, too. Still, I'll try to take Eddie's advice to heart and pull my head out of the sand every now and then.

Q I tried to use your custom caption-painting code from the ShadeCap program in my MDI app, but it doesn't seem to work properly with the MDI child windows. When I switch from my app to another and back the child windows don't get activated, and when I create a maximized child window the title bar flickers. Is this a bug? Is there some way I can fix it?

Jeroen Thur, Gil Rosin, and others

A Yes, it's a bug. Although the code in ShadeCap works wonderfully for SDI apps, merrily painting its caption bar, free from the fulsome flicker, it fails miserably in MDI apps as a number of readers discovered. Well, why didn't you say you wanted it to work for MDI, too? Sheesh, do I have to do everything?

Figure 1: ShadeCap draws a shaded caption
Figure 1: ShadeCap draws a shaded caption

If you recall, ShadeCap handles WM_NCPAINT, WM_NCACTIVATE, and WM_SETTEXT to draw a shaded caption (see Figure 1). It does a lot of hairy stuff to draw the caption in totally flicker-free fashion. Flicker is an issue in Windows if you turn on the feature that draws the window contents dynamically while the user sizes a window (as opposed to the Windows 3.1 way of drawing just a shaded border).
The problem has to do with the Windows 95 default window procedure for MDI apps. Normally, Windows uses a function called DefWindowProc as the window proc, but for MDI apps Windows uses a different function, DefFrameProc. When DefFrameProc gets WM_NCACTIVATE, it activates the active MDI child window's caption as well as the main window caption. Because my OnNcActivate handler in ShadeCap bypasses the installed window proc entirely (in order to work its special magic), it bypasses this important behavior in DefFrameProc in the case of an MDI app.
Not to worry—the fix is simple:
 
 BOOL CMainFrame::OnNcActivate(BOOL bActive)
 {
     CFrameWnd* pActiveFrame = GetActiveFrame();
     if (pActiveFrame!=this) {
         pActiveFrame->SendMessage(WM_NCACTIVATE,
                                  bActive);
         pActiveFrame->SendMessage(WM_NCPAINT);
     }
     .
     . // handle for main frame
     .
 }
CFrameWnd::GetActiveFrame is an MFC function that returns the active frame window. In the case of an SDI app, GetActiveFrame returns "this" (the main frame itself). In the case of a MDI app, GetActiveFrame returns the active MDI child window. Hence, the check for pActiveFrame==this. It's important to send WM_NCACTIVATE and WM_NCPAINT to the child frame before handling WM_NCACTIVATE in the parent. If you do it after, the child frame gets confused about its activated state and doesn't draw properly, something I discovered the hard way.
With the fix in place, ShadeCap activates its MDI child windows properly. But there's another problem that only shows up when the child window is maximized. When DefFrameProc gets a WM_SETTEXT message, it has the annoying habit of appending the child window's title to the main title and then painting it. So if you set the main frame title to ShadeCap and the child window is untitled, you end up with the title "ShadeCap - [untitled]." Since DefFrameProc draws the text as well (even when the window isn't visible), the result is the dreaded flicker as the faulty title appears briefly before your custom title.
In general, the way Windows 95 handles titles is completely messed up. The whole point of having WM_SETTEXT and WM_GETTEXT is to let you override default behavior. Ideally, you should be able to intercept WM_SETTEXT, store your custom title somewhere, and return it to anyone who calls WM_GETTEXT. That would be the "right" way to bypass DefFrameProc. But no, this doesn't work because Windows 95 doesn't use WM_GETTEXT. When it displays your window's title in the task bar or while the user is Alt-Tabbing from app to app, Windows gets the text from some internal character array stored in the WND structure. So you have to set your text there, but because there's no API function to do it you have to send WM_SETTEXT—catch-22.
The only way I can think of to bypass DefFrameProc when setting window text is to call DefWindowProc directly instead of calling CWnd::Default. You have to do it for the MDI child window as well because it does funky caption stuff, too. If overriding message procs to avoid system behavior seems disgusting, it is. You can always switch to Unix. To make it seem less objectionable, I wrote a function, SetInternalWindowText (see PaintCap.cpp in Figure 2), that you can use to set the internal text of any window. It turns off WS_VISIBLE, then calls DefWindowProc, then turns WS_VISIBLE back on again.
Once I had ShadeCap working, I thought "this code is really ugly. I don't want to ever have to look at it again." So I implemented a class, CCaptionPainter (see Figure 2), that encapsulates custom caption painting. Figure 3 shows how ShadeCap uses it. Only the relevant sections appear; you can download the full source code from here.
To use CCaptionPainter, you first instantiate a CCaptionPainter object in your main window class.

 #include "PaintCap.h"
 
 class CMainFrame : public CMDIFrameWnd {
    CCaptionPainter m_capp;
 .
 .
 .
 };
Then, in your OnCreate handler, you must install the caption painter.

 // any value you want
 const UINT WM_PAINTMYCAPTION = WM_USER;
 
 int CMainFrame::OnCreate(LPCREATESTRUCT lpcs)
 {
 .
 . // normal OnCreate stuff
 .
     m_capp.Install(this, WM_PAINTMYCAPTION);
 }
CCaptionPainter::Install takes a pointer to your main window and an integer callback message that CCaptionPainter will send to your window when it's time to paint the caption. CCaptionPainter sends this message with wParam= activated state and lParam = a pointer to a PAINTCAP struct.

 // (in PaintCap.h)
 struct PAINTCAP {
     CDC*    m_pDC;         // DC to draw in
     CSize   m_szCaption;   // size of caption rectangle
     BOOL    m_bActive;     // activated state
 };
PAINTCAP contains the size of the caption rectangle including icon and min/max/close buttons and a device context for you to draw in. CCaptionPainter also has handy member functions to draw the icon and buttons so you don't have to do it manually.

 LRESULT CMainFrame::OnPaintMyCaption(WPARAM bActive, 
                                      LPARAM lParam)
 {
 .
 . // paint caption background
 .
     int cxIcon  = m_capp.DrawIcon(pc);
     int cxButns = m_capp.DrawButtons(pc);
     .
     . // paint title, etc.
     .
 }
CCaptionPainter::DrawIcon draws the app's icon and returns the width in pixels. DrawButtons does the same thing for whichever combination of min/max/close buttons your app uses. It even knows about the new WS_EX_CONTEXTHELP button. Typically, you'll paint the caption background first—for example, ShadeCap's shaded caption—then call DrawIcon and DrawButtons to draw the icon and buttons, then draw the text or whatever else you want. You don't have to worry about flicker because CCaptionPainter gives you a memory DC. When you're finished drawing, CCaptionPainter blasts the bits to the screen. CCaptionPainter even maintains bitmaps for the active/inactive captions (as ShadeCap did), so it only sends your window a paint message when the user has actually changed the size of the caption.
For MDI apps, you have to install a caption painter in your MDI child window class, too. That's so CCaptionPainter will bypass the Windows 95 default message proc for MDI child windows, DefMDIChildProc, when handling WM_SETTEXT. In ShadeCap, CMyMDIChildWnd installs a caption painter with zero as the callback message. If the callback message is zero, CCaptionPainter paints a normal caption. In theory, you could use the same callback message or a different one for your MDI child windows, too, so that both the frame and child windows would have custom captions—but I've never actually tried it.
If you want to do custom caption buttons, you'll have to handle the non-client mouse messages yourself; CCaptionPainter doesn't do mouse messages. It does tell you when the user has changed colors or fonts. When Windows sends WM_SYSCOLORCHANGE or WM_SETTINGCHANGE, CCaptionPainter passes the event to your frame window in the form of a callback message with wParam = 1. This is your cue to regenerate any fonts or brushes you may have hanging around.
If you're wondering how CCaptionPainter achieves all this magic, it uses the CMsgHook message-hooking class from my March 1997 "More Fun with MFC" article (with the mea culpa error in it). I have since renamed CMsgHook to CSubclassWnd since that's what it really does: it lets you subclass an MFC CWnd object multiple times. CSubclassWnd works by inserting a message proc in front of the one MFC uses. If you want the full poop read the MFC article, but close your eyes when you get to the part about multiple inheritance and other frameworks.
Once I had CMsgHook/CSubclassWnd, implementing CCaptionPainter was pretty simple. I just moved all the code from CMainFrame to CCaptionPainter and edited it slightly to invoke CWnd functions through CSubclassWnd:: m_pWndHooked instead of the implicit this pointer. CSubclassWnd is not derived from CWnd; rather, it stores a pointer to the subclassed window in a data member, m_pWndHooked. Since CMsgHook/CSubclassWnd appeared in the article, it doesn't appear in Figure 2). As always, full source code is available from the link at the top of this article.
CCaptionPainter reduces custom captions to the thing that really matters—actually painting the caption. You don't have to handle WM_NCPAINT, WM_NCACTIVATE, WM_SETTEXT, or any of that. Just install the object and handle your own, private paint-caption message. It's the way WM_NCPAINT should have worked if it were designed properly. Thanks to Gil and Jeroen for helping me find and fix the bugs in ShadeCap.

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

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

© 1997 Microsoft Corporation. All rights reserved. Legal Notices.

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