As a followup to my previous blog entry, I’ve decided to make a simple workaround for those users that want to create a statically linked MFC EXE in 2010 with a size comparable to what was possible in 2008 SP1, that is, a size around 600K instead of the 1800K that you’d get by creating a simple MFC SDI app with classic menus, native/default look and feel, and classic docking toolbars.
It turns out that there are only three source files from the “classic” MFC source code with dependencies on the feature pack source code, that is causing it all to be pulled in when linking. If we can modify this source code by including these three files in our projects, by removing references to the feature pack source code, then the generated EXE size drops dramatically to 600K.
And of course, we must make sure that we do not use any of the feature pack classes, because as soon as you pull one in, you get most of it linked in. I’m specifcally alluding to CDialogEx. CDialogEx is the “new” base class for dialog boxes generated by the MFC appwizard, including the default generated About box. CDialogEx is relatively useless, unless you want to change the background color and/or image of your dialog box, which is something I wouldn’t recommend doing anyway. So the first thing you want to do is change any instances in your project from CDialogEx to CDialog (base classes, base method calls, etc). (a simple Find in Files will suffice)
Then, follow the directions below to get your app back to a reasonable size.
1) Create a default MFC SDI app with no special features (MFC standard, windows native/default style, classic menu, classic docking toolbar, no restart manager support) (this step is not necessary if you already have a project to play with)
2) Change to MFC static linking using project properties
3) add the following MFC source files to your project (copy them from the MFC source file folder located in the place you installed Visual Studio 2010, usually your program files\microsoft visual studio 10.0\vc\atlmfc\src\mfc into your project folder and then “add existing item” for all three files)
afxctrlcontainer.cpp
afxglobals.cpp
winmdi.cpp
4) comment out (or #if 0/#endif) the following code in the MFC source files added:
in afxctrlcontainer.cpp:
all the includes after afxctrlcontainer.h
some lines in CMFCControlContainer::CreateDlgControl
some lines in PreUnsubclassControl
the call to tagManager in ReadBoolProp
in afxglobals.cpp
all the includes after comdef.h except afxglobals.h
the loop that starts with “Notify toolbars about font changing:”
the cleanup for various items ( these will be obvious based on the errors you get compiling after removing the includes above)
the references to CPaneFrameWnd
the references to feature pack classes in AfxIsExtendedFrameClass
the reference to CMFCToolbar in AfxIsMFCToolbar
in winmdi.cpp
the two includes after sfdafx.h
the block of code in UpdateClientEdge that starts with CMDIChildWndEx
5) In your project’s stdafx.h add the following lines (these are internal MFC code that some of the source files require, you can add them to the individual source files instead of you prefer):
#include "afxpriv.h"
#include "afxole.h"
#include "..\src\mfc\oleimpl2.h"
#include "..\src\mfc\afximpl.h"
6) Remove the
#include <afxcontrolbars.h>
from the stdafx.h if applicable. And remove any references to CShellManager in your generated project.
After doing the above, recompile your release build, and notice that your generated EXE should be quite a bit smaller (around 600K instead of 1800K for the above example).
39 Comments
I tried these steps and it reduced the size of my exe from 2Mb to 700Kb. Great work.
This worked. Thanks! I’m just wandering why MS does not have option to turn of this libraries. I always seen Borland’s GUI apps were larger than VC++ but not anymore with VC++ 2010.
Maybe there is option that we don’t know? If there is no options than Microsoft don’t be lazy and destroy VC ++ name. Add option in settings to disable this feature packs that makes static exe’s very large files!!!
I agree, there should be an option, there is definitely not one, as I’ve talked to the lead developer of MFC at Microsoft and he stated the same thing. These dependencies were mainly put in there so that the design time (resource editor) support for ribbons are working properly. With some simple restructuring it could have been done and saved us from using this workaround.
I tried the solution in a DHTMLDialog based MFC application. But I get a list of linker errors all from uafxcw.lib(afxglobals.obj) for some symbols already defined in afxglobals.obj.
Is this approach meant for SDI/MDI applications only?
It works for DHTMLDialog apps as well. I did a test, creating a dialog box based app, and checked the HTML Dialog checkbox in the wizard, added the 3 files I mentioned (edited from an older project). The only variation from my instructions, is that I had to get rid of the include afxcontrolbars.h and the references to CShellManager in my generated dialog’s InitInstance. If you need copies of my edited three cpp files let me know I can send you them via email. The resulting EXE was 443K
I have also gone through the instructions listed and a new MFC simple dialog app with the changes mention still comes to 1.6 MB. I have removeds the CDialogEx references as well but still no go. If you have the source and can share them that would be great thanks.
sent requested sample via email
Hi Ted,
Thank you for replying.
For a new project I too am able to compile and link the DHTMLDlg based app. But for my existing application, I get the linker errors.
I also had to #define _WIN32_WINNT 0×0601 to get rid of compilation errors related to m_bComInitialized being not defined.
Notable that I have imported a tlb (*_i.c and *_i.h) for ATL based simlpe object into my MFC app. Is that causing the problems?
Linker Errors:
—————–
uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: __thiscall CMemDC::CMemDC(class CDC &,class CWnd *)” (??0CMemDC@@QAE@AAVCDC@@PAVCWnd@@@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: __thiscall CMemDC::CMemDC(class CDC &,class CRect const &)” (??0CMemDC@@QAE@AAVCDC@@ABVCRect@@@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: virtual __thiscall CMemDC::~CMemDC(void)” (??1CMemDC@@UAE@XZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: __thiscall AFX_GLOBAL_DATA::AFX_GLOBAL_DATA(void)” (??0AFX_GLOBAL_DATA@@QAE@XZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: __thiscall AFX_GLOBAL_DATA::~AFX_GLOBAL_DATA(void)” (??1AFX_GLOBAL_DATA@@QAE@XZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: void __thiscall AFX_GLOBAL_DATA::UpdateFonts(void)” (?UpdateFonts@AFX_GLOBAL_DATA@@QAEXXZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: void __thiscall AFX_GLOBAL_DATA::OnSettingChange(void)” (?OnSettingChange@AFX_GLOBAL_DATA@@QAEXXZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: void __thiscall AFX_GLOBAL_DATA::UpdateSysColors(void)” (?UpdateSysColors@AFX_GLOBAL_DATA@@QAEXXZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: int __thiscall AFX_GLOBAL_DATA::SetMenuFont(struct tagLOGFONTW *,int)” (?SetMenuFont@AFX_GLOBAL_DATA@@QAEHPAUtagLOGFONTW@@H@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “protected: void __thiscall AFX_GLOBAL_DATA::UpdateTextMetrics(void)” (?UpdateTextMetrics@AFX_GLOBAL_DATA@@IAEXXZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “protected: struct HBITMAP__ * __thiscall AFX_GLOBAL_DATA::CreateDitherBitmap(struct HDC__ *)” (?CreateDitherBitmap@AFX_GLOBAL_DATA@@IAEPAUHBITMAP__@@PAUHDC__@@@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: void __thiscall AFX_GLOBAL_DATA::CleanUp(void)” (?CleanUp@AFX_GLOBAL_DATA@@QAEXXZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “void __cdecl ControlBarCleanUp(void)” (?ControlBarCleanUp@@YAXXZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: int __thiscall AFX_GLOBAL_DATA::DrawParentBackground(class CWnd *,class CDC *,struct tagRECT *)” (?DrawParentBackground@AFX_GLOBAL_DATA@@QAEHPAVCWnd@@PAVCDC@@PAUtagRECT@@@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “class CFrameWnd * __cdecl AFXGetParentFrame(class CWnd const *)” (?AFXGetParentFrame@@YAPAVCFrameWnd@@PBVCWnd@@@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: unsigned long __thiscall AFX_GLOBAL_DATA::GetColor(int)” (?GetColor@AFX_GLOBAL_DATA@@QAEKH@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: int __thiscall AFX_GLOBAL_DATA::SetLayeredAttrib(struct HWND__ *,unsigned long,unsigned char,unsigned long)” (?SetLayeredAttrib@AFX_GLOBAL_DATA@@QAEHPAUHWND__@@KEK@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: void __thiscall AFX_GLOBAL_DATA::EnableAccessibilitySupport(int)” (?EnableAccessibilitySupport@AFX_GLOBAL_DATA@@QAEXH@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: class ATL::CStringT<wchar_t,class StrTraitMFC<wchar_t,class ATL::ChTraitsCRT > > __thiscall AFX_GLOBAL_DATA::RegisterWindowClass(wchar_t const *)” (?RegisterWindowClass@AFX_GLOBAL_DATA@@QAE?AV?$CStringT@_WV?$StrTraitMFC@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@PB_W@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: int __thiscall AFX_GLOBAL_DATA::ExcludeTag(class ATL::CStringT<wchar_t,class StrTraitMFC<wchar_t,class ATL::ChTraitsCRT > > &,wchar_t const *,class ATL::CStringT<wchar_t,class StrTraitMFC<wchar_t,class ATL::ChTraitsCRT > > &,int)” (?ExcludeTag@AFX_GLOBAL_DATA@@QAEHAAV?$CStringT@_WV?$StrTraitMFC@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@PB_W0H@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: int __thiscall AFX_GLOBAL_DATA::DwmExtendFrameIntoClientArea(struct HWND__ *,struct _AFX_MARGINS *)” (?DwmExtendFrameIntoClientArea@AFX_GLOBAL_DATA@@QAEHPAUHWND__@@PAU_AFX_MARGINS@@@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: long __thiscall AFX_GLOBAL_DATA::DwmDefWindowProc(struct HWND__ *,unsigned int,unsigned int,long)” (?DwmDefWindowProc@AFX_GLOBAL_DATA@@QAEJPAUHWND__@@IIJ@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: int __thiscall AFX_GLOBAL_DATA::DwmIsCompositionEnabled(void)” (?DwmIsCompositionEnabled@AFX_GLOBAL_DATA@@QAEHXZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: int __thiscall AFX_GLOBAL_DATA::DrawTextOnGlass(void *,class CDC *,int,int,class ATL::CStringT<wchar_t,class StrTraitMFC<wchar_t,class ATL::ChTraitsCRT > >,class CRect,unsigned long,int,unsigned long)” (?DrawTextOnGlass@AFX_GLOBAL_DATA@@QAEHPAXPAVCDC@@HHV?$CStringT@_WV?$StrTraitMFC@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@VCRect@@KHK@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: struct HICON__ * __thiscall AFX_GLOBAL_DATA::GetHandCursor(void)” (?GetHandCursor@AFX_GLOBAL_DATA@@QAEPAUHICON__@@XZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: int __thiscall AFX_GLOBAL_DATA::Resume(void)” (?Resume@AFX_GLOBAL_DATA@@QAEHXZ) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: int __thiscall AFX_GLOBAL_DATA::GetNonClientMetrics(struct tagNONCLIENTMETRICSW &)” (?GetNonClientMetrics@AFX_GLOBAL_DATA@@QAEHAAUtagNONCLIENTMETRICSW@@@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “int __stdcall AfxIsExtendedFrameClass(class CWnd *)” (?AfxIsExtendedFrameClass@@YGHPAVCWnd@@@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “int __stdcall AfxIsMFCToolBar(class CWnd *)” (?AfxIsMFCToolBar@@YGHPAVCWnd@@@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: long __thiscall AFX_GLOBAL_DATA::ShellCreateItemFromParsingName(wchar_t const *,struct IBindCtx *,struct _GUID const &,void * *)” (?ShellCreateItemFromParsingName@AFX_GLOBAL_DATA@@QAEJPB_WPAUIBindCtx@@ABU_GUID@@PAPAX@Z) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “public: static int CMemDC::m_bUseMemoryDC” (?m_bUseMemoryDC@CMemDC@@2HA) already defined in afxglobals.obj
1>uafxcwd.lib(afxglobals.obj) : error LNK2005: “struct AFX_GLOBAL_DATA afxGlobalData” (?afxGlobalData@@3UAFX_GLOBAL_DATA@@A) already defined in afxglobals.obj
———————————-
I belive that in order for my “trick” to work (that is, substituting an existing compiled obj from a library with one in my own project without causing multiple definition errors) is to make sure all compile options are the same. For example, treat WChar as built-in type (under Project settings – C++ – Language) must be set to true. There may be other compilation options that cause the multiple definition error. I would suggest going through (side by side on a monitor, or look at c++ command line options tab) and compare between your non-working project and the default DHTML project you created that works. Good luck and let me know how it goes.
Hey Ted..
I tried your changes in a brand new MFC application (stock generated code, removing most options).. But I only saw a file size reduction from 1.6 MB to 1.5 MB (about 100 KB change).. I don’t know if there’s a project setting I got wrong when going thru the wizard, or if its something else I’m missing??
Email me, and I’ll send you the project, if you feel so inclined.. (assuming you have access to the supplied email addresses)..
Thanks..
For the benefit of other readers, I’ll mention that we resolved this via email, and it ended up being the fact that a reference to CDialogEx still existed in the source code (the default generated one in the About box code). Removing that reference using replace in files brought the app’s release size down to 404K.
Hi Ted,
I tried your solutino but don’t work.
Can you send me via e-mail your project?
Your idea is very good, I hope VS C++ Team resolve this problem.
sent via email
Hello,
Does this modify port some security errors?
Thanks
CDialogEx is not useless. Eg. if you want to put CMFCPopupMenu on CDialog, you can expect curious problems. Especialy if you’re using multithreaded dialgs. So difference between CDialog and CDialogEx is’nt only in setting color atc. If you’re using new CMFCxxx classes, you should use CDialogEx, you can use CDialog but expect some problems in future.
Hi Ted,
I Tried your solution in MFC dialog based application but the size is not reduced. also i am using CDailog instead of CDialogEx but the size remains almost same.
Please mail me for the same.
sent via email.
This worked quite nicely reducing the size of a simple dialog from over 1,700 kb to 428 kb – Still about 150 kb greater than the same program built with VS 2008
Your idea is very good,in my solution but don’t work,Can you send me via e-mail your project?Thanks
Anyone got this to work with VS2010 SP1 ?
I got it to work.
afxglobals.cpp is updated in SP1 so just redo the “trick” with that file and it work.
My exe grow an extra 5kb in size with SP1 compared to VS2010 RTM but that is acceptable.
Thanks very much for the info. It does bring an important point to the forefront which is: whenever one takes copies of MFC source code, it’s always a good idea to check if they’ve been updated.
Ted,
Could you send me your VS2010 project so that I could compare it with my own attempt?
Thanks,
-Dave
tried, but the email you provided had a delivery failure notice when I tried to send to it.
Ted,
Thats really strange… it is my valid e-mail address. Could you try without an attachment? This reply contains an alternate address. :)
-Dave
project sent, didn’t bounce this time :)
i tried your solution in my vs2010sp1 project, it doesn’t work, would you please send me your project?
Regards
Ted,
Could you also send me your VS2010 SP1 project to compare with mine?
I cannot get rid of the LNK2005 uafxcw.lib(afxglobals.obj) in a new MFC dialog EXE project.
Thanks.
-David
(not the same Dave as the 3/25 post)
Hi Ted!
Thanks for the valuable information. I was wondering if you would mind if we reposted this information on our website? Thanks in advance, and great work! Hopefully Microsoft will listen to developers on this one.
-Kirk
Hi Kirk, not a problem – just include a link to the blog entry as well in case I make any future updates.
Worked perfectly with Visual Studio until about 2 weeks ago. Now (probably after installation of SP1), the EXE size is still small, but starting the EXE (Release-Build, without debugger) now lasts several seconds before the application window is shown. This has been tested with a simple dialog application created by the wizard – no additional code.
First I thought it’s some system setting (Firewall or something) that caused the delay, but I found an old EXE from a test-project I did 2 weeks ago, which shows the dialog immediately after starting. A simple rebuild-all of the same (!) project resulted in an EXE file of the same size as the old one, but with the multiple seconds delay.
I re-did all the afxctrlcontainer.cpp, afxglobals.cpp and winmdi.cpp changes with the current MFC sources from the Visual Studio directory, but to no avail.
Note that the application is running perfectly and the EXE size of an empty project (statically linked with the described method) is about 337 KB – and without, it’s ~1.7 MB, but showing the application window immediately. It’s just the long delay of around 4-5 seconds, and it does not change when you launch the EXE multiple times consecutively (so it’s loaded from cache). During the delay, I recognized that the mouse pointer changes the hourglass for first few seconds, then to the pointer-with-small-hourglass, before displaying the dialog.
So to sum up:
Before around two weeks: small EXE, fast start
now (same project): small EXE, slow start
Why must MS always break good workarounds without offering any alternatives?
Anyone with the same problem, or with a solution?
Hmmm… that seems to have been a temporary local problem. Today, it works as before, i.e. the built EXEs do no longer have the delay on startup. I’m still wondering what caused this (as when the effect happened, the older EXEs were still without delay)…
it could have been a corrupted SUO file – this is a hidden file in the solution folder(sometimes they get big – in the range of hundreds of megabytes in size where they should be only 100K or so)
Hi Ted,
Could you mail me your project file as well?
Thanks!
Ran into the problem as well. Any chance you can email me the updated VS10 files?
thanks,
jim
Hi Ted,
Thanks for mailing me your project files?
Thierry
Hi Ted,
Could you please mail me your vs 2010 project files. Thanks in advance for your great working.
Jack.
Hi Ted,
Thank you for sending mail, i have just received your project files.
Much appreciated…
Jack.
Can I also get the files?
Can’t seem to get rid of the linker errors Pallavi Saxena had.
One Trackback
[...] problem that VS 2010′s MFC library suffers from. I applied the solution mentioned in this blog post (actually in my case I only needed to hack the afxglobals.cpp file). Now the MFC balloon is [...]