Visual studio 2010 SignTool.exe错误后,Visual Studio中的后期生成事件失败

Visual studio 2010 SignTool.exe错误后,Visual Studio中的后期生成事件失败,visual-studio-2010,build-process,code-signing,errorlevel,timestamping,Visual Studio 2010,Build Process,Code Signing,Errorlevel,Timestamping,我们在VisualStudio2010中有一个项目,它在生成后事件中运行批处理文件。该批处理从Microsoft SDK调用signtool.exe对二进制文件进行签名和时间戳 然而,由于某些原因,时间戳服务器(我们使用)往往不可靠,有时会失败。这导致构建失败 我们实现了一个更高级的批处理脚本(基于代码),拆分签名和时间戳,并允许在失败时重试时间戳操作 下面是批处理脚本(signfile.bat)的简化版本: 生成后事件代码如下所示: signfile.bat "$(OutDir)$(Targe

我们在VisualStudio2010中有一个项目,它在生成后事件中运行批处理文件。该批处理从Microsoft SDK调用signtool.exe对二进制文件进行签名和时间戳

然而,由于某些原因,时间戳服务器(我们使用)往往不可靠,有时会失败。这导致构建失败

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

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

生成后事件代码如下所示:

signfile.bat "$(OutDir)$(TargetName)$(TargetExt)"
因此,如果时间戳失败,它将以2秒的间隔重试10次

但是,我们观察到的是,如果时间戳从第一次尝试开始就运行良好,那么一切都正常。但是,如果第一次尝试失败,则整个生成后事件都失败,代码为-1,即使时间戳在下一次尝试中成功

因此,正如您所看到的,即使从signfile.bat返回的错误代码是0,VisualStudio仍认为它是-1,并使事件失败


所有清除错误标志的尝试,如在此处或此处添加
ver>nul
,或在最后添加
exit 0
(当然在signfile.bat之前添加“call”)都没有帮助,因为Visual Studio似乎不仅检查了错误级别,还检查了其他内容。事实上,批处理和signfile.bat在出现错误时只返回0或1,而不是-1。如果signtool.exe只返回一次错误,那么似乎无法说服Visual Studio不让后期生成事件失败。

在花费大量时间进行实验和搜索后,找到了一个,在一条评论中提到。看起来VisualStudio会扫描输出,搜索一些特殊的关键字。Signtool.exe输出以及其他
EXEC:Signtool错误:发生了错误
,这似乎足以提醒Visual Studio发生了错误

因此,提出的解决方案是将输出和错误流重定向到nul,如
2>NUL1>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很高兴:


事实上,只要添加
2>nul
就足以修复它。错误流仍将被打印:
错误数:1
,但它不会导致问题。

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

我知道这并不是最初的问题所要问的,但在最终找到解决方案之前,我已经在这上面浪费了好几个小时(偶然发现了这个问题,谢谢!),所以我发布这个信息,希望它能帮助其他人

从PowerShell中,对SignTool的标准调用如下:

$output = & $signToolPath sign `
    /n $companyName `
    /d $productName `
    /du $productWebsite `
    /t $timestampServer `
    /sha1 $shaHash `
    $filePath
$output = & $signToolPath sign `
    <switches and args as above>
    $filePath 2>&1

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

$output -split "([a-z0-9])" -join " " | Write-Host
如果SignTool返回错误代码,这将立即导致构建失败(如TeamCity屏幕截图所示),如果脚本能够自动重试,这是不可取的:

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

$output = & $signToolPath sign `
    <switches and args as above>
    $filePath 2>&1
$output=&$signToolPath符号`
$filePath 2>&1
然而,这意味着当SignTool返回错误代码时,您无法看到错误输出,这显然是没有帮助的。您可以通过如下方式将输出写入主机来解决此问题:

$output = & $signToolPath sign `
    /n $companyName `
    /d $productName `
    /du $productWebsite `
    /t $timestampServer `
    /sha1 $shaHash `
    $filePath
$output = & $signToolPath sign `
    <switches and args as above>
    $filePath 2>&1

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

$output -split "([a-z0-9])" -join " " | Write-Host
$output=&$signToolPath符号`
$filePath 2>&1
$output |写主机
但是,VisualStudio/MSBuild将继续尝试“聪明”,并再次失败生成

我发现的唯一一种允许在不导致生成失败的情况下显示SignTool错误消息的解决方案是重定向错误流,并在写入主机之前篡改错误输出,类似于这样:

$output = & $signToolPath sign `
    /n $companyName `
    /d $productName `
    /du $productWebsite `
    /t $timestampServer `
    /sha1 $shaHash `
    $filePath
$output = & $signToolPath sign `
    <switches and args as above>
    $filePath 2>&1

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

$output -split "([a-z0-9])" -join " " | Write-Host
$output=&$signToolPath符号`
$filePath 2>&1
$output-拆分“([a-z0-9])”-连接“|写入主机
错误消息是可读的,但不会使构建失败,如下所示:

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

再次感谢您的原始问答,我希望有关PowerShell的额外信息对其他人有所帮助


(注:已接受答案中的一个链接已断开,但引用的文章存档在此处:)

当软件试图为了自身利益而过于聪明时,就会发生这种情况。谢谢你的解决方案。