views:

444

answers:

3

We have a 32 bit mixed C/C++ application that we are trying to deploy to the world. It naturally uses C and C++ runtime DLLs. We are using VS 2005.

The manifest constructed by VS2005 is the following:

 <?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.42" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
     </dependentAssembly>
   </dependency>
 </assembly>

We ship this as a file in the same directory as the "application", named (changed to protect the innocent) "application.exe.manifest".

On the face of it, it is sort of reasonable. But, in installing on some systems, we get the message when "application.exe" is launched:

This application has failed to start because the application configuration is incorrect

One way to cure this is to run VCRedist_x86.exe from MSDN. (Unfortunately, while we can run it, we don't know exactly what it is doing. It appears to be parking DLLs in the SxS directories. But what else does it do?)

a) The MS docs seem to indicate that the assembly must have an assemblyIdentity tag directly underneath the assembly tag, that names the application itself. This is clearly missing here, but the manifest seems to partially work in that if we remove it, the application doesn't start even if the DLLs are present.

b) Remarkably the assembly doesn't mention the C runtime DLL. Do I need to just add that by hand?

c) We dont want to be dependent on whether the right version DLL is present on the target machine. Assuming that the assembly makes it clear which DLLs to use, how is that we can ensure the DLLs we need are on the target system? (In particular, we don't want run VCRedist or ask our customer to do this). Before assemblies came along, we solved this problem by simply placing the C and C++ DLLs in the same directory as the application .exe file, and Windows would look there first to pick them up. Can we still ship the C and C++ DLLs in the same directory? I can't figure out from the MS docs I can find how the SxS finds the appropriate dependent assemblies.

Any help appreciated.

A: 

You could link the C/C++ runtime statically.

You can change the library linkage in the C/C++ compiler options in the Code Generation section. Change the entry from Multithreaded [Debug] DLL to Multithreaded [Debug].

Your DLL will then contain the required parts of the runtime, and the separate installation is not required.

Timbo
Thanks for the suggestion. That's an option. But I'd like to understand how the manifest stuff works, too.
Ira Baxter
A: 

I've never understood how the manifest stuff all hangs together ... but instead of placing the C runtime DLLs in the same directory as your exe try copying the whole 'Microsoft.VC90.CRT' folder from the redist folder in the Visual Studio install (C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT on my machine).

I believe Microsoft encourages the use of shared libraries and running the official redist since it installs the libraries 'properly' and means they can patch security issues that may be found in them.

Rob Walker
... try copying the whole ... folder ... to where? And *who* should do this copying (I think we're talking about an installer here, since that's how we are delivering our application). And I'm puzzled... I've installed lots of downloads, and I've NEVER been asked to run VCRedist by a downloaded tool. Show, how can the libraries be shared in practice?
Ira Baxter
Copy the folder to the same directory as your exe.Typically the installer will run the VCRedist piece itself (I believe it is also available as a msi module).
Rob Walker
This is indeed the correct thing to do. Fusion (the SxS loader) will probe for "assemblyIdentity/@name".manifest (eg: Microsoft.VC80.CRT.manifest) in the same directory as the executable to resolve assemblies not stored in the SxS assembly cache.If your customer does already have the CRT redist installed it will use the copy in the SxS folder instead, giving you security updates and may improve app startup and memory use if some other running application is already using the SxS CRT.
Nathan Howell
@Rob/Nathan: you both agree that "the 3 DLLs" (which? the only obvious one is VC80.CRT) should be placed by our installer in our application directory. Rob seems to suggest our installer should also run VCRedist (it wasn't obvious that InstallShield can do this but I'll go look). If we ran VCRedist, why would we need the DLLs in our application directory?
Ira Baxter
The best approach is to run VCRedist and not do anything else with copying DLLs.If you want to avoid that then copy the folder "Microsoft.VC80.CRT" (not just its contents) from "C:\Program Files (x86)\Microsoft Visual Studio 8\VC\redist\x86" to the same folder as your exe. There are 3 dlls within this folder.
Rob Walker
+1  A: 

a) The manifest xml validation clearly has some problems. Depending on the version of Windows this may or may not be an issue. Since so many applications don't correctly follow the schema (and because it was never properly enforced), I doubt it will ever be strict here.

b) The C runtime DLL is referenced in the Microsoft.VC80.CRT.manifest file, pulling it in to to the loader dependency graph. Providing you have a dependency on the manifest, you will also implicitly have a dependency on the DLL.

c) Per my earlier comment, the correct thing to do (aside from installing the latest redist system wide) is to put the CRT manifest and all three DLLs in your application directory. This is poorly documented under SxS: Private Assemblies and Installing Side-by-side Assemblies as Private Assemblies. The probe order is defined in Assembly Searching Sequence.

Generally a SxS binding failure will put an entry in the Application (for Vista+) or System Event Log (pre Vista) describing the error.

Activation context generation failed for "C:\TEMP\sxs\PEVerify.exe".Error in manifest or policy file "C:\TEMP\sxs\Microsoft.VC90.CRT.MANIFEST" on line 4.
Component identity found in manifest does not match the identity of the component requested.
Reference is Microsoft.VC90.CRT,processorArchitecture="x86",publicKeyToken="1fc8b3b9a18e3b",type="win32",version="9.0.21022.8".
Definition is Microsoft.VC90.CRT,processorArchitecture="x86",publicKeyToken="1fc8b3b9a1e18e3b",type="win32",version="9.0.30729.1".
Please use sxstrace.exe for detailed diagnosis.

You can use sxstrace.exe in (Vista+) to see what the loader is actually doing. Junfeng covers this in more detail in Diagnosing SideBySide failures.

To get a better understanding of what is happening at runtime (after the manifest has been parsed and dependencies located), enable "Show Loader Snaps" for your image file (just the filename and extension, do not enter a directory name, like so: "notepad.exe") using gflags.exe. Run your application under windbg (Visual Studio's debugger may also work) and look at the output. Make sure to disable loader snaps when you're done debugging since it will slow down the application even when no debugger is attached. Sample output looks like this:

2d6c:36b4 @ 1246428223 - LdrpHandleOneOldFormatImportDescriptor - INFO: DLL "C:\Program Files\Microsoft SDKs\Windows\v6.1\Bin\PEVerify.exe" imports "MSVCR90.dll"
2d6c:36b4 @ 1246428223 - LdrpMapDll - INFO: Mapping static redirected DLL "C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.4148_none_5090ab56bcba71c2\MSVCR90.dll"
ModLoad: 4fbd0000 4fc73000   C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.4148_none_5090ab56bcba71c2\MSVCR90.dll
2d6c:36b4 @ 1246428285 - LdrpMapDll - INFO: Mapped DLL "C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.4148_none_5090ab56bcba71c2\MSVCR90.dll" at address 4FBD0000
2d6c:36b4 @ 1246428285 - LdrpHandleOneOldFormatImportDescriptor - INFO: DLL "C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.4148_none_5090ab56bcba71c2\MSVCR90.dll" imports "KERNEL32.dll"
Nathan Howell