关于powershell:使用Start-Process和WaitForExit而不是-Wait获取ExitCode

Obtaining ExitCode using Start-Process and WaitForExit instead of -Wait

我正在尝试从PowerShell运行程序,等待退出,然后获得对ExitCode的访问权限,但运气不佳。 我不想将-WaitStart-Process一起使用,因为我需要进行一些处理才能在后台进行。

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

1
2
3
4
5
6
7
8
9
10
11
12
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:

1
2
3
4
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属性?


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

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

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

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

# This will print 1

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

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


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

  • 手动创建System.Diagnostics.Process对象并绕过Start-Process
  • 在后台作业中运行可执行文件(仅适用于非交互式进程!)
  • 您可以按照以下方式进行操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $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

    要么

    1
    2
    3
    4
    5
    6
    Start-Job -Name DoSomething -ScriptBlock {
        & ping.exe somehost
        Write-Output $LASTEXITCODE
    }
    #Do other stuff here
    Get-Job -Name DoSomething | Wait-Job | Receive-Job


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

    例:

    1
    2
    3
    4
    5
    6
    7
    $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)"
    }


    即使我的过程已完成,"-等待"选项似乎也对我不利。

    我尝试了阿德里安的解决方案,它可以工作。但是我使用Wait-Process而不是依赖于检索过程句柄的副作用。

    所以:

    1
    2
    3
    4
    5
    6
    $proc = Start-Process $msbuild -PassThru
    Wait-Process -InputObject $proc

    if ($proc.ExitCode -ne 0) {
        Write-Warning"$_ exited with status code $($proc.ExitCode)"
    }


    或尝试添加此...

    1
    2
    3
    4
    5
    6
    7
    $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)

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