Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C++
Article

Create projects easily with private MFC, ATL and CRT assemblies

Rate me:
Please Sign up or sign in to vote.
4.95/5 (33 votes)
15 Jun 2007CPOL11 min read 246.3K   1.8K   76   84
An easy way to create programs that use CRT, MFC and ATL library DLLs from application local folders

Introduction

Up until VC++ 7.1 (VS.NET 2003), it was easy to deploy programs that use either the CRT-DLLs or the MFC shared DLLs. Just copying the runtime DLLs into the target directory with the executable would allow it to run.

This stopped suddenly with VC++ 8.0 (VS 2005). Programs that use the CRT and MFC with the shared DLL versions can't easily copy the runtime DLLs into the local application folder and run the application. This will work on Windows 2000, but not on Windows XP and Windows Vista. The reason is that VC++ 8.0 binds the CRT and MFC -- and ATL, if used -- with a manifest to the EXE. This manifest tells the loader to pull the files from the Side by Side (SxS) storage of the current Windows system.

So, if the runtime files are not installed in the SxS storage the program can't run, even if the needed files are in the current application folder. You have to install the runtime library files on the machine with the appropriate MSI merge module or installer. See the article about vcredist_x86.exe for more details.

A lot of developers therefore decided to link statically to the MFC and CRT to avoid this. However, in a lot of cases it is not possible to use the static linked versions of the libraries, such as when using MFC extension DLLs. The way to use the MFC and CRT as private assemblies is well-known and you might find some postings, forums, articles or blogs that address this.

Using private assemblies for runtime files allows easy xcopy deployment. No bootstrapping or MSI installer is needed in this case. To use private assemblies of the runtime files, it is necessary to extract the runtime files and modify the created manifest manually. I don't like to edit manifests. There are too many mistakes you can make. Also, you have to modify the standard way in which C++ projects are created. Again, I don't like much intelligence in the project settings. I like code that controls the project as much as possible.

So, I asked myself if there was a way to reuse the current code and to change its behavior such that manifests are automatically created which support private application local assemblies. The result is this project.

Background

When you create a standard program with VC++ 8.0 using the shared DLLs of the MFC and the CRT, the following manifest gets created:

XML
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC80.CRT' 
          version='8.0.50727.762' processorArchitecture='x86' 
          publicKeyToken='1fc8b3b9a1e18e3b'/>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC80.MFC' 
          version='8.0.50727.762' processorArchitecture='x86' 
          publicKeyToken='1fc8b3b9a1e18e3b'/>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.Windows.Common-Controls'
          version='6.0.0.0' processorArchitecture='x86' 
          publicKeyToken='6595b64144ccf1df'language='*' />
    </dependentAssembly>
  </dependency>
</assembly>

The publicKeyToken attributes tell the loader in a Windows XP/2003/Vista system to look into the SxS storage for the needed DLLs. So, if we remove the publicKeyToken entries from the manifest, the program will no longer use the public installed files. It has been changed to use the requested assemblies private and application local.

Now let us remove the tokens. The manually created manifest to be used must still be included into your executable. We can't use the automatically created one. Neither can we use the automatic mechanisms in the project that collects all manifest dependencies from the source code, compiles it into a single manifest and appends it to the application. I will return to this problem later, when I describe the code.

Create private copies of the MFC and CRT assemblies

If we want to use the MFC and CRT as private copies, we have to prepare the assemblies. It is not sufficient to copy the needed files. You have to do this preparation for the CRT files, the MFC files, the MFC locale files and, finally, the ATL DLLs if applicable. And that's not all. You have to do it twice: once for the release mode files and once for the debug mode files. Remember that you are only allowed to redistribute the release mode files.

The files are ready to copy in the Visual Studio directory structure. You find the release mode files in the folder C:\Program Files\Microsoft Visual Studio 8\VC\redist\x86. The debug mode files are located in the folder C:\Program Files\Microsoft Visual Studio 8\VC\redist\Debug_NonRedist\x86. I will now describe the procedure involving the CRT release mode files. In my case, these were the VS-2005 SP1 files, version 8.0.50727.762.

First, we copy the CRT files into our application folder. We use the files from C:\Program Files\Microsoft Visual Studio 8\VC\redist\x86\Microsoft.VC80.CRT. We then find the files msvcr80.dll, msvcp80.dll, msvcm80.dll and Microsoft.VC80.CRT.manifest. Now we need to make some small changes to this manifest file to make it private application local assembly. We must remove the publicKeyToken entries from it, but we don't need to modify any other parts.

XML
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <noInheritable />
  <assemblyIdentity name="Microsoft.VC80.CRT" version="8.0.50727.312" 
      processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"
      type="win32" />
  <file hash="58985c4d847f19365d93fb8703e78c2b1ae57500" hashalg="SHA1" 
      name="msvcr80.dll" xmlns:cmiv2="urn:schemas-microsoft-com:asm.v3" 
      cmiv2:sourceName="">
    <asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2">
      <dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
        <dsig:Transform 
            Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
      </dsig:Transforms>
      <dsig:DigestMethod xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" 
          Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
      <dsig:DigestValue 
          xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
          >zUUe9BK+jgwQPjesMcOhEi690wHyaXcfFCajzryLpYo=</dsig:DigestValue>
    </asmv2:hash>
  </file>
  <file hash="3bf3838264659c30b657fd2176c9cbf4adb5b067" 
      hashalg="SHA1" name="msvcp80.dll" 
      xmlns:cmiv2="urn:schemas-microsoft-com:asm.v3" cmiv2:sourceName="">
    <asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2">
      <dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
        <dsig:Transform 
            Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
      </dsig:Transforms>
      <dsig:DigestMethod xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" 
          Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
      <dsig:DigestValue 
          xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
          >SdVtn3h0M4cTqWshracoyu1oJNmwweuYpi+hhf9rnzY=</dsig:DigestValue>
    </asmv2:hash>
  </file>
  <file hash="aacedb76716864fbcadae5b167eb7ab8dc809fe8" hashalg="SHA1" 
      name="msvcm80.dll" xmlns:cmiv2="urn:schemas-microsoft-com:asm.v3" 
      cmiv2:sourceName="">
    <asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2">
      <dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
        <dsig:Transform 
            Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
      </dsig:Transforms>
      <dsig:DigestMethod xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" 
          Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
      <dsig:DigestValue 
          xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
          >0CXjg8zPg3wsAPUtF5D0iSGO2MxppEkANMI9WqEcjEQ=</dsig:DigestValue>
    </asmv2:hash>
  </file>
</assembly>

If you want, you can simplify the manifest by removing the schema hints in it. The final simplified manifest looks very simple indeed.

XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <noInheritable></noInheritable>
  <assemblyIdentity type="win32" 
      name="Microsoft.VC80.CRT" version="8.0.50727.762" 
      processorArchitecture="x86"></assemblyIdentity>
  <file name="msvcr80.dll" hash="10f4cb2831f1e9288a73387a8734a8b604e5beaa" 
      hashalg="SHA1"/>
  <file name="msvcp80.dll" hash="b2082dfd3009365c5b287448dcb3b4e2158a6d26" 
      hashalg="SHA1"/>
  <file name="msvcm80.dll" hash="542490d0fcf8615c46d0ca487033ccaeb3941f0b" 
      hashalg="SHA1"/>
</assembly>

Now repeat these steps for the MFC and ATL DLLs. If you want, you can repeat this for the debug mode files too. I use private assemblies for the release mode and debug mode files. For testing purposes, it is of interest to embed the debug mode DLLs as private assemblies. This is because you can't install the debug mode runtime files on a second test machine without installing Visual Studio 2005. There is no official MSI package to just get the debug mode files on a machine out of the box.

You will find the modified release mode files in separate ZIP files at the top of this article. I can't upload the debug files, as the EULA doesn't allow this. However, with this description it should be simple to create your own copies of them. Another way to gain the files above is to use them directly from the side by side storage.Here is a complete list of the folder names in side by side storage (C:\Windows\winsxs):

CRT Debugx86_microsoft.vc80.debugcrt_1fc8b3b9a1e18e3b_
8.0.50727.762_none_24c8a196583ff03b
CRT Releasex86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_
8.0.50727.762_none_10b2f55f9bffb8f8
MFC Debugx86_microsoft.vc80.debugmfc_1fc8b3b9a1e18e3b_
8.0.50727.762_none_29a8a38855141f6e
MFC Releasex86_microsoft.vc80.mfc_1fc8b3b9a1e18e3b_
8.0.50727.762_none_0c178a139ee2a7ed
MFC localex86_microsoft.vc80.mfcloc_1fc8b3b9a1e18e3b_
8.0.50727.762_none_43efccf17831d131
ATLx86_microsoft.vc80.atl_1fc8b3b9a1e18e3b_
8.0.50727.762_none_11ecb0ab9b2caf3c

The matching manifests are in the folder C:\Windows\winsxs\Manifests, with the same name as in the table above. If you want to use these files, you have to rename the manifest files to the plain assembly name, i.e. microsoft.vc80.crt.manifest for the release mode CRT assembly.

Where to place the runtime library files

The easiest and most compatible way is to place all DLLs and manifest files in the same folder as your executable. This works for Windows 2000, Windows XP and Vista. If you want, though, you can place all the files of one assembly into a subdirectory named as the desired manifest. If you look in the associated ZIP files, you will find all of the library files arranged in this way. If the loader is requested to search for a manifest named microsoft.vc80.mfc, it also searches the application folder for a directory with the name of this manifest. All files belonging to this assembly -- including the manifest file -- have to be stored into such a subdirectory.

Remember that this file layout will not work with Windows 2000. If you plan an xcopy distribution that runs on Windows 2000 too, you should place all runtime library files in the same folder as the executable.

Results up to now

At this point, we have succeeded in creating an application that loads the runtime library files from the application's local storage. We need to do this just once. The only problem that remains is this: We always need to modify the projects that the automatically created manifests ignore. We have to remove the publicKeyToken entries and we have to embed this new manifest into our application. As I mentioned above, though, it would be nice if we could just modify the manifest with some code and still use the same techniques without touching the project settings.

Using the code

If you include the header file UseMSPrivateAssemblies.h before you include any other CRT or MFC header file, everything is done automatically. You will find that your project compiles and creates a manifest that doesn't use the public side by side assemblies.

Note: It is extremely important that you include this file before any other CRT and MFC header is used. The best location in which to include this file is the stdafx.h file of your project, just at the top, before any define or other include file is used.

Please also note that the ATL and MFC locale defines are commented out in this header file. Feel free to place #ifdef statements around them. The use of ATL.DLL is rare. Also, the MFC locale DLLs must not be loaded via an assembly manifest. The MFC can find them in the current application folder without any further action.

How does it work?

The first thing the code does is define the preprocessor variables _STL_NOFORCE_MANIFEST, _CRT_NOFORCE_MANIFEST, _AFX_NOFORCE_MANIFEST and _ATL_NOFORCE_MANIFEST (only if you use it). These defines prevent the MS header files from ejecting #pragma comment statements with the manifest information of the used CRT and MFC. Using these defines is very wise in library files. In this case, no manifest entries are created from our object code and the code that uses your library is free to define the needed assemblies.

However, this is not enough. If you just define the preprocessor variables, you don't create manifest entries from your compiled code. When you link to the CRT or the MFC files, two object files are pulled from the libraries that again create some manifest entries. It is easy to force the linker to not include these object files by defining a few variables.

C++
int _forceCRTManifest;
int _forceMFCManifest;
int _forceAtlDllManifest;

The variables have to be defined as extern "C". As you can see in the original source code, I declared them with __declspec(selectany). This was to prevent the linker from firing an error because of the multiple definitions that the linker will find of these variables when the header file is used in more than one module.

Finally, we want to create our manifest that defines the assemblies as private. Again, this is easy as long as the base versions of all runtime files are the same. I just use the header file crtassem.h from the CRT code to get the correct version and names. The created manifest entries reflect the debug and release mode, as you can see in the following source code.

C++
#ifdef _DEBUG
#define __LIBRARIES_SUB_VERSION    "Debug"
#else
#define __LIBRARIES_SUB_VERSION    ""
#endif
 
// Manifest for the CRT
#pragma comment(linker,"/manifestdependency:\"type='win32' " \
    "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".
    " __LIBRARIES_SUB_VERSION "CRT' " \
    "version='" _CRT_ASSEMBLY_VERSION "' " \
    "processorArchitecture='x86' \"")
 
// Manifest for the MFC
#pragma comment(linker,"/manifestdependency:\"type='win32' " \
    "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".
    " __LIBRARIES_SUB_VERSION "MFC' " \
    "version='" _CRT_ASSEMBLY_VERSION "' " \
    "processorArchitecture='x86'\"")
 
#pragma comment(linker,"/manifestdependency:\"type='win32' " \
    "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".MFCLOC' " \
    "version='" _CRT_ASSEMBLY_VERSION "' " \
    "processorArchitecture='x86'\"")

Feel free to add #ifdefs and other stuff to this header file to make it more flexible. Maybe this is work for a version 1.1.

Why should we not use the original manifests?

What would happen if you used the unchanged manifests in the application directory? Wouldn't it work?

Yes! It would work. Unfortunately, you can't control what assemblies are loaded. If the needed assemblies are in the SxS storage too, then the loader of the OS will choose the files from there. If the files are not installed in the SxS storage, then the local files in the application folder will be used. If a new service pack is installed in the side by side storage, i.e. SP2, these files will be loaded and executed. The application local files are ignored. So, using the files in this way is a kind of fallback solution. However, it is possible to get some kind of xcopy installation this way.

Thanks to my co-MVP Hans Dietrich for pointing this out.

Get the sample to run

Even after you've compiled the sample, it will not run as long as you haven't copied the needed runtime files into the application directory or subdirectories! So, don't wonder if you get an error. You can find the release mode runtime library files in a separate ZIP file.

Final tips

You don't need a manifest for the MFC locale files. You might just copy them into the application folder. This works fine. The MFC always tries to load the files first by the internal manifest. If it can't find the files, it loads them from the application directory. The MFC files always contain a UNICODE and an MCBS version. You might need only one. Feel free to modify the manifest file for the MFC DLLs and just ship the version that you really need.

That's it; have fun with this. Happy coding and God bless you all.

History

  • 12 June, 2007 -- Version 1.0: first basic version for VC++ 8.0
  • 13 June, 2007 -- Version 1.1: simplified description and added some notes from the current discussion

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Germany Germany
MVP for C++ in Germany from 2000 up to 2015.
Developer since 1979, working with C/C++ since 1982
Started with Windows development in 1990

Love my bicycles (specially my recumbent) and geocaching
http://blog.m-ri.de/index.php/category/real-life/fahrrad/

Comments and Discussions

 
Generalerror message when trying to use private manifest for ATL Pin
xairoy26-May-11 5:22
xairoy26-May-11 5:22 
GeneralRe: error message when trying to use private manifest for ATL Pin
Martin Richter [rMVP C++]26-May-11 10:53
Martin Richter [rMVP C++]26-May-11 10:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.