Using conhost as a PTY host? Sorta? #23328

Closed
opened 2026-01-31 08:39:01 +00:00 by claunia · 4 comments
Owner

Originally created by @prettydiff on GitHub (Jun 1, 2025).

Windows Terminal version

1.22.11141.0

Windows build number

10.0.26100.0

Other Software

This is a re-post from the PowerShell repository per their suggestion: https://github.com/PowerShell/PowerShell/issues/25625

  • bash.exe from git-svm for windows
  • cmd.exe
  • PowerShell v7.5.1
  • PowerShell v5.1
  • Node.js 23.x.x

Steps to reproduce

I am using conhost.exe as a PTY to execute and stream various shells to JavaScript. I am executing conhost as a Node.js child process. Here are two examples of execution:

child_process.spawn(
    "c:\\windows\\system32\\conhost.exe",
    ["c:\\program files\\powershell\\7\\pwsh.exe"],
    {
        env: process.env,
        windowsHide: true
    });

child_process.spawn(
    "c:\\windows\\system32\\conhost.exe",
    ["c:\\program files\\git\\bin\\bash.exe"],
    {
        env: process.env,
        windowsHide: true
    });

Challenges

I am having trouble finding documentation for a ConHost API, but I was able to find a MSDN page listing a bunch of C# functions for modifying ConHost behavior. I did find the PowerShell 7 API documentation, but its only building custom exe files with C# and .NET. I work in a secure environment where I cannot build, deploy, or pull in arbitrary executable files not already part of an approved Windows image.

If I could arbitrarily execute binaries to solve for these conerns I would just use XTERM.js which already solves for these concerns with its own internal PTY and is already a Microsoft project. If this double prompt issue is resolved XTERM.js can become functionally obsolete aside from convenience and shell resize.

Expected Behavior

The PTY stdout stream to third party applications behaves identically as it does for MiscroSoft's TTY applications.

Actual Behavior

  • cmd.exe - works perfectly
  • PowerShell v5.1 - Displays the prompt followed by \r\n>> with some ANSI control characters mixed in.
  • PowerShell v7.5.1 - Same as above but the hidden ANSI control sequences are different.
  • Bash.exe - One completely normal prompt followed by a second completely separate and identical prompt as though the user pressed the "enter" key to generate multiple lines in the shell.

Problem Example 1 - PowerShell v7.5.1 (partial double prompt with visible characters)

PS C:\Users\info\webserver> $psGet=Get-Host
>> 

Problem Example 1 - PowerShell v7.5.1 (partial double prompt with ANSI control characters)

PS C:\\Users\\info\\webserver> \u001b[92m$psGet\u001b[90m=\u001b[93mGet-Host\u001b[37m\r\n>> \r\n\u001b[?25h

Problem Example 2 - PowerShell v7.5.1 (partial double prompt with visible characters)

PS C:\Users\info\webserver> 
>> 

Problem Example 2 - PowerShell v7.5.1 ( partial double prompt with ANSI control characters)

PS C:\\Users\\info\\webserver> \u001b[37m\r\n>> \u001b[38;29H\u001b[?25h

Problem Example 3 - Bash.exe (double prompt)

info@DESKTOP-E98HNIV MINGW64 ~/webserver (certs)
$ 

info@DESKTOP-E98HNIV MINGW64 ~/webserver (certs)
$ 

Except for the double prompt this works perfectly and I can stream shell access across a network into a web browser. ConHost and/or Node.js even successfully executes these shells in a headless mode without use of an undocumented headless option for ConHost. The shells themselves do not output any error messaging. Even complex things like Vim and line wrapping displays correctly.

This defect is only present on the STDOUT stream from the PTY connection. It is not present in the TTY of a graphical terminal interface. This means the problem cannot be reproduced using PowerShell as a desktop application even when PowerShell is the executing shell. I suspect any means to call conhost without using a TTY interface would reproduce this.

Originally created by @prettydiff on GitHub (Jun 1, 2025). ### Windows Terminal version 1.22.11141.0 ### Windows build number 10.0.26100.0 ### Other Software This is a re-post from the PowerShell repository per their suggestion: https://github.com/PowerShell/PowerShell/issues/25625 * bash.exe from git-svm for windows * cmd.exe * PowerShell v7.5.1 * PowerShell v5.1 * Node.js 23.x.x ### Steps to reproduce I am using conhost.exe as a PTY to execute and stream various shells to JavaScript. I am executing conhost as a Node.js child process. Here are two examples of execution: ``` child_process.spawn( "c:\\windows\\system32\\conhost.exe", ["c:\\program files\\powershell\\7\\pwsh.exe"], { env: process.env, windowsHide: true }); child_process.spawn( "c:\\windows\\system32\\conhost.exe", ["c:\\program files\\git\\bin\\bash.exe"], { env: process.env, windowsHide: true }); ``` #### Challenges I am having trouble finding documentation for a ConHost API, but I was able to find a MSDN page listing a bunch of C# functions for modifying ConHost behavior. I did find the PowerShell 7 API documentation, but its only building custom exe files with C# and .NET. I work in a secure environment where I cannot build, deploy, or pull in arbitrary executable files not already part of an approved Windows image. If I could arbitrarily execute binaries to solve for these conerns I would just use XTERM.js which already solves for these concerns with its own internal PTY and is already a Microsoft project. If this double prompt issue is resolved XTERM.js can become functionally obsolete aside from convenience and shell resize. ### Expected Behavior The PTY stdout stream to third party applications behaves identically as it does for MiscroSoft's TTY applications. ### Actual Behavior * cmd.exe - works perfectly * PowerShell v5.1 - Displays the prompt followed by `\r\n>> ` with some ANSI control characters mixed in. * PowerShell v7.5.1 - Same as above but the hidden ANSI control sequences are different. * Bash.exe - One completely normal prompt followed by a second completely separate and identical prompt as though the user pressed the "enter" key to generate multiple lines in the shell. #### Problem Example 1 - PowerShell v7.5.1 (partial double prompt with visible characters) ``` PS C:\Users\info\webserver> $psGet=Get-Host >> ``` #### Problem Example 1 - PowerShell v7.5.1 (partial double prompt with ANSI control characters) ``` PS C:\\Users\\info\\webserver> \u001b[92m$psGet\u001b[90m=\u001b[93mGet-Host\u001b[37m\r\n>> \r\n\u001b[?25h ``` #### Problem Example 2 - PowerShell v7.5.1 (partial double prompt with visible characters) ``` PS C:\Users\info\webserver> >> ``` #### Problem Example 2 - PowerShell v7.5.1 ( partial double prompt with ANSI control characters) ``` PS C:\\Users\\info\\webserver> \u001b[37m\r\n>> \u001b[38;29H\u001b[?25h ``` #### Problem Example 3 - Bash.exe (double prompt) ``` info@DESKTOP-E98HNIV MINGW64 ~/webserver (certs) $ info@DESKTOP-E98HNIV MINGW64 ~/webserver (certs) $ ``` Except for the double prompt this works perfectly and I can stream shell access across a network into a web browser. ConHost and/or Node.js even successfully executes these shells in a headless mode without use of an undocumented *headless* option for ConHost. The shells themselves do not output any error messaging. Even complex things like Vim and line wrapping displays correctly. This defect is only present on the STDOUT stream from the PTY connection. It is not present in the TTY of a graphical terminal interface. **This means the problem cannot be reproduced using PowerShell as a desktop application even when PowerShell is the executing shell.** I suspect any means to call conhost without using a TTY interface would reproduce this.
claunia added the Needs-TriageIssue-Bug labels 2026-01-31 08:39:01 +00:00
Author
Owner

@lhecker commented on GitHub (Jun 2, 2025):

conhost is not a PTY, it's a terminal. As part of an implementation detail, it just so happens to also be used for implementing Microsoft's PTY API at the moment (CreatePseudoConsole). It's not guaranteed that this will remain the case in the future. (...and if I stay at Microsoft long enough, it'll definitely not be the case.)

So, if you're not using CreatePseudoConsole, are you spawning something like this?

conhost --headless --width 123 --height 456 --signal 0xabc --server 0xdef

That's the way CreatePseudoConsole currently works internally and it may stop working at any point in the future.

Your child_process.spawn pseudocode at the start of your comment however shows that you don't pass these arguments. This should spawn conhost in a windowed mode and it should not produce any stdout whatsoever. This conflicts with your description of how you're capturing stdout from conhost.

I feel like you left out some details about your issue... For what it's worth, this issue is likely something we cannot help with, because the only supported way to get a PTY is via CreatePseudoConsole.

@lhecker commented on GitHub (Jun 2, 2025): conhost is not a PTY, it's a terminal. As part of an implementation detail, it just so happens to also be used for implementing Microsoft's PTY API at the moment (`CreatePseudoConsole`). It's not guaranteed that this will remain the case in the future. (...and if I stay at Microsoft long enough, it'll _definitely_ not be the case.) So, if you're not using `CreatePseudoConsole`, are you spawning something like this? ``` conhost --headless --width 123 --height 456 --signal 0xabc --server 0xdef ``` That's the way `CreatePseudoConsole` currently works internally and it may stop working at any point in the future. Your `child_process.spawn` pseudocode at the start of your comment however shows that you don't pass these arguments. This should spawn conhost in a windowed mode and it should not produce any stdout whatsoever. This conflicts with your description of how you're capturing stdout from conhost. I feel like you left out some details about your issue... For what it's worth, this issue is likely something we cannot help with, because the only supported way to get a PTY is via `CreatePseudoConsole`.
Author
Owner

@prettydiff commented on GitHub (Jun 2, 2025):

@lhecker I am executing in this manner:

c:\windows\system32\conhost.exe 'c:\program files\powershell\7\pwsh.exe'

If I execute PowerShell 7 through a third party application like Node without a PTY I can execute simple tasks like "ls" and I get back beautiful formatted output with all the ANSI control characters and formatting. It will works perfectly without a PTY on simple things only. When I execute VIM or exceed the column width of the shell on input the shell locks. When I do execute PowerShell through conhost.exe then absolutely everything works perfectly, but I get the double prompt.

If you think I am leaving anything out please let me know I am eager to resolve these concerns and will disclose all I have attempted on this effort. Here is my currently working code with some descriptions:

e37b13ad38/lib/services/terminal.ts (L50-L107)

In that code sample I am passing in absolute file paths for the pty and shell, such as:

  • pty - "C:\Windows\System32\conhost.exe"
  • shell - "c:\program files\powershell\7\pwsh.exe"

The commented block on lines 31-49 is the preamble for using Microsoft's node-pty solution, which I cannot use at work because it makes use of unapproved binaries. I suspect node-pty does make use of CreatePseudoConsole in the binaries it generates.

The input is streamed to the shell from a network socket on line 68 or 72. The output from the shell is streamed back to the network socket on line 76 via line 103.

@prettydiff commented on GitHub (Jun 2, 2025): @lhecker I am executing in this manner: ```console c:\windows\system32\conhost.exe 'c:\program files\powershell\7\pwsh.exe' ``` If I execute PowerShell 7 through a third party application like Node without a PTY I can execute simple tasks like "ls" and I get back beautiful formatted output with all the ANSI control characters and formatting. It will works perfectly without a PTY on simple things only. When I execute VIM or exceed the column width of the shell on input the shell locks. When I do execute PowerShell through conhost.exe then absolutely everything works perfectly, but I get the double prompt. If you think I am leaving anything out please let me know I am eager to resolve these concerns and will disclose all I have attempted on this effort. Here is my currently working code with some descriptions: https://github.com/prettydiff/webserver/blob/e37b13ad387291665d37b9cf2dc46e3de9b3d6c9/lib/services/terminal.ts#L50-L107 In that code sample I am passing in absolute file paths for the pty and shell, such as: * pty - "C:\Windows\System32\conhost.exe" * shell - "c:\program files\powershell\7\pwsh.exe" The commented block on lines 31-49 is the preamble for using Microsoft's node-pty solution, which I cannot use at work because it makes use of unapproved binaries. I suspect node-pty does make use of CreatePseudoConsole in the binaries it generates. The input is streamed to the shell from a network socket on line 68 or 72. The output from the shell is streamed back to the network socket on line 76 via line 103.
Author
Owner

@DHowett commented on GitHub (Jun 2, 2025):

Part of the problem is: conhost doesn't automatically run in PTY mode when you launch it like that. You aren't passing the right set of arguments (--headless and --width+--height at least.)

If you aren't passing those, you are not guaranteed to be getting a hosted PTY session; rather, you are likely to be getting an unsupported third thing.

@DHowett commented on GitHub (Jun 2, 2025): Part of the problem is: conhost doesn't automatically run in PTY mode when you launch it like that. You aren't passing the right set of arguments (`--headless` and `--width`+`--height` at least.) If you aren't passing those, you are not guaranteed to be getting a hosted PTY session; rather, you are likely to be getting an unsupported third thing.
Author
Owner

@DHowett commented on GitHub (Jun 2, 2025):

To be entirely clear: this is not a supported API surface.

If you can call native functions in native Microsoft libraries, you should be using CreatePseudoConsole through P/Invoke or Node FFI, and following the guidance on hosting a pseudoconsole session.

@DHowett commented on GitHub (Jun 2, 2025): To be entirely clear: this is not a supported API surface. If you can call native functions in native Microsoft libraries, you should be using `CreatePseudoConsole` through P/Invoke or Node FFI, and following the guidance on [hosting a pseudoconsole session](https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#23328