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 your application's binary folder, for example:
C:\MyAppFolder\Application.exe
C:\MyAppFolder\Microsoft.VC90.CRT
C:\MyAppFolder\Microsoft.VC90.MFC
That is all. Redistribute/install your application like this.
Caveats
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 (e.g. 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"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="Microsoft.VC90.MFC" processorArchitecture="x86"
publicKeyToken="1fc8b3b9a1e18e3b"/>
<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"/>
</dependentAssembly>
</dependency>
</assembly>
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.
- <appdir>\<assemblyname>.DLL
- <appdir>\<assemblyname>.manifest
- <appdir>\<assemblyname>\<assemblyname>.DLL
- <appdir>\<assemblyname>\<assemblyname>.manifest
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.
References