A Simple Way to Enable a Windows XP Look and Feel for VC++ 6.0 MFC Applications

Environment: VC6 SP5, Windows 2000 SP2, and Windows XP SP1

The main reason for writing this article is that Visual C++ 6.0 wizards-generated MFC applications don't use the XP look and feel controls if you run them under XP. If you are using Microsoft Visual C++.NET, that is not an issue. I had a goal to develop an application running under any Microsoft 32-bit OS and use the new UI look and feel if it runs under XP.

The solution is very simple. All you need to do is add a custom resource to the project and add a couple of lines to the InitInstance method of the CWinApp derived class.

1. Create a Manifest File

Microsoft has introduced a new type of resource called Manifest. Well, technically it's not a type of resource; it is just an XML file included to the app as a custom resource with ID=1, which describes the application and its dependencies. If an executable file contains this resource, Windows XP will identify it and force the application to use the specific versions of libraries. Our goal is to enforce usage of new Windows Common Controls library (version 6).

Here is an example of a simple manifest file that I've used to solve this task:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
                 manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="X86"
    name="Microsoft.Windows.YourApplication"
    type="win32"
/>
<description>YourApplication</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="X86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
</assembly>

Create a file with its contents as shown above in the project resources folder (res by default). Replace YourApplication with an appropriate name. That should do it for our case. For further information about manifest files, see the MSDN for Visual Studio .NET.

2. Add a Resource to the .rc File

First, let's add two lines to the resource.h file. Just copy and paste the following:

#define IDR_MANIFEST  1
#define RT_MANIFEST  24

Now, open the application custom resource file. Usually, it's located in the res directory; the default extention is .rc2. Manually add the following line:

// Add manually edited resources here...
IDR_MANIFEST RT_MANIFEST MOVEABLE PURE
             "res\\ApplicationManifestXMLFile"

Replace ApplicationManifestXMLFile with the actual file name.

3. Modify the InitInstance Method

It's really simple. Just copy and paste two calls at the beginning of the InitInstance method:

BOOL MoneyApp::InitInstance()
{
  InitCommonControls();    // initialize common control library
  CWinApp::InitInstance(); // call parent class method

#ifdef _AFXDLL
  Enable3dControls();      // Call this when using MFC in a
                           // shared DLL
#else
  Enable3dControlsStatic(); // Call this when linking to MFC
                            // statically
#endif

  // the rest of the code
}

4. Conclusion

This is it. No magic, but it works. You've got all the dialog controls, menus, and toolbars looking like their brothers and sisters in native XP applications. You can still develop under Windows 2000, but now I know that the UI of your applications will have a nice look under XP.

These screenshots illustrate how the same application looks under Windows 2000 and Windows XP:

Running under Windows 2000
Running under Windows XP before the changes
Running under Windows XP after the changes

If you have any questions or ideas about this article, please drop me a couple of lines at alex@simanov.com.

Alexander Simanov

Downloads

I've created a simple project (MFC dialog-based), demonstrating this technique.
Download demo project - 20 Kb.



Comments

  • Great job

    Posted by softhor on 12/06/2006 05:57pm

    Thanks a lot for your efforts. This is the simplest way to give old applications a new look.

    Reply
  • Not working with...

    Posted by Promotional Engine on 09/17/2006 11:18pm

    When using function 'CreateDialogIndirect' to show a dialogbox, the old-style buttons will be shown.

    Reply
  • Way to Cool

    Posted by Charlie Curtis on 04/18/2006 11:17am

    Easy to follow instruction and it works! Excellent article. Made my application look so much better... Thanks Charlie

    Reply
  • Crashes with stingray grid controls

    Posted by Dave McLelland on 08/03/2005 09:11am

    With reference to Shail Srivastav's post, I have exactly the same problem, but don't know how to fix it. Can anyone enlighten me?

    Reply
  • workaround for ownerdraw buttons

    Posted by bob42 on 07/29/2005 03:11am

    If you put an ownerdraw button onto a dialog, add that dialog to your window, register a callback with the dialog and dynamically update your button (add an image etc) it works in most cases. However, I still don't get the hang of all the custom window classes registered in this legacy code I'm working on. Sometimes adding dynamic buttons (without the above described dialog procedure) just works, sometimes it doesn't. Maybe someone knows a good primer on how low-level event handling works on Windows?

    • got it, it's so pathetic

      Posted by bob42 on 07/29/2005 04:43am

      If you use two dialogs from your resources, and the two overlap, say a left dialog and a right dialog, and they are being created from left to right, then the left one may be below the right one. If you put a button on the right dialog though, on the very left, its event handler is never called. Sheesh!!

      Reply
    Reply
  • workaround for ownerdraw buttons

    Posted by bob42 on 07/28/2005 11:37am

    If you put an ownerdraw button onto a dialog, add that dialog to your window, register a callback with the dialog and dynamically update your button (add an image etc) it works in most cases. However, I still don't get the hang of all the custom window classes registered in this legacy code I'm working on. Sometimes adding dynamic buttons (without the above described dialog procedure) just works, sometimes it doesn't. Maybe someone knows a good primer on how low-level event handling works on Windows?

    Reply
  • crashes with stingray grid controls.

    Posted by shail76 on 08/11/2004 02:41pm

    Its works great with MFC contols, but crashes with stingray grid controls. 
    I am trying to fix this issue. If someone knows about fix, please let me know. 
    My e-mail is shailsrivastav@hotmail.com
    
    Here is the stack and function
    
    _free_dbg_lk(void * 0x048edfb0, int 0x00000001) line 1066 + 60 bytes
    _free_dbg(void * 0x048edfb0, int 0x00000001) line 1001 + 13 bytes
    operator delete(void * 0x048edfb0) line 351 + 12 bytes
    CString::FreeData() line 146 + 15 bytes
    CString::~CString() line 213
    CGXEditControl::CalcTextPosUnderPt(CPoint {x=0x0000027a y=0x00000040}) line 1698 + 33 bytes
    CGXEditControl::LButtonUp(unsigned int 0x00000000, CPoint {x=0x0000027a y=0x00000040}, unsigned int 0x00000051) line 628 + 19 bytes
    CGXGridCore::OnLButtonHitRowCol(unsigned long 0x00000001, unsigned long 0x00000006, unsigned long 0x00000001, unsigned long 0x00000006, CPoint {x=0x0000027a y=0x00000040}, unsigned int 0x00000000, unsigned short 0x0051) line 1985
    CGXGridCore::DoLButtonUp(unsigned int 0x00000000, CPoint {x=0x0000027a y=0x00000040}) line 1080
    CGXGridView::OnLButtonUp(unsigned int 0x00000000, CPoint {x=0x0000027a y=0x00000040}) line 450 + 30 bytes
    CWnd::OnWndMsg(unsigned int 0x00000202, unsigned int 0x00000000, long 0x0040027a, long * 0x0012fcac) line 1964
    CWnd::WindowProc(unsigned int 0x00000202, unsigned int 0x00000000, long 0x0040027a) line 1585 + 30 bytes
    CGXGridView::WindowProc(unsigned int 0x00000202, unsigned int 0x00000000, long 0x0040027a) line 333
    AfxCallWndProc(CWnd * 0x04754510 {CCSIPartTypeLibView hWnd=???}, HWND__ * 0x0028045c, unsigned int 0x00000202, unsigned int 0x00000000, long 0x0040027a) line 215 + 26 bytes
    AfxWndProc(HWND__ * 0x0028045c, unsigned int 0x00000202, unsigned int 0x00000000, long 0x0040027a) line 368
    
    
    
    _CRTIMP void __cdecl _free_dbg(
    
    #endif  /* _MT */
    
            void * pUserData,
            int nBlockUse
            )
    {
            _CrtMemBlockHeader * pHead;
    
            /* verify heap before freeing */
            if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF)
                _ASSERTE(_CrtCheckMemory());
    
            if (pUserData == NULL)
                return;
    
            /* forced failure */
            if (!(*_pfnAllocHook)(_HOOK_FREE, pUserData, 0, nBlockUse, 0L, NULL, 0))
            {
                _RPT0(_CRT_WARN, "Client hook free failure.\n");
    
                return;
            }
    
            /*
             * If this ASSERT fails, a bad pointer has been passed in. It may be
             * totally bogus, or it may have been allocated from another heap.
             * The pointer MUST come from the 'local' heap.
             */
            _ASSERTE(_CrtIsValidHeapPointer(pUserData));
    
            /* get a pointer to memory block header */
            pHead = pHdr(pUserData);
    
            /* verify block type */
            _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
    
            /* if we didn't already check entire heap, at least check this object */
            if (!(_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF))
            {
                /* check no-mans-land gaps */
                if (!CheckBytes(pHead->gap, _bNoMansLandFill, nNoMansLandSize))
                    _RPT3(_CRT_ERROR, "DAMAGE: before %hs block (#%d) at 0x%08X.\n",
                        szBlockUseName[_BLOCK_TYPE(pHead->nBlockUse)],
                        pHead->lRequest,
                        (BYTE *) pbData(pHead));
    
                if (!CheckBytes(pbData(pHead) + pHead->nDataSize, _bNoMansLandFill, nNoMansLandSize))
                    _RPT3(_CRT_ERROR, "DAMAGE: after %hs block (#%d) at 0x%08X.\n",
                        szBlockUseName[_BLOCK_TYPE(pHead->nBlockUse)],
                        pHead->lRequest,
                        (BYTE *) pbData(pHead));
            }
    
    
    
    -shail srivastav

    • RE: crashes with stingray grid controls.

      Posted by Dave McLelland on 07/30/2005 05:04am

      What was the problem with the stingray grid. Im modifying an app with Stingray grid 7.01 and have the same problem when I add a manifest.

      Reply
    • I think the solution is easy

      Posted by turkinz on 02/13/2005 04:45pm

      Just read the original docs here: Using XP Visual Styles 1 and this one here: Using XP VS -2 and you'll know why it crashes.

      Reply
    Reply
  • So Cool

    Posted by Legacy on 01/24/2004 12:00am

    Originally posted by: Ian

    That really great work. It makes my application give fancy look. That's appriciable job. On the ohter hand I see one disadvantage as well. It makes my application slow in showing mutiples dialogs. It takes considerable time when I switch one panel to other with a lot of controls on them.
    It looks like it makes my application slow. Is there any remidy to this problem???

    Reply
  • Fix for static wnds on tabs

    Posted by Legacy on 01/12/2004 12:00am

    Originally posted by: awinter

    I like this code, it is really usefull. The only problem
    I have encountered is the background of a static wnd when
    it is placed on a tab wnd. The problem is caused by the
    fact that the tab window has a gradient background, so
    having a static wnd with a solid back color doesn't work.
    The solution is simple, grab the region of the tab wnd
    where the static wnd will be located and use this to paint
    the background of the static wnd.

    Enjoy!

    Reply
  • Simpler way of doing it without changing sourcecode !

    Posted by Legacy on 12/10/2003 12:00am

    Originally posted by: Geno Carman

    Simply import a generic (not even the app name is needed) manifest file as a custom resource of type "24".  Give it an ID that of 1 (IDR_MANIFEST=1).  Relink and you have XP style controls!
    
    

    Generic manifest file name is "Generic.exe.manifest"
    Generic manifest source:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?><assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><assemblyIdentity version="1.0.0.0" processorArchitecture="x86" name="Generic" type="win32" /> <description> Generic</description><dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly></dependency></assembly>

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds