If you create a C++ project in Visual Studio and configure it to "Use MFC in a Shared DLL" (the default) you need to make sure the C runtime (CRT) and the MFC libraries are installed in the correct versions on the end user's computer. That can be difficult.
There are three ways to install the libraries on target computers. In this article I focus on the "private assembly" method where the libraries are stored in your application's directory. Some of its advantages are that you can run the program from any location, even a network share, without installing parts of it to the user's SxS folder first. And you need not worry about calling VC_Redist from your setup.
What you do is simple:
Locate the appropriate folders in C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\redist\x86\ (or AMD64 if you build a 64-bit version). In many cases, when using the runtime and MFC you need Microsoft.VC90.CRT and Microsoft.VC90.MFC. Copy these folders to you application's binary folder, for example:
That is all. Redistribute/install your application like this.
Your executable's manifest (incorporated into your EXE file as a resource) lists the versions of the runtime libraries it requires. The versions listed in the manifest are the initial versions installed on your development machine, though. In the case of Visual Studio 2008 these are the versions of the pre-SP1 libraries. Once you install Service Pack 1 you get the newer libraries in the folder C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\redist\x86, but the manifest in your own binary still demands the original library versions.
That does not work. In fact, it crashes your application when run on a "clean" system.
What you need to do is tell the compiler you want to depend on the newest versions of the libraries. You do that by including the following lines at the very top of Stdafx.h of every binary:
#define _BIND_TO_CURRENT_CRT_VERSION 1
#define _BIND_TO_CURRENT_ATL_VERSION 1
#define _BIND_TO_CURRENT_MFC_VERSION 1
#define _BIND_TO_CURRENT_OPENMP_VERSION 1
or, a combination of all these:
#define _BIND_TO_CURRENT_VCLIBS_VERSION 1
Please note: "Generated files (eg by MIDL) don't include stdafx.h, so putting _BIND_TO_CURRENT_VCLIBS_VERSION in stdafx won't work in this case. You must use _BIND_TO_CURRENT_VCLIBS_VERSION=1 in the C/C++ Preprocessor Definitions page of every project configuration." (Jon Baggott on http://msdn.microsoft.com/en-us/library/cc664727.aspx)
Updates, Patches and Service Packs
You may wonder what happens if you happily install the most current libraries on your users's computers and, some time later, an updated version is released by Microsoft. Will your application continue to use the then old and insecure version? Good question!
If a new version of the runtime library is pushed down via Windows Update and installed to %Windir%WinSxS, not only are the library binaries and their manifest updated, but also a policy file is installed that basically tells the loader to use the new version instead of the old version. See this example (from %Windir%\WinSxS\Manifests\x86_policy.9.0.microsoft.vc90.mfc_1fc8b3b9a1e18e3b_9.0.30729.1_none_7dd1e0ebd6590e0b.manifest):
="1.0" ="UTF-8" ="yes"
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32-policy" name="policy.9.0.Microsoft.VC90.MFC"
version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
<assemblyIdentity type="win32" name="Microsoft.VC90.MFC" processorArchitecture="x86"
<bindingRedirect oldVersion="9.0.20718.0-9.0.21022.8" newVersion="9.0.30729.1"/>
<bindingRedirect oldVersion="9.0.30201.0-9.0.30729.1" newVersion="9.0.30729.1"/>
This tells the loader to use the version 9.0.30729.1 instead of the versions 9.0.20718.0 and 9.0.30201.0.
Nice, but how does this affect your application with its private assemblies (not tied to the WinSxS folder)? Simple, but effective solution: the loader always looks into the WinSxS folder first. So if your application needs mfc90.dll in version 9.0.30201.0 and version 9.0.30201.0 is what you have installed in your application folder, with the help of the policy file above Windows will load the newer version from SxS nevertheless.
For the sake of completeness here is simplest form of the assembly search sequence:
- Side-by-side searches the WinSxS folder.
And for those who still question the use of private assemblies: here is a list of products installed on my system that use them:
- C:\Program Files (x86)\Citrix\ICA Client\msvcm80.dll
- C:\Program Files (x86)\McAfee\Common Framework\Microsoft.VC80.CRT\msvcm80.dll
- C:\Program Files (x86)\TechSmith\Camtasia Studio 6\Microsoft.VC90.CRT\msvcm90.dll
- C:\Program Files (x86)\VMware\VMware Workstation\Resources\msvcm80.dll
And if we take a last look, at, for example, Camtasia Studio: its private version of msvcm90.dll is 9.0.21022.8, but when we examine its loaded DLLs in Process Explorer, we see it uses 9.0.30729.4926 from WinSxS. Magic!
Addendum: Application Crashes at Startup because MSVCR90.DLL is Not Found
Some time after implementing the solution described in this article for a medium-sized project I was made aware of a strange problem: on some machines the application crashes while loading before even being able to display any messages.
The usual debugging tools sxstrace and fuslogvw (Fusion log viewer) did not show anything useful, but Dependency Walker was helpful. It pointed out the problem right away:
Note: CoreFunctions.dll is one of the DLLs of our product. It is dependent on the Visual C runtime files (including msvcr90.dll) and msvcr90.dll is located in the subfolder Microsoft.VC90.CRT as described in this article. But still, msvcr90.dll is not found by the OS loader.
After some research I found out that this happens both on XP (SP3) and Vista (SP2) if and only if the application is started from a mapped network drive. Copying the application's folder to the local hard drive makes the problem go away. Another way to fix this is to put the VC runtime libraries directly into the application folder, not in the subdirectory Microsoft.VC90.CRT.
Helge Klein is an independent consultant and developer. As a consultant, he has worked in Windows and Citrix projects for various larger German corporations. As a developer, he architected sepago's user profile management product sepagoPROFILE whose successor is now available as Citrix Profile Management. In 2009 Helge received the Citrix Technology Professional (CTP) Award, in 2011 he was nominated a Microsoft Most Valuable Professional (MVP).
Helge's professional interests are focused on Microsoft server technologies, various Citrix products and programming in several languages. He publishes his knowledge in English in his blog at http://helgeklein.com/blog
. Helge can also be found on Twitter as @HelgeKlein
. He has presented on many occasions, e.g. Citrix TechEdge Munich 2009, ice Lingen (2009 and 2011), PubForum (2010 and 2011), Microsoft TechDay Online 2010, Citrix Synergy 2011 and 2012.
Helge is the author of SetACL
, a powerful tool for managing Windows permissions from the command line or from scripts and programs. SetACL is open source and has been downloaded more than 500,000 times. SetACL's modern cousin SetACL Studio
comes with an intuitive graphical user interface and is available for a small fee. Another popular tool, Delprof2
, automates the deletion of user profiles.
Helge lives in Cologne, Germany.