Did you ever try to Authenticode sign all the assemblies in a solution and package them up into an MSI using a Visual Studio Setup and Deployment project? Well, if you decided to use MSBuild to sign the assemblies, your first attempt may have left you disappointed to find that assemblies packaged up in the MSI were not signed even though you went through all the trouble to sign them using signtool. Anyway, that’s what happened to me.
My strategy was to use MSBuild to build all of the .Net projects first, then funnel the output assemblies through the signtool utility, then build the Setup and Deployment project and then voila: an MSI filled with signed assemblies, but it didn’t work. The Setup and Deployment project smartly decided to check the references and build all of the .net assemblies before packaging and it wiped out all of signing that was just done. Great! My research into this issue uncovered suggestions to use postbuild events on all of the C# projects, which I didn’t want do do because there were many of them, or to use some other packaging solution that I didn’t have. By the way, I was doing Authenticode signing and not Strong Name signing which could have been done from the project properties.
Determined to use MSBuild for the signing, so that I would not have to go through every single C# project and add a nice long signtool command to its postbuild event, the clincher turned out to be the one single solitary prebuild event on the Setup and Deployment project itself. Picture this: just before devenv is about to build the vdproj, it pauses, runs a little MSBuild project that goes through all of the assemblies to sign and verify them, and then it builds the MSI filled with signed assemblies. Yes!
Now, I along the way, I encountered some gotchas:
- I had to sign the assemblies in the obj directory and not the bin directory, because the vdproj was picking up the binaries from the obj output.
- I had to specify the full path for MSBuild ( C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe ) because apparently devenv didn’t load the full environment for the postbuild event.
- I needed a full path for signtool ( $(VS80COMNTOOLS)bin\signtool.exe ) because of the same environment loading problem.
- I had to run devenv on the solution with a project specifier rather than directly on the vdproj because it didn’t work reliably when running directly. Sometimes it would build other times it would fail for an unknown reason. So, I used this command: ( "$(DevEnvDir)\devenv.exe" $(SolutionFullname) /Build "Release|Any CPU" /Project "$(MSISetupDirectory)$(MSISetupName).vdproj" /ProjectConfig Release )
- I had to create an ItemGroup with an entry for each assembly because I no longer had an automatically generated list of items spitting out from an MSBuild task run for the entire sln file. In the postbuild event my MSBuild project was starting from scratch.
In retrospect, the best solution would have been a “Project Only” build from devenv, or even better from MSBuild, but these options do not exist. As a next best alternative, this solution turned out ok.