我们在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不要使后期构建事件失败。
答案 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 屏幕截图所示),如果脚本能够自动重试,这是不可取的:
为防止构建失败,可以使用 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
错误消息是人类可读的,但不会使构建失败,如下所示:
当然,如果所有重试尝试都失败,那么您需要从 PowerShell 脚本返回一个适当的非零退出代码,以便构建确实失败。
再次感谢最初的问答,我希望这些关于 PowerShell 的额外信息对其他人有所帮助。
(PS:已接受答案中的一个链接已损坏,但引用的文章存档在此处:http://web.archive.org/web/20180729111947/http://blog.robertromito.com/2010/08/ignore-error-from-visual-studio-post.html)