由于我不想进一步讨论的原因,我需要从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
时。
1条答案
按热度按时间yfjy0ee71#
解决方案需要两个修改:
.bat
),这(正如你自己发现的)会阻止Code.exe
将诊断输出打印到批处理文件的控制台窗口:[1]字符串
.ps1
),如下所示:型
-Wait
使Start-Process
调用自身 * 异步 *,并使用-PassThru
使其发出Wait-Process
随后等待的进程信息对象。.ExitCode
属性检查进程退出代码。-Wait
,但令人惊讶的是,实际上并非如此:Start-Process -Wait
-与Wait-Process
小程序和底层的.WaitForExit()
.NET API方法不同-不仅等待子进程 * 本身 *,还等待 * 它的 * 子进程,这导致了这里的问题:它还等待Code.exe
进程退出。请注意,如果您想在单独的控制台窗口中执行批处理文件 ,则只需要在用例中使用
Start-Process
;否则, 直接 * 调用即可:型
Start-Process
的指导。Wait-Process
一样。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
进程然后将 * 委托 * 给后者并在此后退出(此委托过程本身需要一些时间)。