With our current developer tools, there's no immediately obvious way to embed a manifest in a managed executable. There are actually a number of ways to do this, and it's really not that hard. I found the hard part was putting together all the pieces and figuring out the options.
Here's how I typically describe the options. (Note that the classification scheme and taxonomy are mine, and don't necessarily represent official Microsoft terminology) :
- The “step-by-step” method involves explicitly completing each step in the end-to-end process: defining a native resource script, compiling it to create a binary resource file, and embedding the binary resources into the executable during the build process
- The “generic” method involves using the mt.exe tool, which directly embeds an external manifest file into an existing executable without having to create and compile a resource script. It’s generic in that it can be used independently of the dev tools and languages used to implement and build the executable. (Mt.exe is commonly used to sign catalogue files and, maybe because that's not a task I typically do, it didn't seem very obvious to me that I could also use it for embedding manifests.)
- The “manifest tool” method involves using specific project options. The only language/tool I'm aware of that provides options for specifying manifest settings is Visual C++ 2005. (Not sure if it's available in VC++ 2003)
The option you use will likely depend on which programming language you’re using and how you build your executable. I have some thoughts around which method I would use for some specific scenarios, and I'll share them after describing each of the three methods.
Method #1 - The Step-By-Step Approach
I like to describe this method first, because it illustrates all the pieces, and steps through the entire process. I think it's easier to make sense of the "generic" method below, which provides some short-cuts, once you understand this method.
1. Create a resource script (.rc) file
- Create a text file (if you're using Visual Studio, right click on the project, select Add->New Item…, and select Text File)
- Save the file as ExeName.rc (where ExeName is the name of your target executable)
- Add the following boilerplate text to the resource file (if you're using Visual Studio, right click on the file and select Open with…, then select Source Code Editor) and replace ExeName with the name of your target executable
#define RT_MANIFEST 24
#define APP_MANIFEST 1
APP_MANIFEST RT_MANIFEST ExeName.exe.manifest
The line above means: take the contents of ExeName.exe.manifest, and include it as a resource of type RT_MANIFEST, whose identifier is APP_MANIFEST. (The value of the manifest resource for an executable should be 1.)
- Ideally, you would specify #include <winuser.h>, rather than directly defining RT_MANIFEST, but it requires doing some extra work to specify command line parameters to define a number of different SDK include folders as well as the VC subfolder of the SDK.
- Instead of having an external manifest file, you can include the contents of the manifest directly inline in the resource script.
2. Compile the resource script file using the native resource compiler (rc.exe)
For those of you who may not be familiar with the native resource compiler, the command line for compiling a resource script is as follows:
rc.exe /r ExeName.rc
This will produce a file named ExeName.res with the resulting binary native resources.
For VS project types that support custom build steps (e.g. C#), you can run this as a Pre-Build step:
- Open the project properties (right-click on the project and select Properties)
- Select the Build Events tab, and enter the following command into the Pre-build event command line text box:
"$(DevEnvDir)..\..\SDK\v2.0\bin\rc.exe" /r "$(ProjectDir)$(TargetName).rc"
The above command makes use of VS-defined macros to construct the path to the rc.exe tool (located in Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin) as well as to determine the input file path. You should be able to use this command directly (in VS 2005), without having to make any text substitutions. (If anyone knows of a more elegant way to define these paths in a location-independent manner, I'd love to hear about it)
3. Embed the binary native resources into the executable during the build process
Option A: Build using Visual Studio 2005/MSBuild
Open the project script in the editor by right-clicking on the project and selecting Unload Project, then right clicking on the project and selecting Edit <project name>
Add the following element to the project script, replacing ExeName with the name of the project
Note: You can ignore the syntax error that appears in the editor (i.e. squiggly blue line under the element name). Win32Resource is the correct element name.
Option B: Build using the command line compilers
If you build your projects using custom command line scripts, C#, VB, and J# all have command line options that enable you to specific the name of a binary native resource file that should be embedded into the resulting executable.
csc /win32res:ExeName.res ExeName.cs
vjc /win32res:ExeName.res ExeName.jsl
vbc /win32resource:ExeName.res ExeName.vb
- Shawn has a post that discusses this method, Adding a UAC Manifest to Managed Code. He includes the manifest directly inline in the .rc file, rather than pointing to an external manifest file.
- Daniel also describes this method in his Vista: User Account Control post
- We asked Ian to walk through these steps on this Channel9 screen cast
[Updated on Feb. 16, 2007] Since writing this, I've learned of a couple of issues that you should be aware of before deciding to use this approach: assembly version information is lost after the binary native resources are embedded into the executable, as are application icons. In the likely case that you care about assembly version information and your application's icon, use Method #2.
Method #2 - The "Generic" Approach (using mt.exe)
The mt.exe tool can be used to embed an input file as a native manifest resource into an already-built .exe file. The command line is as follows:
mt.exe -manifest ExeName.exe.manifest -outputresource:ExeName.exe;#1
This will take a manifest file named ExeName.exe.manifest and embed it into ExeName.exe as a native resource (of type RT_MANIFEST, with an identifier of 1 – see Method #1 above for a description of theses elements)
For VS project types that support custom build steps (e.g. C#), you can define this as a Post-Build step as follows:
- Open the project properties (right-click on the project and select Properties)
- Select the Build Events tab, and enter the following command into the Post-build event command line text box:
"$(DevEnvDir)..\..\SDK\v2.0\bin\mt.exe" -manifest "$(ProjectDir)$(TargetName).exe.manifest" –outputresource:"$(TargetDir)$(TargetFileName)";#1
"$(DevEnvDir)..\..\VC\bin\mt.exe" -manifest "$(ProjectDir)$(TargetName).exe.manifest" –outputresource:"$(TargetDir)$(TargetFileName)";#1
The above command makes use of VS-defined macro variables to construct the path to the mt.exe tool (located in
Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin Program Files\Microsoft Visual Studio 8\VC\Bin) as well as to determine the input and output file paths. You should be able to use this command directly (in VS 2005, at least), without having to make any text substitutions. The double quotes around the file paths enables you to use file paths that contain spaces.
[Updated on Feb. 16, 2007] Since writing this post, I've learned that the version of mt.exe in the SDK folder can cause a crash when the resulting executable is run on Windows XP. Use the more recent version of mt.exe in the VC\Bin folder to avoid this problem. I've modified the paths above to reflect this.
Another lesson learned: running the mt.exe tool as a Post-Build step from VC++ projects replaces the manifest entries added by the project (which typically provide the necessary side-by-side info for loading comctl32.dll v6). So unless you want to provide all necessary manifest entries, the best option seems to be the one I described in a later post: Quick Tip: Manifests and VC++ Projects
Method #3 - Using the Manifest Tool
Visual C++ 2005 is the only tool I'm aware of that provides project properties for specifying manifest options. To find these options, open the project properites dialog, on the left-hand pane expand the Configuration Properties->Manifest Tool node and select Input and Output. You can enter the name of an external manifest file in the Additional Manifest Files field.
I don’t see any of these methods as being particularly complicated. I think the real question is how to integrate one of these methods into your build process. And as I mentioned above, this will likely depend on which programming language you’re using and how you build your executable.
In general, I think I would opt for using mt.exe (the “generic” method) – as long as I could integrate it into my build process. This means I would be using a dev tool that allows me to define a custom post-build step (like C#) or using a command line build script in which I can add an additional entry to call mt.exe at the end.
That said, I can think of at least one very common scenario that I don't believe meets this condition: compiling a VB project within Visual Studio. In this case, I would likely opt for the step-by-step method, making the call to the resource compiler externally, and including the .res file as an item in my project to keep track of it. (I haven’t been able to figure out a way to automatically include the resource compilation into the VB build process, if anyone knows a way in which this can be done I’d love to hear about it).
[Updated on Oct. 31, 2006] Since I wrote this post, I learned that VB actually does have a way to specify custom build steps. I was discussing the options for embedding manifests in assemblies with my colleague Michael Lehman, in the context of how to include this in Project Glidepath, and lamenting the lack of custom build options in VB. Michael thought he remembered otherwise, and we opened the properties for a VB project in Visual Studio 2006 and took a closer look. Under the Compile tab, low and behold - there's a Build Events button in the lower right corner! (Michael, thanks so much for pointing this out!).