Reducing the Size of Statically-linked MFC Applications in VC11

Reducing the Size of Statically-linked MFC Applications in VC11

Rate This
  • Comments 12

Pat Brenner

Hello, I’m Pat Brenner, a developer on the Visual C++ Libraries team, and I am the primary developer working on the Microsoft Foundation Classes (MFC).

In Visual Studio 2010, the size of statically-linked MFC applications grew substantially. We’ve gotten a number of comments about this issue, so I wanted to post an article about the cause and the solution that we have come up with.

Cause

In Visual Studio 2010, we added a feature to the resource editor which allows you to add MFC controls to your dialogs. The MFC control types appear in the toolbox along with the standard Windows controls. Properties specific to the MFC controls can be set on them, so they behave as desired when the dialog is created.

In order for this to work properly, a DLGINIT block has to be written in the RC file for the project, which contains the properties information in binary format. The DLGINIT block has to be parsed when the dialog is being initialized, so the controls can be initialized using the information in the DLGINIT block. The code to do this parsing lives in CWnd::ExecuteDlgInit. ExecuteDlgInit method lives in WINCORE.CPP, whose object is always included in every statically-linked MFC application (because it contains the CWnd constructors and the AfxWndProc method).

The code that performs the MFC control initialization, of course, needs to know about all of the MFC controls. Those controls, in turn, may need to know about various visual managers in order to know how to draw themselves. And the visual managers, in turn, have dependencies on other MFC classes.

The result of these dependencies is that much more of MFC needs to be pulled into a statically-linked MFC application, because the linker cannot determine at build time that none of those methods will need to be called, since it all depends on the content of the RC file and DLGINIT structures inside it.

We were alerted to this size increase in statically-linked MFC applications shortly before the release of Visual Studio 2010 RTM, but we were not able to definitively establish the cause before Visual Studio 2010 shipped. Even if we had, we most likely would not have been able to put the finishing touches on a solution before the release date, because we had to try several different approaches before arriving at a working solution that puts a very small requirement on the MFC developer.

Solution

To fix the problem, we eliminated a number of dependencies between MFC classes (further details are below). We also moved several methods that have an effect on the MFC control initialization:

  • CWnd::ExecuteDlgInit, DDX_Control, AfxRegisterMFCCtrlClasses
  • CMFCControlContainer::SubclassDlgControls and CMFCControlContainer::PreUnsubclassControl

into separate source modules.

These separate source modules are then compiled in two different ways:

  • With _AFX_NO_MFC_CONTROLS_IN_DIALOGS not #defined, they are built into the standard static MFC libraries, NAFXCW[d].LIB and UAFXCW[d].LIB, with the standard behavior enabled.
  • With _AFX_NO_MFC_CONTROLS_IN_DIALOGS #defined, they are built into a new small static MFC library, AFXNMCD[d].LIB, without the ability to initialize MFC controls on dialogs. (The NMCD in the library name is an acronym for “No MFC Controls on Dialogs”.)

The new smaller library has the same methods (same names, but different implementations) as the larger standard MFC libraries, so we must make sure to link it in first. This ensures that the functions that don’t have any dependency on MFC control initialization are used and the dependencies are eliminated. This is accomplished via symbols that are defined in the new source modules, and force-included via #pragma statements in AFX.H based on #defines set.

The result of this work is that you can simply #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS in your MFC application’s stdafx.h file, and all the code that performs MFC control initialization on dialogs will be left out of your application. In a simple dialog-based application, this will reduce the size of the application by approximately 80%. [Note that if you do use MFC controls on a dialog, and build with _AFX_NO_MFC_CONTROLS_IN_DIALOGS #defined, your application may not run at all (or dialogs will not appear) because a dialog containing a nonexistent window class cannot be created. We added TRACE statements to MFC to this effect to help point out this issue.]

In addition, we have made changes in the code generated by the MFC application wizard. It will generate code that contains #ifdefs for the _AFX_NO_MFC_CONTROLS_IN_DIALOGS, so:

  • Dialogs will be derived from CDialog instead of CDialogEx if the #define is set.
  • No CShellManager will be created in the application’s InitInstance method if the #define is set.

We have implemented these changes in MFC for the next major release of Visual Studio. Now that we understand the cause and the best solution, we looked at the possibility of porting the changes back to Visual Studio 2010 in order to benefit applications built with that version. Unfortunately, the changes we made to reduce the dependencies between MFC classes included:

  1. Moving D2D-related member functions/data out of the _AFX_GLOBAL_DATA class to a separate class
  2. Adding a new virtual method to both CMDIChildWnd and CMDIChildWndEx
  3. Adding a new method to the CWinApp class

Because these changes introduce binary incompatibilities, we are not able to port the changes back to Visual Studio 2010 without breaking existing MFC applications.

I hope you find this information helpful!

 

 

Pat Brenner
Visual C++ Libraries Development

  • Is this the same as the problem that started with the VS2008 update which added the Ribbon etc. to MFC?

    My simple MFC dialog app became 5x larger with that update, unrelated to anything in VS2010 as far as I can tell.

  • Hi Pat,

    its really great news that the MFC got a bit of attention again. The last post must be months (feels like years) in the past ;-)

    I think many developers will appreciate that the bloated code libraries which were introduced in VS 2010 can now be removed and for those using not the internal MFC UI classes but the ones from a well known competitor get finally again smaller executable files.

    Would it be possible to deploy a hotfix or patch for VS 2010, otherwise tagetting Windows XP in an MFC app using the MFC in VS2010 will always be code bloated. As Visual Studio 11 won't support Windows XP as target operating system. For a hotfix or patch each developrt could decide to install and therefore is aware of the code breaking changes.

    I wanted to know if there is any update about the MFC ARM compiler configuration support for Windows 8 ?

    The BUILD videos and webcasts present the windows 8 desktop on an ARM tablet, does this mean libraries like GDI etc...were already ported to ARM? Otherwise how would this video have been possible then? And if those are ported to ARM I think the step to port the MFC to ARM would be a very small one.

    Wouldn't it be enough to simply port the CRT to ARM and then simply rebuild the MFC sources based on the ARM CRT ?

    It would be a great opportunity for MFC developers to be able to target x86, x64 and ARM with their desktop applications. A lot of MFC ported to ARM is already available in the old Windows CE MFC code base.

    Imagine developing a business desktop app which your customer can then use on his normal desktop computer and seaminglessly on his ARM tablet using Windows 8! That would even push windows 8 tablets a top of Android or iOS, because of the countless companies using Windows as their main OS and using their beloved native desktop applications on their ARM tablets.

    I see this as a great opportunity for establishing Windows-tablets especially in companies.

    Greetings, Moritz

  • I'm confused what is an MFC control?  How can I differentiate them from non-MFC controls like RichEdit?  What if I start my application with _AFX_NO_MFC_CONTROLS_IN_DIALOGS defined but later need to use an MFC control (whatever that is) so then I remove this #define.  Do I manually go back and derive my dialogs from CDialogEx and create the CShellManager?  Or is it an AppWizard setting that all bets are off if I want to change it and I need to recreate a new project?  It would have been better to segment this like the rest of MFC - leave functionality out until it's needed, then bring it (and the bloat in) automatically without diddling with any #define.  But thanks for addressing the bloat as best you can.

  • Hi all, let me answer some of the questions that have been posted so far.

    @David Ching: the "MFC controls" I'm referring to are listed with "MFC" prepended in the toolbox when you are working on a dialog in the dialog editor.

    A partial list is: MFC Button Control, MFC FontComboBox Control, MFC PropertyGrid Control, MFC ShellList Control, MFC ShellTree Control.

    And no, you don't have to re-create your project from scratch if you change your mind about using MFC controls on your dialog.  You can switch your dialogs to derive from CDialogEx and add the CShellManager creation and you should be fine.

    The trouble with your "segmentation" suggestion is that much of MFC is more tightly interrelated than it used to be, because of visual managers and other classes.

    @Moritz Leutenecker: As I mentioned in the original post, it is not possible to deploy a hotfix for Visual Studio 2010, because the changes we had to make would introduce binary incompatibilities and all MFC applications would have to be rebuilt.

    @Leo Davidson: Yes, this is the same problem that began with the Visual C++ 2008 Feature Pack.  We have addressed some of the interdependencies that cause those original problems as well.

  • @Moritz ,

    The ARM version of Windows will only run Metro apps.

  • It's nice to see that you're maintaining existing libraries. However, it's obvious that MFC doesn't fit into the world of

    modern C++ development. MFC is more C than C++, it also doesn't do a very good job on wrapping WinAPI. Mixing C++11 and MFC (which dates

    back before STL was standardized) doesn't result in nice code.

    Why doesn't Microsoft create something like Qt? Except it would be even more modern - make use of the STL, new C++11 features etc.

  • what is an MFC control? from http://www.zealsofts.com

  • I don't use VS2010 for this reason, and because the class viewer / object browser has some problems displaying base classes after the nth level of inheritance). The MFC implementation of the BCG classes is also incomplete: for instance, the MFC CFormViewEx class is missing and CFormView doesn't support themes (see the TabbedView example)

  • @Zealsofts: the "MFC controls" I'm referring to are listed with "MFC" prepended in the toolbox when you are working on a dialog in the dialog editor.

    A partial list is: MFC Button Control, MFC FontComboBox Control, MFC PropertyGrid Control, MFC ShellList Control, MFC ShellTree Control.

    @Marcello: When we expanded MFC for the Visual Studio 2008 Feature Pack, we did comment that not all of the BCGSoft capabilities would be included in MFC.  See the original blog post here: blogs.msdn.com/.../mfc-update-powered-by-bcgsoft.aspx

    @Chris: while I understand your suggestion about a more modern MFC, that would certainly require more resources than we current have available on the libraries team.

  • @Pat.  Thanks but I am not going to use the feature classes for now: I will continue using the BCGSoft of Codejock libray with VC++ in some of my projects that need a nice gui and my old and obsolete compiler that generates smaller executables. If your team can accomplish that in the new release( and looks like the team is going in the right direction) it would be great. I will check again in two or three years to see how things are going, and hope that it will be enough time for the team to fix all the bugs, compatibility problems and add the missing features. See you ( if I haven't retired by then).

  • @Pat. Is this feature included in Visual Studio 11 Developer Preview (x86 and x64) - DVD ?

  • Hi Pat,

    You talk about 220 bugs fixed in the new Visual Studio 2011, we use Visual Studio 2010, is there a pacth?

    Thanks

Page 1 of 1 (12 items)