2

My goal is to run several processes and save their ProcessName and Id for later usage. Here is my code

[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

Output of this script is:

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

I have also tried replace [System.Collections.ArrayList]$startedProcesses = @() with $startedProcesses = New-Object System.Collections.Generic.List[System.ComponentModel.Component] but I ended with the same result.

Questions: why I do not have calc under ProcessName? If I remove Start-Sleep 1 then i got calc under ProcessName. Why is that? Is this correct way of starting apps or am I doing something wrong? My OS: Windows 10 Home

Wakan Tanka
  • 7,542
  • 16
  • 69
  • 122
  • 1
    for me, on win7ps5.1, your code works as expected - the `calc` string is listed in the `ProcessName` list. it also works with the `Start-Sleep` lines commented out. – Lee_Dailey Nov 06 '19 at 01:01
  • 1
    Interesting, Calc appx does have this trouble, it seems like `calc` passes the request to `svchost`, then the Calculator appx launched. In this way, `calc` is not the parent process of `Calculator.exe` which means unable to locate via Win32_Process. The problem remains: `How to get the correct process id of Calculator.exe launched by calc?` – Larry Song Nov 06 '19 at 04:54
  • @Lee_Dailey: The problem surfaces on W8 and above, where `calc.exe` is just a _stub_ executable for the separate Calculator UWP application (AppX package); for more info on the latter, see [this answer](https://stackoverflow.com/a/58733061/45375). – mklement0 Nov 06 '19 at 15:17
  • Good sleuthing, @LarrySong. My answer shows a solution, which, while not foolproof, should be robust enough. – mklement0 Nov 06 '19 at 16:07

1 Answers1

4

Your problem is unrelated to the use of Start-Sleep.

Instead, the problem is that, as of Windows 10, calc.exe is only a stub executable for the process that is ultimately launched, and the stub process exits as soon as it has launched the real executable.

If you remove the Start-Sleep call between the Start-Process and the echo calls, the stub process object contained in $startedProcesses typically does reflect the stub executable's name - calc.exe - in the ProcessName column at that time, due to still being alive (albeit only shortly), but you still won't be able to track the real executable's process lifetime and exit code through that object.

The real executable's name is Calculator.exe, whose exact path incorporates a full AppX package name (a package name, a version number, and publisher ID):

For instance, after launching calc.exe, (Get-Process Calculator).Path yields something like:

C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_10.1908.0.0_x64__8wekyb3d8bbwe\Calculator.exe

However, you can NOT use that path to launch Calculator directly - you'll get an Access denied error, even when running with elevation.

Calculator, as an AppX package (a UWP application typically distributed via the Microsoft Store), is most easily launched by its URL scheme:

Start-Process ms-calculator:

Note:

  • For an automated way to discover an AppX app's URL scheme names by package name or wildcard-based part of it - e.g., *Calculator* - see this answer.

  • The less convenient alternative is to use a shell: URL that is based on an application's AppID - see this answer.

Unfortunately, as of PowerShell (Core) 7.2 / Windows PowerShell v5.1, adding -PassThru to invocation of an AppX-package URL with Start-Process results in an error instead of returning an object representing the launched process - though Calculator still launches; the same applies to -Wait:

# 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.
...

The problem has been reported in GitHub issue #10996.


Workaround:

# 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 newly launched or a preexisting instance is used (see below).
$processStatus = Get-Process -Name Calculator |
                 Where-Object SessionId -eq (Get-Process -ID $PID).SessionId).ID

Note: Calculator only ever creates one process per user session (window station): If a process already exists, subsequent launches delegate to this process.

mklement0
  • 382,024
  • 64
  • 607
  • 775