Deploying on Windows with DLL Manifest Issues

Posted by Bill Hoffman in Software Process. Viewed 14498 times.
http://kitware.com/blog/home/post/4

Some history and explaining of manifests and DLL hell or:

This application has failed to start

As of Microsoft Visual C++ 2005, visual studio has supported the concept of a side by side assembly. This is basically a bit of XML that describes run time DLL dependencies for an executable or a DLL. The XML text can be embeded inside a binary executable or a DLL. This allows the executable or DLL to specify the exact version of things it depends on. This avoids what is known as DLL hell (http://en.wikipedia.org/wiki/DLL_hell). For more information about manifests you can see this article: http://en.wikipedia.org/wiki/Side-by-Side_Assembly. However, the manifests can cause their own set of problems.

Since the exact version of a DLL can now be specified, merely having a DLL with the right name is no longer enough to be able to use it. When the DLL in question is a Microsoft run time library, there are two ways to get the DLL onto the deployment machine. You can install it into the system as an admin, or you can copy the DLL and put it next to your executable. However, you must also put an additional manifest file next to the DLL so the loader will know it is the right one.

If you do some google searches on this: "Results 1 - 10 of about 6,690,000 for This application has failed to start", it is pretty clear this is a problem for lots of people.

CMake/CPack and InstallRequiredSystemLibraries.cmake can help
If you are using CMake/CPack, you can use InstallRequiredSystemLibraries.cmake to install the DLLs and the manifest file from the run time of your compiler. This all works great, except when it does not work... The main problem comes about when a DLL or executable ends up depending on multiple versions of the MS run time libraries, which causes applications to fail to open. The tricky part is that this will work on the machine that you are building the software on becuase it will have the system installed versions of all DLL's that your exe or DLL depends on installed from the compiler install.

There are two known ways that this can happen:

• VS (2009 Service Pack 1): In this version of VS 2009, the XML text file that ships from MS has the wrong version number in it. So, the .exe files created by that compiler have embedded manifest files that do not match the XML file. The fix for this is to hand edit the .manifest XML files in the VS 2009 install tree and make the version match what the compiler is putting into the .exe files. The files can be found in the redist folder for your VS 9 installation tree. A common location is here:
        c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\redist/amd64/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest
c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT\Microsoft.VC90.CRT.manifest
• VS (any version): If a service pack or security update is installed for VS, it may change the version of the MS runtime libraries that the .exe needs to run. If that happens, ALL code needs to be recompiled before you can create an .exe that can be shipped from that machine. The problem is that the .exe files will have TWO versions of the runtime embedded in them, one for any code that was compiled BEFORE the update, and one for files compiled after the update. This includes third party libraries like Qt. To see if this is a problem, you can load a .exe into emacs or some other text editor and look for something like this:
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.DebugCRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency> 

There should only be ONE version="9.0.21022.8" requirement, and it has to match the one found in this file:
Microsoft.VC90.CRT.manifest

If there is more than one of these lines (with different versions):

name="Microsoft.VC90" version="9.0.21022.8"

in the .exe, then you have a problem... and it shows that the code was not all compiled with the same exact version of the compiler.

However, looking at binary and exe files in a text editor can be a bit of a pain... So, I created a new module in CMake that can be used to diagnose manifest issues. In CVS CMake (soon to be git, but that is another blog post..), you can find the new module that can be used to verify an install tree. The script is in CVS CMake here: Modules/CMakeVerifyManifest.cmake. To run the script, cd into the binary install directory for your project and run cmake -P /path/to/CMakeVerifyManifest.cmake. Here is an example:

For CMake 2.8, you get this output:

$c:/Program\ Files/CMake\ 2.8/bin/cmake -P c:/hoffman/My\ Builds/CMake/Modules/CMakeVerifyManifest.cmake Versions found in C:/Program Files/CMake 2.8/bin/Microsoft.VC90.CRT.manifest: 9.0.21022.8 Information: no embeded manifest in: C:/Program Files/CMake 2.8/bin/msvcm90.DLL Information: no embeded manifest in: C:/Program Files/CMake 2.8/bin/msvcp90.DLL Information: no embeded manifest in: C:/Program Files/CMake 2.8/bin/msvcr90.DLL This script will return non-zero if there are errors. So, it could be used as the final install rule for a project, in which case CPack would not run if there were errors during the install. The script should be able to detect both of the failure cases mentioned above. One last problem.... There is one other odd case that seems to be a problem. If you have a Qt plugin that is in a different directory than the main .exe of your project, even if you copy the DLL and the .manifest file for the plugin into the plugin directory, it will not load the plugin on Windows XP service pack 2! To get around this issue with XP service pack 2, you have to remove the embeded manifest from the plugin DLL. To do this in a CMake project, you do this: if (MSVC) # Do not generate manifests for the plugins - caused issues loading plugins set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /MANIFEST:NO")
endif()

BTW, Marcus Hanwell, discovered this fix while working on Avogadro. :)

The end for now

Since every time I think I have this all figured out something else comes up, if I have something wrong in here, or if you find a new case, let me know. Thanks!

-Bill

14 readers like this post. Do you like this post? I really do!

By Michael Wild

"CVS CMake (soon to be git, but that is another blog post..)" - Oh joy!

By Marcus Hanwell

I am waiting with eager anticipation for this move too!

By David Cole

See this web page on MSDN for information about how assemblies are searched at runtime for dlls with the appropriate manifest match:
http://msdn.microsoft.com/en-us/library/aa374224(v=vs.85).aspx
posted @ 2014-08-01 13:51  kevinzhwl  阅读(...)  评论(... 编辑 收藏