Windows Side-by-Side Assemblies (WinSxS)

Before Windows Side-by-Side Assemblies, or WinSxS, (introduced with Windows XP in the Windows\WinSxS directory) the world was plunged into "DLL Hell". That is to say run-time DLLs would all need to be copied into a path (such as System or System32) that is in the global application loader module search path. Commonly people publishing software would also include copies of these DLLs in the local application directory just to be safe, in case the client did not have the necessary runtime already installed, or there was a version mismatch.

MFC 8 & 9, through VS 2005 & 2008 respectively, makes use of WinSxS (future releases will likely continue in this vein, e.g. VS 2010). It embeds a manifest into the compiled application which describes the runtime it requires to operate properly. For example, the following is taken from VS 2005 and describes a debug build making use of version 8 of the C run-time and MFC:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT"
version="8.0.50608.0" processorArchitecture="x86"
publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC80.DebugMFC"
version="8.0.50608.0" processorArchitecture="x86"
publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls"
version="6.0.0.0" processorArchitecture="x86"
publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
</dependentAssembly>
</dependency>
</
assembly>

The application loader will read this manifest and attempt to locate the correct files based on the information inside (described shortly), instead of looking for the name of the DLL in the available search paths. If you're missing the MFC 8 runtime then the loader will complain that the correct application environment has not been established and the program will not load (here are some tips for diagnosing the problem). The old quick fix of copying the runtime into the local application directory will not fix this because the loader looks for the runtime DLLs (described by the manifest) in the Windows\WinSxS directory only (unless a manifest file is explicity included whose name matches that of the required assembly). Therefore it is to the WinSxS directory that the runtime must be copied! Luckily no additional 'registration'/registry editing is required! (Note: this is not true in certain circumstances! Read on...)

The following example deals with the MFC 8 and VC 2005 Debug runtime (look here for the Release build):

In addition to the DLLs, the relevant files and directories from the 'Manifests' and 'Policies' directories must also be copied to the computer requiring the runtime. The directory structure must be maintained! The operative strings are:

  • x86_Microsoft.VC80.DebugCRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_f75eb16c
  • x86_Microsoft.VC80.DebugMFC_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_c8452471

Once all the files have been copied, all MFC 8/VC 2005 applications will be loaded! (Whether the applications run perfectly depends on their own code...)

For an example of the directories and files that need to be copied, see this. It could be inflated upon the Windows directory while maintaining its structure.

Here is another good WinSxS resource: http://blogs.msdn.com/martynl/archive/2005/10/13/480880.aspx

For the offical Microsoft rundown entitled "Visual C++ Libraries as Shared Side-by-Side Assemblies", visit their MSDN page. This includes a comprehensive file list.

 

The New Old Quick Fix

 

The 'copy the missing DLL to the application directory' trick can still work. For example, imagine an application requires version 8 of the C run-time and it needs to be run regardless of whether the appropriate Side-by-Side assembly is installed. One can copy msvcr80.dll to the applications directory, but this is not enough as the loader must be instructed to use it. Therefore an additional manifest file Microsoft.VC80.CRT.manifest must also be placed next to the DLL. Its contents causes the local DLL to be used:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright © 1981-2001 Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <noInheritable/>
    <assemblyIdentity 
        type="win32" 
        name="Microsoft.VC80.CRT" 
        version="8.0.50727.1833" 
        processorArchitecture="x86" 
        publicKeyToken="1fc8b3b9a1e18e3b"
    />
    <file name="msvcr80.dll"/>
    <file name="msvcp80.dll"/>
    <file name="msvcm80.dll"/>
</assembly>

 

New developments with VS 2008

 

With the introduction of Side-by-Side linking for C++ applications written with Visual Studio, a new file crtassem.h (found in the VC include directory C:\Program Files\Microsoft Visual Studio 9.0\VC\include) has appeared that describes the manifest information the linker embeds in the compiled output.

For VS 2008 it contains:

#ifndef _VC_ASSEMBLY_PUBLICKEYTOKEN
#define _VC_ASSEMBLY_PUBLICKEYTOKEN "1fc8b3b9a1e18e3b"
#endif
 
#if !defined(_BIND_TO_CURRENT_VCLIBS_VERSION)
  #define _BIND_TO_CURRENT_VCLIBS_VERSION 0
#endif
 
#if !defined(_BIND_TO_CURRENT_CRT_VERSION)
  #if _BIND_TO_CURRENT_VCLIBS_VERSION
    #define _BIND_TO_CURRENT_CRT_VERSION 1
  #else
    #define _BIND_TO_CURRENT_CRT_VERSION 0
  #endif
#endif
 
#ifndef _CRT_ASSEMBLY_VERSION
#if _BIND_TO_CURRENT_CRT_VERSION
#define _CRT_ASSEMBLY_VERSION "9.0.30729.1"
#else
#define _CRT_ASSEMBLY_VERSION "9.0.21022.8"
#endif
#endif
 
#ifndef __LIBRARIES_ASSEMBLY_NAME_PREFIX
#define __LIBRARIES_ASSEMBLY_NAME_PREFIX "Microsoft.VC90"
#endif

As you can see, there are two C run-time assembly versions available. By default the older one (9.0.21022.8) is selected. It is possible to customise the version by defining your own values (e.g. setting _CRT_ASSEMBLY_VERSION). To use the latest version, it is easier to place the following at the very top of your stdafx.h file (before any header that includes any of the C run-time headers, which eventually include crtassem.h):

#define _BIND_TO_CURRENT_VCLIBS_VERSION 1

Or, in the C++ compiler settings, add _BIND_TO_CURRENT_VCLIBS_VERSION=1 to the preprocessor definitions.

However there is a curious twist. As it turns out, you can specify any assembly version you like but the application loader may not honour it. Just because you have certain versions of the C run-time assembly installed, you cannot simply select the one you want your application to use by setting the appropriate information in the embedded manifest. In the end it comes down to certain settings in the registry, namely the Side-by-Side Winners, which are found at:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\SideBySide\Winners

As our example, let's assume that we have defined _BIND_TO_CURRENT_VCLIBS_VERSION=1 and so the manifest is written with version 9.0.30729.1, which can be seen here:

Opening the executable in Dependency Walker reveals something unusual though: the C run-time DLL being loaded is:

c:\windows\winsxs\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.4148_none_5090ab56bcba71c2\MSVCR90.DLL

Its version is 9.0.30729.4148 (found directly after the public key value encoded in the above path). This is higher than was specified in the manifest! Both versions (and older ones) are installed on my system, so why is the loader choosing a different version? The answer lies in the Winners key for this particular assembly (notice the directory and key name have different values after 'none'):

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\SideBySide\Winners\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_none_ea33c8f0b247cd77\9.0

There are three REG_BINARY values present, each with a value of 01 (meaning enabled; 00 means disabled).

  • 9.0.21022.8
  • 9.0.30729.1
  • 9.0.30729.4148

Therefore as the highest version (9.0.30729.4148) is registered as an enabled Winner, the loader automatically picks it, regardless of the version in the manifest. In a way it makes sense, as if you distribute an application linked to an older version of the assembly that you don't have installed (let's assume you have only the latest version of everything) it would be nice if it still worked off the bat, and didn't require you downloading that specific version of the assembly. Therefore backward compatibility is maintained (and we all know that Microsoft prides itself on this).

The funny thing is that if we made up a non-existent version (e.g. I tried 9.0.30729.0) and specified this using _CRT_ASSEMBLY_VERSION, that version would appear in the manifest. Despite this, the loader would still pick the highest Winner and use that, so the application will still run! However, if there is no enabled Winner with a version higher than the one specified, the application will not run and you'll see the usual "This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem." error message.

Comments

The Policies

I was looking all over for helpful information and it seems that no one is mentioning it.

I have setup a machine that never had Visual Studio on it before with VS 2010. Built my project fine. Problem came when I tried to run the debug version of it. That dreaded error that is discussed here about SXS and error reading the manifests and so forth. Turns out one of the 3rdparty DLLs we use is expecting to use MSVCR80D.dll in debug - which is not isntalled by the trusty VS2010. The release version is installed though.

What finally worked after about 3 days of looking is this:
went to a machine with VS2005 ionstall (that is the 8.0 version) and copied the VC80.DebugCRT:
i. directories from c:\Windows\SxS
ii. manifest and catalog files from c:\Windows\SxS\manifests
iii. policy from c:\Windows\SxS\Policies

The policies one is the one I was missing for the longest time and gave me the most run around because the CRT dll required by the manifest for the 3rd party DLL points to a version of the CRT that I do not have from my source machine. But since I could run the debug app on that machine, I knew there is some redirection taking place. The parent article here gave a Winners clue, but that is not on my machine at all (XP Pro SP3) and when I tried to introduced I could never be sure I was creating a healthy registry entry. In despair, I went looking around to see what else there is and decided to open the policy file - if it can be read - ina text viewer. Not only is it text, but it is the redirection of what DLL to use when a version is requested. Now I am sailing in the air.

I have the same problem as

I have the same problem as you mentioned, so could you please explain little more about the steps what did you do to solve the problem. Basically i didn't get you which files did copied from vs2005 machine to which location of target machine.

Thanks and Regards,
Sathish

side-by-side assemblies

Can only a .net application use side-by-side.
if i have an app(exe) developed in VB6 and I want to use .net dll's without registering them can side-by-side do this?

You can do it in VB6 - Do a

You can do it in VB6 - Do a google search for "Make My Manifest"

Thanks for posting

Good of you to come back with the answer! MMM looks like the perfect tool.

win sxs problem

hi, as you mentioned in the above para, I do have three versions of msvcrt90.dll. i want my application to use old version then i create manifest file point to lower version. but still problems exists. did i need to disable the REG_BINARY of latest version?

Problem?

What exactly is the problem you're experiencing? Things to do would be to change the value of the highest Winner(s) to 0 and/or physically move the files you don't want to use out of the WinSxS folder (so the loader can't find them). Be careful though: you might break things by changing/moving those files!

Thank you!

You r pro!
Thank you for your very comprehensive solution!
I wish I met your article sooner!