Problems with custom actions that depend on the Visual C++ 8.0 runtime files on Windows Vista or Windows Server 2008

I recently heard about a couple of scenarios that were causing problems for folks creating MSI-based setup packages.  In one case, the developer was installing a service and attempting to start it using the ServiceControl table, but it failed when running setup on Windows Vista or Windows Server 2008.  In another case, the developer was attempting to self-register a DLL and it failed when running setup on Windows Vista or Windows Server 2008 (such as in the scenario described here).

In both of these scenarios, the error message "Activation context generation failed for <assembly name>" was logged in the event log.  The error code seen in these scenarios was -2147010895, which is 0x800736B1 in hexadecimal and translates to ERROR_SXS_CANT_GEN_ACTCTX.

In both cases, the binaries in question had a dependency on the Visual C++ 8.0 runtime files, and the MSI included the MSMs to install the VC 8.0 runtime files as part of the same setup.

Root cause of this side-by-side activation error

There is a tricky issue with the VC 8.0 runtime files that only affects Windows Vista and later versions of Windows and is does not appear to be well documented yet.  Specifically, on Windows Vista and later, the VC 8.0 runtime files are installed to the WinSxS cache as global assemblies, whereas on downlevel platforms such as Windows XP or Windows Server 2003, the VC 8.0 runtime files are installed using standard Windows Installer file table entries.

In Windows Installer, you cannot use global assemblies until the installation transaction has been commited.  This means that only commit custom actions or custom actions sequenced to run after InstallFinalize will be able to use global assemblies that are being installed as part of the same MSI.

In the 2 scenarios I described above, the MSI was attempting to use binaries that depended on the VC 8.0 runtime files before they were fully installed to the global assembly cache, and the binaries failed to run and logged a missing dependency error.

Possible workarounds to fix an MSI that fails with a side-by-side activation error

There are a few possible workarounds for this type of scenario:

  • Install the VC 8.0 runtimes using the redistributable package via a setup chainer instead of merging in the MSMs.  The redistributable packages can be downloaded from the following locations:  x86; x64; ia64
  • Install private copies of the VC 8.0 runtime files.  This option is described in the section titled Deploying Visual C++ library DLLs as private assemblies in this MSDN post.
  • Statically link to the VC 8.0 runtimes when building your binaries so that they will not depend on the global assemblies

If none of the above are possible, you can also use one of these workarounds depending on the exact scenario you need to support:

  • In the case of service installation, use a commit custom action or a custom action scheduled after InstallFinalize.  You cannot use the ServiceInstall or ServiceControl table if the service depends on any global assemblies (such as the VC 8.0 runtime files or a managed .NET Framework assembly).  This scenario is specifically described in the ServiceControl table documentation on MSDN.
  • In the case of self-registration, convert the self-registration information to standard MSI authoring (registry keys in most cases) and stop using self-registration.  If that is not possible for some reason, you must use a commit custom action or a custom action scheduled after InstallFinalize to run self-registration instead of the SelfReg table.

A note about eliminating self-registration

Sometimes, it can be difficult to convert self-registration to standard MSI authoring, especially in cases like the ATL project template in Visual Studio.   The ATL project only has the following code for the DllRegisterServer function:

STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

The ATL project contains a .rgs file that is embedded as a resource when the binary is built, and some specific code within ATL parses that .rgs file to replace some tokens to determine what registry entries to write when running DllRegisterServer.

In cases like this where you do not have direct ownership of the self-registration code in your binary, it is most effective to use a self-registration capturing tool such as the Tallow tool that is part of the WiX v2.0 toolset or the Heat tool that is a part of the WiX v3.0 toolset.

<update date="3/21/2008"> Added notes to indicate that this issue will affect not only Windows Vista but also other versions of Windows that shipped after Vista such as Windows Server 2008. </update>

<update date="2/20/2009"> Added a link to an MSDN topic that describes how to deploy Visual C++ DLLs as private assemblies. </update>