Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identify WindowsTerminal process ID #5694

Closed
jdhitsolutions opened this issue May 1, 2020 · 19 comments
Closed

Identify WindowsTerminal process ID #5694

jdhitsolutions opened this issue May 1, 2020 · 19 comments
Labels
Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Needs-Tag-Fix Doesn't match tag requirements Resolution-Won't-Fix We're just really obstinate about this. There's probably a good reason.

Comments

@jdhitsolutions
Copy link

Description of the new feature/enhancement

I'd like a way to identify the process ID associated with a Windows Terminal session. In PowerShell, I can see the session and profile ID variables. And it is easy enough to run Get-Process and find the Windows Terminal process. But what if there are 2 instances running? How can I tell which ID goes with which?

Proposed technical implementation details (optional)

PowerShell has a built-in variable $pid that helps me identify the current PowerShell process. It would be nice to either create a variable like $wtpid. Or add another environment variable that identifies the process ID of this Windows Terminal instance.

@jdhitsolutions jdhitsolutions added the Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. label May 1, 2020
@ghost ghost added Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting Needs-Tag-Fix Doesn't match tag requirements labels May 1, 2020
@DHowett-MSFT
Copy link
Contributor

Thanks for the request! I'm going to need a use case for this one o_O

@DHowett-MSFT
Copy link
Contributor

(You can use (Get-Process -Id $PID).Parent to get at the WT hosting your powershell instance.)

@jdhitsolutions
Copy link
Author

That PowerShell command only works in PowerShell 7, not Windows PowerShell. I am building a simple PowerShell tool that gets the Windows Terminal process and its children. This is not difficult if there is only one instance of Windows Terminal running. But I don't want to make that assumption.

@jdhitsolutions
Copy link
Author

I have a viable workaround for my immediate need. Still, since you are already populating environment variables with profile_id and session, I'm hoping it isn't that hard to add wt_pid.

@oising
Copy link
Collaborator

oising commented May 2, 2020

Hey Jeff -- for windows powershell, use (gwmi win32_process -Filter "processid = $pid").parentprocessid

PS C:\Users\oisin> gps -id (gwmi win32_process -Filter "processid = $pid").parentprocessid

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    675      37    55884      59052     307.84   6748   1 WindowsTerminal

@jdhitsolutions
Copy link
Author

That's the workaround I'm using.

@oising
Copy link
Collaborator

oising commented May 3, 2020

That's the workaround I'm using.

Ok, but how does that depend on the assumption that there's only one WT running? I'm not following.

@jdhitsolutions
Copy link
Author

This workaround using WMI also solves the multiple instances issue. I originally started with a simple Get-Process WindowsTerminal to get the process ID and go from there. That's when I realized that wouldn't work with I had 2 instances running. AND I realized the Parent property was added in PS7. I should have taken a little more time to think things through before posting. Personally, I'm set with the use case I posted.

But I still think it would be handy to have a variable of some sort to indicate the process id just as we do with $pid in PowerShell.

@DHowett-MSFT
Copy link
Contributor

So, I've been hemming and hawing about this issue.

I don't want to introduce another environment variable exposing WT state for the following reasons:

  • Every environment variable we expose is an API we need to continue supporting
    • You might think that a PID is going to be stable, but if we make any process model changes (the likes of which might support "tab tearoff", or "mixed elevation" (!)), the previously inviolate assumption that a single WT process is the host of your shell breaks down
    • (We can't poke a new value for WT_PID into each process running under terminal if it changes live; that's incredibly invasive!)
  • Environment variables are, unfortunately, inherited . . . like, by code, and all the processes spawned in VSCode's integrated terminal.

I'm not sure the benefit here outweighs the cost, especially since there's a viable workaround 😄

@DHowett-MSFT DHowett-MSFT added Resolution-Won't-Fix We're just really obstinate about this. There's probably a good reason. and removed Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting labels May 11, 2020
@tik9
Copy link

tik9 commented Jun 20, 2021

(gwmi win32_process -Filter "processid = $pid").parentprocessid
This workaround using WMI also solves the multiple instances issue. I originally started with a simple Get-Process WindowsTerminal to get the process ID and go from there. That's when I realized that wouldn't work with I had 2 instances running. AND I realized the Parent property was added in PS7. I should have taken a little more time to think things through before posting. Personally, I'm set with the use case I posted.

But I still think it would be handy to have a variable of some sort to indicate the process id just as we do with $pid in PowerShell.

In PS7, gwmi is deprecated and does not work. It is now get-ciminstance. I have several sub WindowsTerminal and one is freezing. So I want to get the freezing pid and stop it. In linux it would be ps axo pid,wchan:32,cmd taken here

@gerardog
Copy link

gerardog commented Sep 30, 2022

Checking the parent process doesn't work if WT is the Default Terminal Application, and you launch a console app from a graphical UI app.

  • For example via Explorer, Win+R then pwsh (with WT as default terminal app) the process tree looks like.
svchost.exe -> WindowsTerminal.exe
svchost.exe -> OpenConsole.exe

explorer.exe -> pwsh.exe -> conhost.exe

The console window is actually hosted by WindowsTerminal.exe.

  • But if you launch WT.EXE, then a pwsh profile the process tree looks like this:
explorer.exe -> wt.exe (shim that dies immediately) -> WindowsTerminal.exe +-> OpenConsole.exe
                                                                           |-> pwsh.exe

... and each additional PowerShell tab launches a new pair of OpenConsole.exe, pwsh.exe.

So, for the first case, I really couldn't find a perfect solution to get the exact WindowsTerminal.exe app.
But I found a terrible (but working) hack:

function Get-ConsoleHostProcessId {
  
    # Save Original ConsoleHost title	
    $oldTitle=$host.ui.RawUI.WindowTitle; 
    # Set unique console title.
    $r=(New-Guid); 
    $host.ui.RawUI.WindowTitle = "$r"; 
    #Find console window by title, then find console PID.
    $result = (tasklist.exe /FO LIST /FI "WINDOWTITLE eq $r") | Select-String -Pattern  "PID:*" -Raw
    
    if ($null -ne $result) {
        $consolePid = [int]($result.SubString(5).Trim());
    } else {
        $consolePid = 0;
    }        
    # Restore original ConsoleHost title.
    $host.ui.RawUI.WindowTitle=$oldTitle;

    return [int]$consolePid;
}
  
function Test-IsWindowsTerminal {
    $consolePid = Get-ConsoleHostProcessId;
    if ($consolePid -gt 0) {
        return (Get-Process -PID (Get-ConsoleHostProcessId)).ProcessName -eq "WindowsTerminal";
    }
    return $false;
}

@german-one
Copy link
Contributor

german-one commented Oct 31, 2022

@gerardog I've been struggling whether to publish my crazy attempt ... 🤣

Using Process Explorer I observed that the WindowsTerminal process has a handle to the Shell process open. Relying on the assumption that this is always the case I tried to write a piece of code that enumerates all open handles searching for the right process handle.
I'm fully aware that this is all based on assumptions, not recommended API, undocumented structures, unsafe memory handling, etc. Also, since I'm not experienced in PowerShell and C# I have no doubts that there is quite some room for improvements. However, I verified the results in Task Manager, Process Explorer, and Spy on Windows 11, ver. 10.0.22621.755. Turns out it works just fine for the current terminal process model.

Edit: I improved it a little ...

  • wrap unmanaged resources into managed objects to make the code more robust
  • use the ID of the Shell that actually spawned the Conhost process for investigations instead of relying on $PID
  • implement the Get-TermProc function that returns a System.Diagnostics.Process rather than only a process ID

Edit2:

  • make it PowerShell v.2 compliant

Last Edit:
You know what, I'm going to remove the PowerShell code here. I just created a repo with transcriptions in C, C++, and C# besides of the PowerShell script.
https://github.com/german-one/termproc


Output of the script, DefTerm vs. Conhost:

DefTerm_vs_Conhost

@BlueOnBLack
Copy link

BlueOnBLack commented Dec 3, 2022

Test method using Sysinternals handle tool

https://pastebin.com/Q9EEMAkw

@BlueOnBLack
Copy link

Also leave here
Parent process Check demo
detect SUB SUB -> SUB -> WT.exe

https://pastebin.com/wiWqwK5Z

@BlueOnBLack
Copy link

Also leave here - get PID of current seassion

https://pastebin.com/WYBwDca7

@BlueOnBLack
Copy link

BlueOnBLack commented Dec 3, 2022

some Explain ---
when wt is under 11 as default ...
WT keep handles of hidden process,
but it is never the parent proccess
so using handle tool to filter 'CMD(some_PID_HERE)' under any windowsterminal.exe files
so given the result, you find the pid :)
you can see what i'm talking about, open processEsxplorer or processHacker
search in handles section for process Type,
there is 2, one for cmd file under explorer.exe, and another one

@mrkey7
Copy link

mrkey7 commented May 4, 2024

Checking the parent process doesn't work if WT is the Default Terminal Application, and you launch a console app from a graphical UI app.

  • For example via Explorer, Win+R then pwsh (with WT as default terminal app) the process tree looks like.
svchost.exe -> WindowsTerminal.exe
svchost.exe -> OpenConsole.exe

explorer.exe -> pwsh.exe -> conhost.exe

The console window is actually hosted by WindowsTerminal.exe.

  • But if you launch WT.EXE, then a pwsh profile the process tree looks like this:
explorer.exe -> wt.exe (shim that dies immediately) -> WindowsTerminal.exe +-> OpenConsole.exe
                                                                           |-> pwsh.exe

... and each additional PowerShell tab launches a new pair of OpenConsole.exe, pwsh.exe.

So, for the first case, I really couldn't find a perfect solution to get the exact WindowsTerminal.exe app. But I found a terrible (but working) hack:

function Get-ConsoleHostProcessId {
  
    # Save Original ConsoleHost title	
    $oldTitle=$host.ui.RawUI.WindowTitle; 
    # Set unique console title.
    $r=(New-Guid); 
    $host.ui.RawUI.WindowTitle = "$r"; 
    #Find console window by title, then find console PID.
    $result = (tasklist.exe /FO LIST /FI "WINDOWTITLE eq $r") | Select-String -Pattern  "PID:*" -Raw
    
    if ($null -ne $result) {
        $consolePid = [int]($result.SubString(5).Trim());
    } else {
        $consolePid = 0;
    }        
    # Restore original ConsoleHost title.
    $host.ui.RawUI.WindowTitle=$oldTitle;

    return [int]$consolePid;
}
  
function Test-IsWindowsTerminal {
    $consolePid = Get-ConsoleHostProcessId;
    if ($consolePid -gt 0) {
        return (Get-Process -PID (Get-ConsoleHostProcessId)).ProcessName -eq "WindowsTerminal";
    }
    return $false;
}

Works in pwsh. error in powershell.

Select-String : A parameter cannot be found that matches parameter
name 'Raw'.
At C:\Users\Hill\Desktop\x.ps1:9 char:97
+ ...  LIST /FI "WINDOWTITLE eq $r") | Select-String -Pattern  "PID
:*" -Raw
+
    ~~~~
    + CategoryInfo          : InvalidArgument: (:) [Select-String]
   , ParameterBindingException
    + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.Pow
   erShell.Commands.SelectStringCommand

@MamiyaOtaru
Copy link

Checking the parent process doesn't work if WT is the Default Terminal Application, and you launch a console app from a graphical UI app.

  • For example via Explorer, Win+R then pwsh (with WT as default terminal app) the process tree looks like.
svchost.exe -> WindowsTerminal.exe
svchost.exe -> OpenConsole.exe

explorer.exe -> pwsh.exe -> conhost.exe

The console window is actually hosted by WindowsTerminal.exe.

  • But if you launch WT.EXE, then a pwsh profile the process tree looks like this:
explorer.exe -> wt.exe (shim that dies immediately) -> WindowsTerminal.exe +-> OpenConsole.exe
                                                                           |-> pwsh.exe

... and each additional PowerShell tab launches a new pair of OpenConsole.exe, pwsh.exe.
So, for the first case, I really couldn't find a perfect solution to get the exact WindowsTerminal.exe app. But I found a terrible (but working) hack:
function Get-ConsoleHostProcessId {

# Save Original ConsoleHost title	
$oldTitle=$host.ui.RawUI.WindowTitle; 
# Set unique console title.
$r=(New-Guid); 
$host.ui.RawUI.WindowTitle = "$r"; 
#Find console window by title, then find console PID.
$result = (tasklist.exe /FO LIST /FI "WINDOWTITLE eq $r") | Select-String -Pattern  "PID:*" -Raw

if ($null -ne $result) {
    $consolePid = [int]($result.SubString(5).Trim());
} else {
    $consolePid = 0;
}        
# Restore original ConsoleHost title.
$host.ui.RawUI.WindowTitle=$oldTitle;

return [int]$consolePid;

}

function Test-IsWindowsTerminal {
$consolePid = Get-ConsoleHostProcessId;
if ($consolePid -gt 0) {
return (Get-Process -PID (Get-ConsoleHostProcessId)).ProcessName -eq "WindowsTerminal";
}
return $false;
}

Works in pwsh. error in powershell.

Select-String : A parameter cannot be found that matches parameter
name 'Raw'.
At C:\Users\Hill\Desktop\x.ps1:9 char:97
+ ...  LIST /FI "WINDOWTITLE eq $r") | Select-String -Pattern  "PID
:*" -Raw
+
    ~~~~
    + CategoryInfo          : InvalidArgument: (:) [Select-String]
   , ParameterBindingException
    + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.Pow
   erShell.Commands.SelectStringCommand

change
$result = (tasklist.exe /FO LIST /FI "WINDOWTITLE eq $r") | Select-String -Pattern "PID:" -Raw
to
$result = ((tasklist.exe /FO LIST /FI "WINDOWTITLE eq $r") | Select-String -Pattern "PID:
").toString()

@AVOnMe
Copy link

AVOnMe commented Jan 8, 2025

By January 2025 we do it this way, the window doesn’t get focus so you can’t know its title.
But you count how many instances there are

Pwsh version:


$i=(Get-CimInstance Win32_Process -Filter "Name = 'OpenConsole.exe'"|select Name).Count

if ($i -le 1) {


$host.UI.RawUI.WindowTitle = "devEnv"


} else {

$drive="G:"; $host.UI.RawUI.WindowTitle = "devEnvWS"


}

Cmd batch version

for /f "tokens=* USEBACKQ" %%i in (`powershell -c "Get-CimInstance Win32_Process -Filter \"Name ^= 'OpenConsole.exe'\"|select Name" ^| find "OpenConsole.exe" /c`) do (
if %%i leq 1 (TITLE devEnv) else (
(SET "drive=G:") &TITLE devEnvWS
)
)

now that wmic is gone

then you can use one window to work on different projects

The lesson we learned is always the same, we study your behavior and then we play everything in the hand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Needs-Tag-Fix Doesn't match tag requirements Resolution-Won't-Fix We're just really obstinate about this. There's probably a good reason.
Projects
None yet
Development

No branches or pull requests

10 participants