Start-Process / System.Diagnostics.Process ExitCode is $null with -NoNewWindow

Start-Process / System.Diagnostics.Process ExitCode is $null with -NoNewWindow

Powershell cmdlet Start-Process行为异常:

当我启动另一个控制台进程并指定 -NoNewWindow 时,ExitCode 属性(int!)为空。

这是一个测试:除了 cmd 之外的其他东西也一样。本次测试是在 PS5 的 Win10 上进行的,在 PS5:

的 Win7 上也是如此
PS C:\Users\Martin> cmd.exe /Cver

Microsoft Windows [Version 10.0.15063]
PS C:\Users\Martin> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.15063.296
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.15063.296
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1


PS C:\Users\Martin> $pNewWindow = Start-Process -FilePath "cmd.exe" -ArgumentList '/C"exit 42"' -PassThru
PS C:\Users\Martin> $pNewWindow.WaitForExit()
PS C:\Users\Martin> $pNoNewWindow.HasExited
True
PS C:\Users\Martin> $pNewWindow.ExitCode
42
PS C:\Users\Martin> $pNoNewWindow = Start-Process -FilePath "cmd.exe" -ArgumentList '/C"exit 42"' -PassThru -NoNewWindow

PS C:\Users\Martin> $pNoNewWindow.WaitForExit()
PS C:\Users\Martin> $pNoNewWindow.HasExited
True
PS C:\Users\Martin> $pNoNewWindow.ExitCode
PS C:\Users\Martin> $pNoNewWindow.ExitCode -eq $null
True
PS C:\Users\Martin> $pNoNewWindow | Get-Member | ? {$_.Name -imatch "exit"}


   TypeName: System.Diagnostics.Process

Name        MemberType Definition
----        ---------- ----------
Exited      Event      System.EventHandler Exited(System.Object, System.EventArgs)
WaitForExit Method     bool WaitForExit(int milliseconds), void WaitForExit()
ExitCode    Property   int ExitCode {get;}
ExitTime    Property   datetime ExitTime {get;}
HasExited   Property   bool HasExited {get;}


PS C:\Users\Martin>

...所以,属性 在那里,但它是 null,即使它是 int?

来自linked question的回答/评论的回答:

had to do was cache the process handle. As soon as I did that, $process.ExitCode worked correctly. If I didn't cache the process handle, $process.ExitCode was null.

实际上,对于 不会 立即终止的进程 (与示例中的 cmd.exe 不同),解决方法有效:

$proc = Start-Process -NoNewWindow -PassThru ...
$handle = $proc.Handle # cache proc.Handle 
$proc.WaitForExit()
$proc.ExitCode ... will be set

用户在 comments 中添加了解释:

This is a quirk of the implementation of the .NET Process object. The implementation of the ExitCode property first checks if the process has exited. For some reason, the code that performs that check not only looks at the HasExited property but also verifies that the proces handle is present in the proces object and throws an exception if it is not. PowerShell intercepts that exception and returns null.

Accessing the Handle property causes the process object to retrieve the process handle and store it internally. Once the handle is stored in the process object, the ExitCode property works as expected.

由于Start-Process无法启动挂起的进程,而且只有进程为运行(出现)才能获取handle,所以用法有点脆-运行 进程。

我们在环境中遇到了类似的问题。 在执行以下命令时:

Start-Process -FilePath C:\Windows\system32\reg.exe -PassThru -Wait -NoNewWindow

Start-Process -FilePath C:\Windows\system32\reg.exe -PassThru -Wait -WindowStyle Hidden

如 Martin Ba 所述,启动进程 (reg.exe) 似乎在 Start-Process 能够从进程中获取句柄之前已停止。

使用-Wait.WaitForExit()没有区别,因为这两种方法都会检查进程是否已退出。如果它退出,PowerShell 将无法获得该进程的必要句柄。 Handle 是必需的,因为它包含已启动进程的 ExitCode。

如果进程已经退出,则会抛出以下错误:

System.Management.Automation.CmdletInvocationException: Cannot process request because the process (<ProcessIdHere>) has exited. ---> 
System.InvalidOperationException: Cannot process request because the process (<ProcessIdHere>) has exited.
  at System.Diagnostics.Process.GetProcessHandle(Int32 access, Boolean throwIfExited) 
  at System.Diagnostics.Process.OpenProcessHandle(Int32 access) 
  at System.Diagnostics.Process.get_Handle()

使用 -WindowStyle Hidden 而不是 -NoNewWindow 可能会降低错误发生的频率。这是因为 -WindowStyle Hidden 正在创建一个新的 Shell(--> 更多开销),而 -NoNewWindow 使用当前的 Shell.

我们为这个问题创建了一个 UserVoice 条目。也许 PowerShell 团队能够解决此问题:https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/35737357--bug-start-process-might-not-return-handle-exitco