3

I'm trying to launch Windows applications using their AppID such as Microsoft.WindowsCalculator_8wekyb3d8bbwe!App which I get by calling Get-StartApps

Currently I can launch the applications but can't get the correct PID

cmd = exec.Command("powershell", "start", `shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App`)
err := cmd.Start()
fmt.Println(cmd.Process.Pid)

This returns the PID of powershell

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe start shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App

Is there a way to launch the application by the AppID and still get the correct PID?

mklement0
  • 382,024
  • 64
  • 607
  • 775
zeitue
  • 1,674
  • 2
  • 20
  • 45
  • 1
    You can call the COM API but even then it will not give you the PID unless you capture it in a Job and even then, UWP hosts and other WinRT junk might get in the way... – Anders Sep 12 '22 at 15:54
  • @Anders I'll try that, tho not sure how to capture the PID in a job. – zeitue Sep 12 '22 at 17:01
  • 1
    JOB_OBJECT_MSG_NEW_PROCESS – Anders Sep 12 '22 at 17:07
  • @mklement0 It works, but kinda inconstant, which you also said in your answer – zeitue Sep 13 '22 at 06:22
  • Please see my update, which should now be robust. It turns out that the fact that Calculator only ever creates _one_ process per user session makes identifying the relevant process simple and, from what I can tell, reliable. Note that more work is required if you want to identify the new _window_ that gets created. – mklement0 Sep 13 '22 at 15:21
  • @mklement0 This works if I change Calculator to CalculatorApp. Tho looking up the names for each of the apps is not really a good solution. Tho it seems there is not real solution to this, so I'll give the accepted answer to you as it does work. – zeitue Sep 13 '22 at 16:19
  • Thanks, @TaylorRamirez. Re `CalculatorApp`: that is curious, because on my Windows 10 machine it is definitely `Calculator` - are you on Windows 11? After launching Calculator, `(Get-Process *calc*).Name` returns `CalculatorApp` for you? In general, yes, searching by process name isn't ideal, but it's the best solution under the circumstances. – mklement0 Sep 13 '22 at 19:55
  • 1
    @mklement0 Yeah this test machine it Windows 11. You think MS would fix this issue. – zeitue Sep 14 '22 at 16:06

1 Answers1

3

tl;dr

// Make PowerShell not only launch Calculator, but also
// determine and output its PID, as described in the next section.
out, _ := 
        exec.Command(
          `powershell.exe`, 
          `-NoProfile`, 
          `-Command`, 
          `Start-Process -ErrorAction Stop calculator: ; (Get-Process Calculator | Where-Object SessionId -eq (Get-Process -ID $PID).SessionId).ID`,
        ).Output()

// Parse stdout output, which contains the PID, into an int
var pid int
fmt.Sscanf(string(out), "%d\n", &pid)

  • In principle, you can pass -PassThru to PowerShell's Start-Process (start) cmd, which returns a process-info object that has an .Id property containing the launched process' PID, and output the latter.

  • Unfortunately, with UWP / AppX applications specifically, such as Calculator, this does not work, which is a problem that exists in the underlying .NET APIs, up to at least .NET 6.0 - see GitHub issue #10996.

You can try the following workaround:

  • Launch the AppX application with Start-Process, which indirectly creates a process whose name is Calculator (Windows 10) / CalculatorApp (Windows 11).

    • You can identify this name yourself if you run (Get-Process *calc*).Name after launching Calculator. Get-Process *calc* | Select-Object Name, Path would show the executable path too, but note that this executable should be considered an implementation detail and can not be invoked directly.
  • Return the ID of that Calculator / CalculatorApp process. The fact that Calculator only ever creates one such process in a given user session actually makes identifying that process easy.

    • Note that this means that the PID of a preexisting Calculator process may be returned, which, however, is the correct one, because the transient process launched by Start-Process simply delegates creation of a new Calculator window to an existing process.

    • If you wanted to identify the newly created window, more work would be required: You'd have to enumerate the process' windows and identify the one with the highest z-order.

PowerShell code (note: in Windows 11, replace Calculator with CalculatorApp):

# Launch Calculator - which may reuse an existing instance and
# merely create a new *window* - and report the PID.
Start-Process -ErrorAction Stop calculator:
(Get-Process Calculator | Where-Object SessionId -eq (Get-Process -ID $PID).SessionId).ID

Note that I've used the URL scheme calculator: as a simpler way to launch Calculator.

Note:

  • The Where-Object SessionId -eq (Get-Process -ID $PID).SessionId guards against mistakenly considering potential Calculator processes created by other users in their own sessions (Get-Process returns all processes running on the local machine, across all user sessions). Filtering by .SessionID, i.e. by the active user session (window station), prevents this problem.

As a PowerShell CLI call:

powershell.exe -NoProfile -Command "Start-Process -ErrorAction Stop calculator: ; (Get-Process Calculator | Where-Object SessionId -eq (Get-Process -ID $PID).SessionId).ID"
mklement0
  • 382,024
  • 64
  • 607
  • 775