为什么Electron应用程序在使用start命令从批处理脚本启动时会阻止PowerShell?

zpqajqem  于 8个月前  发布在  Electron
关注(0)|答案(1)|浏览(77)

由于我不想进一步讨论的原因,我需要从PowerShell脚本启动一个批处理脚本,并等待批处理脚本完成。
在批处理脚本中,我想异步启动一个可执行文件,然后很快退出批处理脚本。一旦批处理脚本终止,我也希望PowerShell脚本退出。
下面是两个脚本的基本示例,它可以实现我想要的功能:

startExe.bat

start "" "C:\Windows\System32\calc.exe"

exit

字符串

runStartExe.ps1(在同一目录下):

Start-Process -FilePath "startExe.bat" -Wait;


在这种情况下,calculator 将启动,并且在PowerShell脚本按预期终止后不久。
然而,当我尝试对基于Electron的应用程序做同样的事情时,让我们假设你安装了Visual Studio Code

startExe.bat

start "" "C:\Users\%username%\AppData\Local\Programs\Microsoft VS Code\Code.exe"

exit


PowerShell脚本将被阻止,直到应用程序关闭。
当只运行批处理脚本时,它在所有情况下都可以正常退出。
为什么会这样,我该如何预防?

其他发现

在测试时,我注意到Windows 10和11之间的行为似乎存在差异。也就是说,当我在批处理脚本中运行start "" "C:\Windows\System32\notepad.exe"时,与基于电子的应用程序相同的锁定将在Windows 10中发生,但在Windows 11中不会发生。似乎在Windows 10中,嵌入字符串!This program cannot be run in DOS mode.的每个可执行文件都会发生这种情况(大多数程序都有,但例如Windows计算器没有这个字符串),而在Windows 11中,它只会发生在基于电子的程序中。
我还注意到,当我在启动应用程序之后和exit语句之前引入timeout 5时,批处理脚本也会为基于电子的应用程序锁定。
此外,我注意到选项-PassThru-NoNewWindow似乎在PowerShell脚本中产生了差异,但我仍然无法可靠地完成这项工作,特别是在批处理脚本中引入timeout 5时。

yfjy0ee7

yfjy0ee71#

解决方案需要两个修改:

  • 如下修改 * 批处理文件 *(.bat),这(正如你自己发现的)会阻止Code.exe将诊断输出打印到批处理文件的控制台窗口:[1]
set ELECTRON_NO_ATTACH_CONSOLE=1
start "" "%LOCALAPPDATA%\Programs\Microsoft VS Code\Code.exe"

字符串

  • 修改 PowerShell 脚本(.ps1),如下所示:
Start-Process -PassThru -FilePath "startExe.bat" | Wait-Process

  • 也就是说,通过删除-Wait使Start-Process调用自身 * 异步 *,并使用-PassThru使其发出Wait-Process随后等待的进程信息对象。
  • 如果需要,您可以保存这个进程信息对象,然后通过它的.ExitCode属性检查进程退出代码。
  • 虽然这 * 看起来 * 在功能上等同于只使用-Wait,但令人惊讶的是,实际上并非如此:
  • 正如GitHub issue #15555中所描述的,Start-Process -Wait-与Wait-Process小程序和底层的.WaitForExit().NET API方法不同-不仅等待子进程 * 本身 *,还等待 * 它的 * 子进程,这导致了这里的问题:它还等待Code.exe进程退出。

请注意,如果您想在单独的控制台窗口中执行批处理文件 ,则只需要在用例中使用Start-Process;否则, 直接 * 调用即可:

.\startExe.bat

  • 对于在同一个窗口中的 * 同步 * 执行,通常最好直接执行控制台应用程序(包括批处理文件):它还将其标准输出流(stdout和stderr)直接连接到PowerShell的流,允许您 * 捕获或重定向 * 其输出;请参阅GitHub docs issue #6239提供有关何时使用Start-Process的指导。
  • 顺便说一句:
  • 直接执行本身只需要等待直接子进程,就像Wait-Process一样。
  • 但是,如果您从批处理文件中执行PowerShell * 捕获输出 *,整个子进程 * 树 * 将再次等待,就像Start-Process -Wait一样。也就是说,$output = .\startExe.bat.\startExe.bat > out.txt.\startExe.bat | Measure-Object等命令都将导致原始问题重新出现(它们将等待Code.exe终止)。

[1]Code.exe是一个 GUI(-subsystem)应用程序,这意味着当它启动时不会为其创建控制台窗口。但是-这对于GUI应用程序来说是不寻常的-它会检测到它何时被 * 从控制台窗口 * 调用,然后显式地附加到该控制台并打印诊断输出。
[2]然而,在实践中,如果Visual Studio Code的副本碰巧已经在运行,这并不是一个(严重的)问题,因为新的Code.exe进程然后将 * 委托 * 给后者并在此后退出(此委托过程本身需要一些时间)。

相关问题