C# 我应该如何在msbuild脚本中引用sn.exe?

C# 我应该如何在msbuild脚本中引用sn.exe?,c#,.net,msbuild,code-signing,assembly-signing,C#,.net,Msbuild,Code Signing,Assembly Signing,我需要在生成完成后重新签名我的程序集(并且我已经对它做了一些其他的事情),因此我首先添加了一个名为C:\ProgramFiles(x86)\Microsoft SDK\Windows\v8.0A\bin\NETFX 4.0 Tools\sn.exe的任务。这必须适用于其他开发人员/环境,因此我希望我可以从该文件夹中复制sn.exe和sn.exe.config,并将其存储在我们的代码存储库中,这样我就可以始终从已知位置调用它的通用版本 sn.exe在sdk目录之外独立崩溃,因此我想知道如何在不知道

我需要在生成完成后重新签名我的程序集(并且我已经对它做了一些其他的事情),因此我首先添加了一个名为
C:\ProgramFiles(x86)\Microsoft SDK\Windows\v8.0A\bin\NETFX 4.0 Tools\sn.exe的
任务。这必须适用于其他开发人员/环境,因此我希望我可以从该文件夹中复制
sn.exe
sn.exe.config
,并将其存储在我们的代码存储库中,这样我就可以始终从已知位置调用它的通用版本


sn.exe
在sdk目录之外独立崩溃,因此我想知道如何在不知道它将位于哪个路径下的情况下引用它。不同的人有不同的环境(x86与x64、不同的安装目录、不同的版本),因此我希望能够轻松地参考该工具的最新版本(或者任何版本)。似乎是一个足够简单的工具,也许有另一种方法可以使用另一个工具/命令/msbuild任务对程序集进行签名?任何帮助都将不胜感激。

您可以在引用可执行文件的每台开发计算机上创建一个环境变量,MSBuild允许您将其作为属性引用

因此,通过系统属性的高级选项卡创建环境变量。我通常只创建一个系统环境变量,而不是一个作用于当前用户的变量。您必须重新启动VisualStudio,它才能拾取它

然后,在MSBuild中引用它:

<Exec Command="$(SnExe)">


其中,
SnExe
是您定义的环境变量。

发现有一个名为“GetFrameworkDKPath”的任务将获取Windows SDK位置。从那时起,我必须测试sn.exe是否直接存在于
bin
文件夹中,或者它是否存在于
bin\netfx4.0tools\
中。到目前为止似乎是可靠的

<PropertyGroup>
  <SNExePath>NotSet</SNExePath>
</PropertyGroup>

<!-- Sometimes theres nothing in the WindowsSdkPath dir and there's stuff in a deeper folder called 'NETFX 4.0 Tools'. -->
<Target Name="GetSNPath" BeforeTargets="AfterBuild">
  <GetFrameworkSdkPath>
    <Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
  </GetFrameworkSdkPath>
  <PropertyGroup>
    <SNExePath>$(WindowsSdkPath)bin\sn.exe</SNExePath>
  </PropertyGroup>
  <PropertyGroup>
    <SNExePath Condition="!Exists($(SNExePath))">$(WindowsSdkPath)bin\NETFX 4.0 Tools\sn.exe</SNExePath>
  </PropertyGroup>
</Target>  

<Target Name="AfterBuild">
  <Exec Command="$(SNExePath) -R $(TargetPath) $(SignatureFile)" />
</Target>

NotSet
$(WindowsSdkPath)bin\sn.exe
$(WindowsSdkPath)bin\NETFX 4.0 Tools\sn.exe

要以适合大多数人的方式在msbuild脚本中正确引用
sn
sqlmetal
(我所追求的)等工具,必须考虑操作环境和框架实现的不同方面。主要有两种情况:Microsoft Windows和Microsoft的框架实现,然后是其他一切(我指的是Mono/unix)。最后列出了一个支持我所能想到的情况的正确方法示例

微软 查找
sn
或其他类似工具在Windows中的位置的正确方法是从开始,如

但是,正如问题所示,无法直接确定
sn
或其他刀具在FrameworkDKPath中的确切位置。参考答案表明,在FrameworkSdkPath下,工具可以驻留的文件夹只有
bin
bin/NETFX 4.0 tools
。但是,也可以使用其他值(Visual Studio 2013预览版使用
bin/NETFX 4.5.1工具
)。因此,搜索
sn
的唯一正确方法是使用glob表达式或递归搜索它。我很难理解如何使用MSBuild进行全局扩展,而内置的MSBuild任务似乎不支持在FrameworkSdkPath下搜索特定的实用程序。但是,cmd具有此功能,可用于执行搜索。结果类似于以下msbuild代码:

<Target Name="GetSNPath" BeforeTargets="AfterBuild">
  <GetFrameworkSdkPath>
    <Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
  </GetFrameworkSdkPath>
  <Exec Command="WHERE /r &quot;$(WindowsSdkPath.TrimEnd('\\'))&quot; sn &gt; sn-path.txt"  />
  <ReadLinesFromFile File="sn-path.txt">
    <Output TaskParameter="Lines" PropertyName="SNPath"/>
  </ReadLinesFromFile>
  <Delete Files="sn-path.txt" />
  <PropertyGroup>
    <SNPath>$([System.Text.RegularExpressions.Regex]::Replace('$(SNPath)', ';.*', ''))</SNPath>
  </PropertyGroup>
</Target>
利用这一点,我们得到的
GetSNPath
任务如下所示:

<Target Name="GetSNPath" BeforeTargets="AfterBuild">
  <GetFrameworkSdkPath>
    <Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
  </GetFrameworkSdkPath>
  <Exec Command=":; echo sn &gt; sn-path.txt; exit $?
WHERE /r &quot;$(WindowsSdkPath.TrimEnd('\\'))&quot; sn &gt; sn-path.txt"  />
  <ReadLinesFromFile File="sn-path.txt">
    <Output TaskParameter="Lines" PropertyName="SNPath"/>
  </ReadLinesFromFile>
  <Delete Files="sn-path.txt" />
  <PropertyGroup>
    <SNPath>$([System.Text.RegularExpressions.Regex]::Replace('$(SNPath)', ';.*', ''))</SNPath>
  </PropertyGroup>
</Target>

$([System.Text.RegularExpressions.Regex]::替换(“$(SNPath)”;*”,“”))

结果是找到调用
sn
所需字符串的可移植方法。最后一个解决方案允许您同时支持Microsoft及其msbuild以及使用xbuild的所有其他平台。它还克服了将
bin\NETFX 4.0工具
硬编码到.csproj文件中的缺点,以同时支持Microsoft工具的未来版本和当前版本。

我目前使用的方法包括使用属性函数在framework SDK路径下搜索SN.exe-ala:


$([System.IO.Directory]::GetFiles(“$(DotNetFrameworkDir)”,“sn.exe”,SearchOption.AllDirectories)[0])
到目前为止,我只在Visual Studio 2013上亲自测试过这个问题,但文档表明它应该回到Visual Studio 2010。

在类似的情况下,$(SDK40ToolsPath)变量为我提供了价值。这就不需要知道安装了哪些特定版本的工具,例如:

  <PropertyGroup>
    <XsdExePath>$(SDK40ToolsPath)xsd.exe</XsdExePath>
  </PropertyGroup>
  <Target Name="BeforeBuild">
    <ItemGroup>
      <xsd Include="Objects.xsd" />
    </ItemGroup>
    <Exec Command="&quot;$(XsdExePath)&quot; @(xsd) /c /namespace:Blah.Objects" />
  </Target>

$(SDK40ToolsPath)xsd.exe

指向.NET Framwork工具(如sn.exe)的路径实际上是使用GetFramworkSDKPath(如上所述)和(几乎)TargetFrameworkVersion(如项目文件中所指定)的组合。例如,您基本上需要从TargetFrameworkVersion中删除初始的“v”,并将各个部分连接在一起以形成工具的路径


$([System.Text.RegularExpressions.Regex]::替换('$(TargetFrameworkVersion)''v','')
NETFX$(框架版本)工具
$(WindowsSdkPath)bin\$(NETFXVersion)\sn.exe

sn.exe有一个与之关联的配置文件。我注意到,如果你不向exe提供任何参数,它也会崩溃。我已经尝试将sn.exe.config与它一起复制,但这似乎也不起作用。甚至不允许我运行
sn.exe-h
以获取帮助信息而不会崩溃。您可以尝试下载并查看是否有任何依赖项丢失肯定比我原来的答案更彻底+1.这不是很容易移植,需要更改机器级别才能运行构建。这里的目标是允许以机器无关的方式从.NETSDK查找工具。
  <PropertyGroup>
    <XsdExePath>$(SDK40ToolsPath)xsd.exe</XsdExePath>
  </PropertyGroup>
  <Target Name="BeforeBuild">
    <ItemGroup>
      <xsd Include="Objects.xsd" />
    </ItemGroup>
    <Exec Command="&quot;$(XsdExePath)&quot; @(xsd) /c /namespace:Blah.Objects" />
  </Target>