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

时间:2012-04-21 19:23:47

标签: powershell powershell-v2.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

运行此脚本将导致Notepad启动。手动关闭后,将打印退出代码,它将重新开始,而不使用-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属性?

6 个答案:

答案 0 :(得分:79)

这里要记住两件事。一种是添加-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.

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

$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的情况下运行它,它将不打印任何内容。

答案如下: How do I run a Windows installer and get a succeed/fail value in PowerShell?

答案 1 :(得分:40)

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

  1. 手动创建System.Diagnostics.Process对象并绕过Start-Process
  2. 在后台作业中运行可执行文件(仅适用于非交互式进程!)
  3. 以下是您可以这样做的方法:

    $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
    

答案 2 :(得分:32)

在尝试上面的最终建议时,我发现了一个更简单的解决方案。我所要做的就是缓存进程句柄。一旦我这样做,$ 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)"
}

答案 3 :(得分:8)

&#39; -Wait&#39;即使我的过程已经完成,选项似乎也阻止了我。

我尝试了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)"
}

答案 4 :(得分:4)

或尝试添加此内容......

$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()。

答案 5 :(得分:0)

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

如果您曾经想卸载folding @ 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