使用Start-Process和WaitForExit而不是-Wait获取ExitCode

Ric*_*ard 61 powershell powershell-2.0

我正在尝试从PowerShell运行程序,等待退出,然后访问ExitCode,但我没有太多运气.我不希望使用-WaitStart-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
Run Code Online (Sandbox Code Playgroud)

运行此脚本将导致启动记事本.手动关闭后,将打印退出代码,它将重新开始,不使用-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:
Run Code Online (Sandbox Code Playgroud)

我需要能够在启动程序和等待它退出之间执行额外的处理,所以我无法使用-Wait.我该怎么做,仍然可以访问此进程的.ExitCode属性?

Dan*_*ton 87

这里要记住两件事.一个是添加-PassThru参数,两个是添加-Wait参数.由于此缺陷,您需要添加wait参数:http://connect.microsoft.com/PowerShell/feedback/details/520554/start-process-does-not-return-exitcode-property

-PassThru [<SwitchParameter>]
    Returns a process object for each process that the cmdlet started. By d
    efault, this cmdlet does not generate any output.
Run Code Online (Sandbox Code Playgroud)

执行此操作后,将传回一个进程对象,您可以查看该对象的ExitCode属性.这是一个例子:

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

# This will print 1
Run Code Online (Sandbox Code Playgroud)

如果你没有-PassThru或没有-Wait它,它将打印出来.

答案如下:如何在PowerShell中运行Windows安装程序并获取成功/失败值?

  • 值得注意的解决方法:$ p.GetType().GetField("exitCode","NonPublic,Instance").GetValue($ p) (2认同)
  • 第一个链接(实际上)已损坏:*“Microsoft Connect 已停用”* (2认同)

And*_*ndi 41

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

  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
Run Code Online (Sandbox Code Playgroud)

要么

Start-Job -Name DoSomething -ScriptBlock {
    & ping.exe somehost
    Write-Output $LASTEXITCODE
}
#Do other stuff here
Get-Job -Name DoSomething | Wait-Job | Receive-Job
Run Code Online (Sandbox Code Playgroud)

  • 我必须来这里才知道“Start-Process”创建了一个“System.Diagnostics.Process”类型的对象。他们为什么不将这些信息包含在 powershell 文档中? (2认同)

小智 35

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

例:

$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)"
}
Run Code Online (Sandbox Code Playgroud)

  • @CarlR这是.NET Process对象实现的一个怪癖.ExitCode属性的实现首先检查进程是否已退出.出于某种原因,执行该检查的代码不仅查看HasExited属性,还验证过程对象中是否存在过程句柄,并且[如果不是则抛出异常](https://github.com/Microsoft /referencesource/blob/e458f8df6ded689323d4bd1a2a725ad32668aaec/System/services/monitoring/system/diagnosticts/Process.cs#L1400).PowerShell拦截该异常并返回null. (5认同)
  • 如果有人有任何想法我会很感激,我很想知道为什么会有效. (3认同)
  • (续)访问Handle属性会导致进程对象检索进程句柄并[在内部存储](https://github.com/Microsoft/referencesource/blob/e458f8df6ded689323d4bd1a2a725ad32668aaec/System/services/monitoring/system/diagnosticts/ Process.cs#L1742).句柄存储在进程对象中后,ExitCode属性按预期工作. (3认同)
  • 这很疯狂,但作为一种解决方法,它确实可以完美运行。奇怪的! (3认同)

Ben*_*n T 8

即使我的流程已经完成,'-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)"
}
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的答案!在我看来,使用 WaitForExit 或 -Wait 也应该有效,但这个答案是最好的选择。比使用 System.Diagnostics.Process 简单得多。 (3认同)

Gre*_*gel 5

或者尝试添加这个...

$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)
Run Code Online (Sandbox Code Playgroud)

通过使用此代码,您仍然可以让 PowerShell 负责管理重定向的输出/错误流,这是您无法直接使用 System.Diagnostics.Process.Start() 完成的。

  • 提示:用`Start-Process`启动进程后缓存进程句柄`($process.Handle)`。进程退出后不再设置。 (2认同)