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
Powershell cmdlet Start-Process
行为异常:
当我启动另一个控制台进程并指定 -NoNewWindow
时,ExitCode 属性(int
!)为空。
这是一个测试:除了 cmd
之外的其他东西也一样。本次测试是在 PS5 的 Win10 上进行的,在 PS5:
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