87

我正在尝试从 PowerShell 运行程序,等待退出,然后访问 ExitCode,但我运气不佳。我不想使用-Waitwith Start-Process,因为我需要在后台进行一些处理。

这是一个简化的测试脚本:

cd "C:\Windows"

# ExitCode is available when using -Wait...
Write-Host "Starting Notepad with -Wait - return code will be available"
$process = (Start-Process -FilePath "notepad.exe" -PassThru -Wait)
Write-Host "Process finished with return code: " $process.ExitCode

# ExitCode is not available when waiting separately
Write-Host "Starting Notepad without -Wait - return code will NOT be available"
$process = (Start-Process -FilePath "notepad.exe" -PassThru)
$process.WaitForExit()
Write-Host "Process exit code should be here: " $process.ExitCode

运行此脚本将导致记事本启动。手动关闭后,会打印退出码,重新启动,不使用-wait. 退出时不提供 ExitCode:

Starting Notepad with -Wait - return code will be available
Process finished with return code:  0
Starting Notepad without -Wait - return code will NOT be available
Process exit code should be here:

我需要能够在启动程序和等待它退出之间执行额外的处理,所以我不能使用-Wait. 我怎样才能做到这一点,并且仍然可以从此过程中访问 .ExitCode 属性?

4

6 回答 6

125

这里有两件事要记住。一是添加-PassThru论据,二是添加-Wait论据。由于这个缺陷,您需要添加等待参数。

-PassThru [<SwitchParameter>]
    Returns a process object for each process that the cmdlet started. By default,
    this cmdlet does not generate any output.

完成此操作后,将返回一个流程对象,您可以查看该对象的 ExitCode 属性。这是一个例子:

$process = start-process ping.exe -windowstyle Hidden -ArgumentList "-n 1 -w 127.0.0.1" -PassThru -Wait
$process.ExitCode

# This will print 1

如果你在没有-PassThruor的情况下运行它-Wait,它不会打印任何内容。

相同的答案在这里:如何运行 Windows 安装程序并在 PowerShell 中获得成功/失败值?

还值得注意的是,上面的“缺陷报告”链接中提到了一种解决方法,如下所示:

# Start the process with the -PassThru command to be able to access it later
$process = Start-Process 'ping.exe' -WindowStyle Hidden -ArgumentList '-n 1 -w 127.0.0.1' -PassThru

# This will print out False/True depending on if the process has ended yet or not
# Needs to be called for the command below to work correctly
$process.HasExited

# This will print out the actual exit code of the process
$process.GetType().GetField('exitCode', 'NonPublic, Instance').GetValue($process)
于 2013-04-15T14:51:05.090 回答
59

在尝试上面的最终建议时,我发现了一个更简单的解决方案。我所要做的就是缓存进程句柄。一旦我这样做了, $process.ExitCode 就可以正常工作。如果我没有缓存进程句柄,则 $process.ExitCode 为空。

例子:

$proc = Start-Process $msbuild -PassThru
$handle = $proc.Handle # cache proc.Handle
$proc.WaitForExit();

if ($proc.ExitCode -ne 0) {
    Write-Warning "$_ exited with status code $($proc.ExitCode)"
}
于 2014-05-22T04:20:34.500 回答
51

我认为你可以做两件事...

  1. 手动创建 System.Diagnostics.Process 对象并绕过 Start-Process
  2. 在后台作业中运行可执行文件(仅适用于非交互式进程!)

您可以这样做:

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "notepad.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = ""
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
#Do Other Stuff Here....
$p.WaitForExit()
$p.ExitCode

或者

Start-Job -Name DoSomething -ScriptBlock {
    & ping.exe somehost
    Write-Output $LASTEXITCODE
}
#Do other stuff here
Get-Job -Name DoSomething | Wait-Job | Receive-Job
于 2012-04-21T19:32:19.613 回答
18

即使我的过程已经完成,“-Wait”选项似乎也阻止了我。

我尝试了 Adrian 的解决方案并且它有效。但是我使用了Wait-Process而不是依赖于检索进程句柄的副作用。

所以:

$proc = Start-Process $msbuild -PassThru
Wait-Process -InputObject $proc

if ($proc.ExitCode -ne 0) {
    Write-Warning "$_ exited with status code $($proc.ExitCode)"
}
于 2017-05-02T01:56:30.910 回答
5

Or try adding this...

$code = @"
[DllImport("kernel32.dll")]
public static extern int GetExitCodeProcess(IntPtr hProcess, out Int32 exitcode);
"@
$type = Add-Type -MemberDefinition $code -Name "Win32" -Namespace Win32 -PassThru
[Int32]$exitCode = 0
$type::GetExitCodeProcess($process.Handle, [ref]$exitCode)

By using this code, you can still let PowerShell take care of managing redirected output/error streams, which you cannot do using System.Diagnostics.Process.Start() directly.

于 2012-06-14T01:12:49.893 回答
0

这是这个主题的一个变体。我想卸载 Cisco Amp,等待,然后获取退出代码。但是卸载程序会启动另一个名为“un_a”的程序并退出。使用此代码,我可以等待 un_a 完成并获取它的退出代码,即“需要重新启动”的 3010。这实际上是在一个 .bat 文件中。

如果您曾经想卸载 fold@home,它的工作方式类似。

rem uninstall cisco amp, probably needs a reboot after

rem runs Un_A.exe and exits

rem start /wait isn't useful
"c:\program files\Cisco\AMP\6.2.19\uninstall.exe" /S

powershell while (! ($proc = get-process Un_A -ea 0)) { sleep 1 }; $handle = $proc.handle; 'waiting'; wait-process Un_A; exit $proc.exitcode
于 2020-05-21T21:38:29.257 回答