Visual studio 2013 如果没有硬编码路径,如何在Visual Studio 2013的生成后事件中使用signtool?

Visual studio 2013 如果没有硬编码路径,如何在Visual Studio 2013的生成后事件中使用signtool?,visual-studio-2013,path,post-build-event,signtool,Visual Studio 2013,Path,Post Build Event,Signtool,我已经创建了一个生成后事件,以便在使用以下生成后脚本成功生成应用程序后对其进行代码签名 copy $(TargetPath) $(TargetDir)SignedApp.exe signtool sign /t http://timestamp.verisign.com/scripts/timestamp.dll /a $(TargetDir)SignedApp.exe 我发现错误,“signtool”未被识别为内部或外部命令。 因此,用于构建事件的路径似乎不指向signtool实用程序。当我

我已经创建了一个生成后事件,以便在使用以下生成后脚本成功生成应用程序后对其进行代码签名

copy $(TargetPath) $(TargetDir)SignedApp.exe
signtool sign /t http://timestamp.verisign.com/scripts/timestamp.dll /a $(TargetDir)SignedApp.exe
我发现错误,“signtool”未被识别为内部或外部命令。 因此,用于构建事件的路径似乎不指向signtool实用程序。当我运行VS2013 x86 Native Tools命令提示符时,我可以运行signtool,因为它包含一个指向以下内容的路径:

C:\Program Files (x86)\Windows Kits\8.1\bin\x86
我可以将此路径硬编码到构建事件中

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

然而,这似乎是不可移植的。如何获得为本机命令提示符定义的相同路径,以供生成后事件使用,而无需对其进行硬编码?我查看了宏列表,但没有找到任何有用的宏

我决定的解决方案是:

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
这是@Dennis Kuypers建议4的一个变体。开发人员必须将环境变量SIGNTOOL设置到正确的位置。如果他们未能做到这一点,则尝试一个已知的可能位置。如果失败,则会报告错误,指示他们正确设置SIGNTOOL环境变量

我确实发现有一个环境变量WindowsSdkDir

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

但是,这只是在运行本机命令提示符时设置的,因此在运行生成后事件脚本时没有定义。

我在Visual Studio 2012中遇到了同样的问题,并找到了一种更简单的解决方法。而不是启动Visual Studio直接启动“针对VS2012的开发人员命令提示符”,然后在命令提示符中键入“devenv”以启动Visual Studio。在那之后,signtool对我来说很好。

我首先发现了这个问题,所以我将发布我最终同意的答案

一路上,我看到了另一个答案和一些文档:

我的解决方案是将这个大型PropertyGroup添加到csproj文件中:

<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>

$([MSBuild]::GetRegistryValueFromView('HKEY\U LOCAL\U MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots','KitsRoot10',null,RegistryView.Registry32,RegistryView.Default))
$([MSBuild]::GetRegistryValueFromView('HKEY\U LOCAL\U MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots','KitsRoot81',null,RegistryView.Registry32,RegistryView.Default))
$([MSBuild]::GetRegistryValueFromView('HKEY\U LOCAL\U MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots','KitsRoot',null,RegistryView.Registry32,RegistryView.Default))
$(WindowsKitsRoot)bin\x64\
$(WindowsKitsRoot)bin\$(平台)\
$([System.IO.Directory]::GetDirectory(“$(WindowsKitsRoot)bin”,“10.0.*”)
$(SignToolPathBin.Split(“;”).Length)
$([MSBuild]::添加(-1,$(SignToolPathLen)))
$(SignToolPathBin.Split(“;”).GetValue($(SignToolPathIndex)))\
$(SignToolPathBase)x64\
$(SignToolPathBase)$(平台)\
我需要很多额外的中间属性,因为我机器上的Windows SDK没有在
\bin\x64\signtool.exe
中安装
signtool.exe
,而是在另一个目录级别下安装,这是我绝对不想硬编码的SDK版本


然后在后期构建中,我可以使用这个
“$(SignToolPath)signtool.exe”

1。在回购协议2中包括signtool。包括一个执行签名的exe,通过定位signtool为您提供帮助3。设置具有固定路径的构建服务器(这就是我最后要做的)4。添加一个批处理文件,该文件不在您的VCS中,每个用户都必须编辑(理想情况下编辑一次)。我看到这个解决方案的问题是,当另一个开发人员出现(可能几年后)并尝试构建项目时,它将失败,而没有任何迹象表明他们做错了什么。这需要一种非常规的启动VS的方法,并且需要开发人员知道总是以这种方式启动VS。我想我更喜欢我的解决方案,因为它会向开发人员反馈当事情不起作用时该怎么办,一旦设置了环境变量,那么VS就可以以传统的方式启动。IMO,基于长时间的谷歌搜索和堆栈溢出,这是正确的答案,而不仅仅是VS2013(发表此评论时我正在使用VS2019)。谢谢,@webjprgm。另外,我现在知道了msbuild属性函数,很酷的东西!