繁体   English   中英

Start-Process / System.Diagnostics.Process ExitCode为$ null,带有-NoNewWindow

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

Powershell cmdlet Start-Process异常:

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

这是一个测试:与cmd之外的其他东西相同。 该测试是在带有PS5的Win10上进行的,与Win7和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吗?

链接问题的答案/评论中的答案:

要做的是缓存进程句柄。 我这样做后,$ process.ExitCode便可以正常工作。 如果我没有缓存进程句柄,则$ process.ExitCode为null。

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

$proc = Start-Process -NoNewWindow -PassThru ...
$handle = $proc.Handle # cache proc.Handle https://stackoverflow.com/a/23797762/1479211
$proc.WaitForExit()
$proc.ExitCode ... will be set

用户在评论中添加了解释:

这是.NET Process对象的实现的怪癖。 ExitCode属性的实现首先检查该进程是否已经退出。 出于某种原因,执行该检查的代码不仅查看HasExited属性,还验证proces对象中是否存在proces句柄, 如果不存在, 则会引发异常 PowerShell拦截该异常并返回null。

访问Handle属性会使流程对象检索流程句柄并将其存储在内部 将句柄存储在流程对象中后,ExitCode属性将按预期工作。

由于Start-Process无法启动挂起的进程,并且仅在进程正在运行时才可以获取该句柄(出现该句柄),因此对于短时间运行的进程而言,用法有些脆弱。

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

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)在启动进程能够从进程获取句柄之前已经停止。

使用-Wait.WaitForExit()没有区别,因为这两种方法检查进程已退出。 如果退出,PowerShell将无法获取该流程的必要句柄。 该句柄是必需的,因为它保存了已启动进程的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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM