Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use signtool in post build event for Visual Studio 2013 without hard coding path?

I've created a post build event to do code signing of the application after a successful build with the following post build script.

copy $(TargetPath) $(TargetDir)SignedApp.exe
signtool sign /t http://timestamp.verisign.com/scripts/timestamp.dll /a $(TargetDir)SignedApp.exe

I get the error 'signtool' is not recognized as an internal or external command. So it seems the path used for the build event doesn't point to the signtool utility. When I run the VS2013 x86 Native Tools Command Prompt I can run signtool as it includes a path which points to:

C:\Program Files (x86)\Windows Kits\8.1\bin\x86

I could hard-code this path into my build event

"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\signtool" sign /t http://timestamp.verisign.com/scripts/timestamp.dll /a $(TargetDir)SignedApp.exe

However that seems non-portable. How do I get the same path defined for the Native Command Prompt to be used by my post build event without hard coding it? I've looked at the list of macros but haven't found any that would be useful.

like image 672
JonN Avatar asked Jun 14 '17 23:06

JonN


People also ask

How do I get SignTool?

SignTool is available as part of the Windows SDK, which you can download from https://developer.microsoft.com/windows/downloads/windows-10-sdk/. The Windows 10 SDK, Windows 10 HLK, Windows 10 WDK and Windows 10 ADK builds 20236 and later require specifying the digest algorithm.

How do I debug a post build event in Visual Studio?

Another way is to check the bin\debug dir for 'PreBuildEvent. bat' or 'PostBuildEvent. bat' which are the file that Visual Studio creates and run during the build events, if there is an error the files remain in the output dir and you can run them manually and spot the error.

Which of the following build events will occur if the error occurs before the linking phase 1 pre build?

If an error occurs in the build, the post-build event does not occur; if the error occurs before the linking phase, neither the pre-link nor the post-build event occurs. Additionally, if no files need to be linked, the pre-link event does not occur.


2 Answers

I found this question first so I'll post the answer I eventually went with.

Along the way I looked at this other answer and some docs:

Path to SignTool.exe or "Windows Kits" directory when using Visual Studio 2012

https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions?view=vs-2017

My solution ended up being this big PropertyGroup added into the csproj file:

<PropertyGroup>
  <!-- Find Windows Kit path and then SignTool path for the post-build event -->
  <WindowsKitsRoot>$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots', 'KitsRoot10', null, RegistryView.Registry32, RegistryView.Default))</WindowsKitsRoot>
  <WindowsKitsRoot Condition="'$(WindowsKitsRoot)' == ''">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots', 'KitsRoot81', null, RegistryView.Registry32, RegistryView.Default))</WindowsKitsRoot>
  <WindowsKitsRoot Condition="'$(WindowsKitsRoot)' == ''">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots', 'KitsRoot', null, RegistryView.Registry32, RegistryView.Default))</WindowsKitsRoot>
  <SignToolPath Condition="'$(SignToolPath)' == '' And '$(Platform)' == 'AnyCPU' and Exists('$(WindowsKitsRoot)bin\x64\signtool.exe')">$(WindowsKitsRoot)bin\x64\</SignToolPath>
  <SignToolPath Condition="'$(SignToolPath)' == '' And Exists('$(WindowsKitsRoot)bin\$(Platform)\signtool.exe')">$(WindowsKitsRoot)bin\$(Platform)\</SignToolPath>
  <SignToolPathBin Condition="'$(SignToolPath)' == ''">$([System.IO.Directory]::GetDirectories('$(WindowsKitsRoot)bin',"10.0.*"))</SignToolPathBin>
  <SignToolPathLen Condition="'$(SignToolPathBin)' != ''">$(SignToolPathBin.Split(';').Length)</SignToolPathLen>
  <SignToolPathIndex Condition="'$(SignToolPathLen)' != ''">$([MSBuild]::Add(-1, $(SignToolPathLen)))</SignToolPathIndex>
  <SignToolPathBase Condition="'$(SignToolPathIndex)' != ''">$(SignToolPathBin.Split(';').GetValue($(SignToolPathIndex)))\</SignToolPathBase>
  <SignToolPath Condition="'$(SignToolPath)' == '' And '$(SignToolPathBase)' != '' And '$(Platform)' == 'AnyCPU'">$(SignToolPathBase)x64\</SignToolPath>
  <SignToolPath Condition="'$(SignToolPath)' == '' And '$(SignToolPathBase)' != ''">$(SignToolPathBase)$(Platform)\</SignToolPath>
</PropertyGroup>

I need lots of the extra intermediary properties because the Windows SDK on my machine doesn't install signtool.exe in <root>\bin\x64\signtool.exe but rather under another directory level that is the version of the SDK which I definitely do not want to hard-code.

And then in the post-build I can use this "$(SignToolPath)signtool.exe"

like image 158
webjprgm Avatar answered Sep 19 '22 00:09

webjprgm


The solution I decided on was:

REM If SIGNTOOL environment variable is not set then try setting it to a known location
if "%SIGNTOOL%"=="" set SIGNTOOL=%ProgramFiles(x86)%\Windows Kits\8.1\bin\x86\signtool.exe
REM Check to see if the signtool utility is missing
if exist "%SIGNTOOL%" goto OK1
    REM Give error that SIGNTOOL environment variable needs to be set
    echo "Must set environment variable SIGNTOOL to full path for signtool.exe code signing utility"
    echo Location is of the form "C:\Program Files (x86)\Windows Kits\8.1\x86\bin\signtool.exe"
    exit -1
:OK1
echo Copying $(TargetFileName) to $(TargetDir)SignedApp.exe
copy $(TargetPath) $(TargetDir)SignedApp.exe
"%SIGNTOOL%" sign /t http://timestamp.verisign.com/scripts/timestamp.dll /a $(TargetDir)SignedApp.exe

This was a variation on @Dennis Kuypers suggestion #4. The developer must set environment variable SIGNTOOL to the correct location. If they fail to do so then one known possible location is attempted. If that fails then error is reported instructing them to set SIGNTOOL env var appropriately.

I did discover there is an environment variable WindowsSdkDir

WindowsSdkDir=C:\Program Files (x86)\Windows Kits\8.1\

But again, this was set only when running the Native Command Prompt and thus was not defined when running the post build event script.

like image 30
JonN Avatar answered Sep 19 '22 00:09

JonN