2011-01-18

Manifest this

In this blog entry I will talk about Visual Studio 2008 and the C runtime library.

Firstly we create a new Win32 hello world console application:

#include <iostream>

int main()
{
    std::cout << "Hello World" << std::endl;
}

The resulted hello.exe binary is 9KB large, and by using Dependency Walker we find that is dynamically liked to msvcp90.dll, msvcr90.dll, and kernel32.dll

One might think that in order to deploy this hello application you will only need msvcp90.dll and msvcr90.dll alongside the application, or to have them somehow installed in the system.

The latter option is preferred by most applications, you only need to install Microsoft Visual C++ 2008 Redistributable Package and that's it.

The redistributable package is 1.7MBytes in size, and is installed in c:\Windows\WinSxS directory, and it also includes MFC. But this is just a hello world application, surely we don't need to install MFC.

The good news is that you can copy from C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT\ only msvcp90.dll, msvcr90.dll, and the funnily file named Microsoft.VC90.CRT.manifest. You don't need to copy msvcm90.dll because this is required for "Managed" C++ applications.

The manifest file is required because starting with Visual Studio 2005 the C runtime library (CRT) is packaged as Side-by-side Assembly. Applications need to have also a bit of magic in order to work with the new CRT namely the RT_MANIFEST resource embedded in the executable.

By using Resource Hacker we can take a peek at this RT_MANIFEST section:

<assembly manifestversion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <trustinfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedprivileges>
        <requestedexecutionlevel level="asInvoker" uiaccess="false"></requestedexecutionlevel>
      </requestedprivileges>
    </security>
  </trustinfo>
  <dependency>
    <dependentassembly>
      <assemblyidentity name="Microsoft.VC90.CRT" processorarchitecture="x86" publickeytoken="1fc8b3b9a1e18e3b" type="win32" version="9.0.21022.8"></assemblyidentity>
    </dependentassembly>
  </dependency></assembly>

I have highlighted the version of  the CRT used.

What happens if you decide to install Visual Studio 2008 SP1? You would expect that you should have a different CRT version. That was the case for Visual Studio 2005, Visual Studio 2008 uses the RTM version also when you install SP1, as described in this Visual C++ Team Blog article.

The size of the Microsoft Visual C++ 2008 SP1 Redistributable Package changed as well, from 1.7MB to 4.0MB due to MFC Facelift... Feature Pack and C++ TR1 inclusion.

In order to have the new CRT referenced one needs to set the following preprocessor define _BIND_TO_CURRENT_CRT_VERSION. After doing so the CRT version becomes 9.0.30729.1.

After Visual Studio 2008 SP1 there was another CRT update due to ATL Security Update, which bumped the CRT version to 9.0.30729.4148. This version is referenced in "c:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT\Microsoft.VC90.CRT.manifest"

So we have build our hello application with version 9.0.30729.1 and we distribute the CRT with version 9.0.30729.4148, which of course doesn't work in practice.

Others have encountered this problem and they have taken drastic measures like reinstallation of Visual Studio 2008 and/or manually change the CRT version in "c:\Program Files\Microsoft Visual Studio 9.0\VC\include\crtassem.h" header file.

Since these manifest files are xml files why not just change them using the manifest tool?

The following two commands extract and repack the RT_MANIFEST resource:
mt.exe -inputresource:hello.exe;#1 -out:hello.manifest
mt.exe -outputresource:hello.exe;#1 -manifest hello.manifest

Now there is the issue of replacing "9.0.30729.1" to "9.0.30729.4148" in hello.manifest and since sed is not present on a normal Windows machine I have created a Javascript script which does this task, which is called like this:

cscript.exe replace_string.js hello.manifest "9.0.30729.1" "9.0.30729.4148"

This way we can update the CRT files without having to wait for Visual Studio to catch up.

They say Visual Studio 2010 has addressed the whole manifest nightmare and one can copy the CRT files alongside the executable file without any hacking.

No comments: