So I have been working recently on making the Mission Control (MC) repository, a little more developer/user friendly. I am saying “user” as I am considering developers to really be users of repositories.
Now, most of developers would be able to pull down MC repository from GitHub and figure out how to build it from there, but I have to admin that even for some experienced ones, it has been a little bit of a drag. Anyways, the task that HOK asked me to perform was to clean up a little in that repo, to make it easier to pull, build, and deploy.
First task that I ran into when pulling MC, was the fact that it would error out on Post Build tasks that were supposed to sign the assemblies. Now, normally signing an assembly is not a big deal. All you have to do is call the signtool.exe with a path to the PFX certificate, and a password. I talked about it in detail in this post: Link. That method is good, but only if you are developing at home and are not too worried about putting your password into the Post Build method. Of course when I was at HOK, we were not going to do that. Instead we had our IT manage the certificates, and install them on user machines. As a developer, I was added to special Domain Group at HOK, that gave me access to a special certificate that I could use to sign my assemblies. Naturally, all of the MC assemblies then depended on me being on HOK network, and having that certificate installed to finish the build process. That’s not very flexible when we are talking about Open Source projects.
MC is now an Open Source project, and it is likely to be used by other developers. They will not have that certificate installed on their machines. Even if they did, it would likely not be named just like mine etc. so either way my Post Build methods would fail. So, how do we make the Code Signing, universal, and flexible, so others can quickly hook into it.
The solution that I chose was nothing revolutionary. The good old Power Shell scripts. Instead of calling a specific line of code from Post Build, I decided to call a BAT file, that would execute whatever code was in it. Now, it’s up to the user to decide what code to put into their BAT file, and since this BAT file is also ignored by Git, you can put your password into it, and it won’t matter because it would not be committed.
OK, so we are calling a batch script from a Post Build. Really? Does that really require a post? Well, normally no, but in this case, I ran into some issues and wanted to make sure that others were aware of a few things to look out for.
OK, so couple of things to take note of here:
- " is used where you would normally use ” (double quote). You want to put double quotation marks around file paths (path to powershell.exe) that might contain spaces.
- %WINDIR% you can use Environmental Variables in your file path, and they will be resolved properly.
- You might be wondering why am I even calling powershell.exe using the full file path. That’s because I was running into issues where if powershell.exe was called from Post Build, it would call an x64 version of the PowerShell and that would in return cause issues later when I was accessing Registry (Registry also has a x64 and x86 versions)
- $(SolutionDir) you can also take advantage of Visual Studio’s shortcuts to Solution Directory or Target File Path etc.
- ..\ is a shorthand for telling the script that I want to go one directory up in the hierarchy so if you want to go two up use ..\..\ etc.
So the above works pretty well. Now you have a Post Build event where you just call a BAT file. Now, let’s have a look at the BAT file itself to talk about some of the things that you can do there.
The above script has a few more things that are worth talking about:
- set DLL_PATH=%1 this allows us to set the variable named DLL_PATH using one of the arguments that the BAT file was called with. In this case it’s a file path to the $(TargetPath). This passes the DLL file path into the BAT script to tell it which one of the DLLs I wanted to sign.
- set PFX_PATH simply sets the file path to a location somewhere on my drive where I keep the PFX certificate that I want to use to sign this DLL. Now others can keep them elsewhere. The whole point is that you can define it here.
- set KEY_NAME=”
HKEY_LOCAL_MACHINE\SOFTWARE\HOK “ I am not going to expose my password for the PFX certificate. Instead, I saved it in the Registry so that the BAT file can read that value every time it needs to use the certificate to sign a DLL. You can submit your BAT script to the repository, but you won’t be giving up your password.
- set VALUE_NAME is just a name of the key that I stored the password under.
- that next line reads the password from the Registry, and sets it to a local variable called PFX_PASS so that I can use it in the next line of code.
- finally a call to signtool.exe, and again I am being specific about which version of the signtool.exe I am using here.
- setlocal and endlocal calls are supposed to constrain the scope of the script so that password is not revealed outside of the scope of this BAT file.
All right! Looks like we got a viable solution. Everyone now will be able to pull MC repo, place their own version of the above BAT file in the folder, and when you build the solutions, you will sign the DLLs with your own certificate, or you can simply add an empty BAT file to skip the signing. That’s all dandy. There is only one more issue that I was running into: spaces in file paths. To be exact, some of the solutions in that MC repo, had more than a few levels that I would have to move up in the directory, before I would get to the _postBuild folder. They also had spaces, and that was tripping up the Post Build Target. The solution that I was able to find was to call the powershell.exe a little differently:
Notice a few changes:
- we are calling powershell.exe with extra permission flags -NonInteractive -executionpolicy Unrestricted
- we are also calling it with explicit -command flag which allows us to use a little different syntax for the command arguments. This helps us better control file paths and avoid errors related to spaces
- Here’s a link to a more detailed explanation of this method: Link
Anyways, this change also required that I made a small change to the actual BAT script:
Notice the %windir%\Sysnative\reg line where I had to specifically target the 32-bit Registry executable to make sure that the right version of the Registry was queried for the password.
We got to the end. You can see from the above, that we can easily make some of the routines in our Post Build events more generic simply when we switch over from running specific pieces of code, to running BAT files, where user has control over what is executed. I understand that it’s “shifting the responsibility” to the end user, which in this case is a developer, but still. I am not too worried. The idea was to make things be more customize-able and flexible. Now the user can choose if they want to code sign their assemblies, and if they do, they can use their own certificate.
PS. Special thanks to the bunch at HOK and especially:
- Greg for making my wish, of one day working on Open Source projects and being paid for it, a reality.
- Dan for being gracious enough to review my code, and help me push some of these changes through.
very clear and good article easy to understand. Thank you