SignTool.exe错误后,Visual Studio中的PostBuild事件失败

时间:2013-10-01 18:53:48

标签: visual-studio-2010 build-process code-signing errorlevel timestamping

我们在Visual Studio 2010中有一个项目,它在后期构建事件中运行批处理文件。该批处理从Microsoft SDK调用signtool.exe来对二进制文件进行签名和时间戳记。

然而,时间戳服务器(我们使用http://timestamp.verisign.com/scripts/timstamp.dll)由于某种原因往往不可靠,有时会失败。这导致构建失败。

我们实现了更高级的批处理脚本(基于this代码),拆分签名和时间戳,并允许重试时间戳操作(如果失败)。

以下是批处理脚本(signfile.bat)的简化版本:

@echo off

REM sign the file...
signtool.exe /f Authenticode.pfx /p PASS %1

if %errorlevel% neq 0 exit /b %errorlevel%

set timestamp_server=http://timestamp.verisign.com/scripts/timstamp.dll

for /L %%a in (1,1,10) do (

    REM try to timestamp the file...
    signtool.exe timestamp /t %timestamp_server% %1

    if errorlevel 0 if not errorlevel 1 GOTO succeeded

    REM wait 2 seconds...
    ping -n 2 127.0.0.1 > nul
)

REM return an error code...
echo signfile.bat exit code is 1.
exit /b 1

:succeeded
REM return a successful code...
echo signfile.bat exit code is 0.
exit /b 0

构建后的事件代码如下所示:

signfile.bat "$(OutDir)$(TargetName)$(TargetExt)"

因此,如果时间戳失败,则以2秒的间隔重试10次。

但是,我们观察到的是,如果时间戳从第一次尝试开始就好了,一切都还可以。但是,如果第一次尝试失败,则整个post-build事件失败,代码为-1,即使下次尝试时间戳成功。

1>------ Build started: Project: myproject, Configuration: NonOptimized x64 ------
1>  Done Adding Additional Store
1>  Successfully signed: E:\tfs\MySolution\bin\x64\NonOptimized\myproject.dll
1>  
1>EXEC : SignTool error : The specified timestamp server either could not be reached
1>  or returned an invalid response.
1>    This may happen if you specify an RFC 3161 timestamp URL but used
1>    the /t option or you specified a legacy Authenticode timestamp URL
1>    but used the /tr option.
1>EXEC : SignTool error : An error occurred while attempting to timestamp: E:\tfs\MySolution\bin\x64\NonOptimized\myproject.dll
1>  
1>  
1>  Number of errors: 1
1>  
1>  Successfully timestamped: E:\tfs\MySolution\bin\x64\NonOptimized\myproject.dll
1>  
1>  signfile.bat exit code is 0.
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(113,5): error MSB3073: The command "signfile.bat "E:\tfs\MySolution\bin\x64\NonOptimized\myproject.dll"
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(113,5): error MSB3073: :VCEnd" exited with code -1.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

因此,正如您所看到的,即使signfile.bat返回的错误代码为0,Visual Studio也认为它是-1并且未能通过该事件。

所有尝试清除错误标志的方法,比如在这里和那里添加ver>nul,或者最后添加exit 0(当然在signfile.bat之前添加“call”)没有帮助,因为它似乎Visual Studio不仅检查错误级别,还检查其他内容。实际上,批处理以及signfile.bat仅在出错时返回0或1,但不返回-1。如果signtool.exe一次返回错误,似乎没有办法说服Visual Studio不要使后期构建事件失败。

2 个答案:

答案 0 :(得分:21)

在花了很多时间进行实验和搜索后,在评论中找到了article,提到了here。看起来Visual Studio会扫描输出,搜索一些特殊的关键字。 Signtool.exe在其他EXEC : SignTool error : An error occurred之间输出,这似乎足以警告Visual Studio存在错误。

因此,提出的解决方案是将输出和错误流重定向到nul 2>nul 1>nul。 Errorlevel仍将设置,因此您将能够确定是否发生错误。但您可能需要打印一些额外的消息才能看到状态:

REM try to timestamp the file...
signtool.exe timestamp /t %timestamp_server% %1 2>nul 1>nul

if errorlevel 0 if not errorlevel 1 (
    echo Successfully timestamped: %1
    GOTO succeeded
)
echo Timestamping failed for %1

现在Visual Studio很高兴:

1>------ Build started: Project: myproject, Configuration: NonOptimized x64 ------
1>  Done Adding Additional Store
1>  Successfully signed: E:\tfs\MySolution\bin\x64\NonOptimized\myproject.dll
1>  
1>  Timestamping failed for "E:\tfs\MySolution\bin\x64\NonOptimized\myproject.dll"
1>  Successfully timestamped: "E:\tfs\MySolution\bin\x64\NonOptimized\myproject.dll"
1>  signfile.bat exit code is 0.
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

事实上,只需添加2>nul即可解决问题。错误流仍将打印:Number of errors: 1,但不会导致问题。

答案 1 :(得分:2)

我遇到了一个非常相似的问题,但增加了难度,即不是直接调用 SignTool,而是我们的构建后步骤调用 PowerShell 脚本,该脚本本身调用 SignTool。我在 PowerShell 脚本中设置了一些重试逻辑(尝试各种时间戳服务器,有延迟),但发现如果第一次尝试失败,那么整个构建都会失败,即使错误被正确捕获和处理并随后进行签名尝试成功。

我很欣赏这并不是最初的问题所问的问题,但我在最终制定解决方案之前浪费了数小时和数小时(偶然发现了这个问答 - 谢谢!),所以我发布了这个信息希望能帮到别人。

在 PowerShell 中,对 SignTool 的标准调用如下所示:

$output = & $signToolPath sign `
    /n $companyName `
    /d $productName `
    /du $productWebsite `
    /t $timestampServer `
    /sha1 $shaHash `
    $filePath

如果 SignTool 返回错误代码,这将立即使构建失败(如 TeamCity 屏幕截图所示),如果脚本能够自动重试,这是不可取的:

TeamCity screenshot - failure

为防止构建失败,可以使用 PowerShell 重定向语句 2>&1 抑制 PowerShell 错误流(类似于已接受答案中的解决方案),如下所示:

$output = & $signToolPath sign `
    <switches and args as above>
    $filePath 2>&1

但是,这意味着当 SignTool 返回错误代码时您看不到错误输出,这显然没有帮助。您可以通过将输出写入主机来解决这个问题,如下所示:

$output = & $signToolPath sign `
    <switches and args as above>
    $filePath 2>&1

$output | Write-Host

但随后 Visual Studio / MSBuild 将继续尝试变得“聪明”并再次失败构建!

我发现的唯一允许显示 SignTool 错误消息而不会导致构建失败的解决方案是 both 重定向错误流 以混淆错误写入主机之前的输出,如下所示:

$output = & $signToolPath sign `
    <switches and args as above>
    $filePath 2>&1

$output -split "([a-z0-9])" -join " " | Write-Host

错误消息是人类可读的,但不会使构建失败,如下所示:

TeamCity screenshot - success

当然,如果所有重试尝试都失败,那么您需要从 PowerShell 脚本返回一个适当的非零退出代码,以便构建确实失败。

再次感谢最初的问答,我希望这些关于 PowerShell 的额外信息对其他人有所帮助。

(PS:已接受答案中的一个链接已损坏,但引用的文章存档在此处:http://web.archive.org/web/20180729111947/http://blog.robertromito.com/2010/08/ignore-error-from-visual-studio-post.html