我的目标是运行多个进程,并保存它们的ProcessName
和Id
供以后使用。这是我的代码
[System.Collections.ArrayList]$startedProcesses = @()
$processStatus = Start-Process -FilePath notepad.exe -passthru
Start-Sleep 1
$startedProcesses.add($processStatus)
$processStatus = Start-Process -FilePath calc.exe -passthru
Start-Sleep 1
$startedProcesses.add($processStatus)
echo $startedProcesses
此脚本的输出为:
PS C:\Users\wakatana\Desktop\> .\so_question0.ps1
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
235 15 3408 14112 0.08 24812 2 notepad
0 0 0 0.13 21460
我也尝试替换[System.Collections.ArrayList]$startedProcesses = @()
以$startedProcesses = New-Object System.Collections.Generic.List[System.ComponentModel.Component]
结束,但结果还是一样。
问题:为什么我在calc
下没有ProcessName
?如果删除Start-Sleep 1
,则在calc
下得到ProcessName
。这是为什么?这是启动应用程序的正确方法,还是我做错了什么?我的操作系统:Windows 10 Home
答案 0 :(得分:2)
您的问题与Start-Sleep
的使用无关。
相反,问题在于,从Windows 10开始, calc.exe
只是最终启动的进程的 stub 可执行文件,并且存根进程会立即退出。因为它已经启动了真正的可执行文件。
如果您删除了Start-Sleep
和Start-Process
之间的echo
调用,则$startedProcesses
中包含的 stub 过程对象通常会反映当时calc.exe
列中存根可执行文件的名称-ProcessName
-由于仍在运行中(虽然只是很快),但是您仍然无法跟踪 real < / em>可执行文件的进程生存期,并通过该对象退出代码。
实际可执行文件的名称为Calculator.exe
,其确切路径包含完整的AppX软件包名称(软件包名称,版本号和发布者ID):
例如,启动calc.exe
后,(Get-Process Calculator).Path
会产生类似以下内容:
C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_10.1908.0.0_x64__8wekyb3d8bbwe\Calculator.exe
但是,即使您知道该路径,也无法使用它直接启动计算器 。
Calculator,作为 AppX软件包(通常通过Microsoft Store分发的UWP应用程序),貌似必须通过其URL协议方案发布: [1 ]
Start-Process ms-calculator:
不幸的是,从PowerShell Core 7.0.0-preview.5 / Windows PowerShell v5.1开始,将-PassThru
添加到具有Start-Process
的AppX软件包URL的调用中会导致< em> error ,而不是返回代表已启动进程的对象-尽管Calculator仍会启动:
# Launches Calculator, but doesn't return a process object
# and reports an error instead:
PS> Start-Process ms-calculator: -PassThru
Start-Process : This command cannot be run completely because the system cannot find all the information required.
...
该问题已在this GitHub issue中报告。
解决方法:
# Invoke the stub executable *synchronously*, so that
# the real executable has already been launched by the time
# the call returns.
Start-Process -Wait -FilePath calc.exe
# Now get the real process object, named 'Calculator'.
# The most recently launched instance is used.
$processStatus = (Get-Process -Name Calculator | Sort-Object TotalProcessTime)[0]
注意:此方法并非万无一失,因为假设另一个进程可以假设同时启动一个Calculator实例,但在实践中应该足够健壮。
[1]有关通过包名称或其中基于通配符的部分(例如*Calculator*
,请参见this answer)自动发现AppX应用的协议名称的方法。