Skip to content

Update CreatePseudoConsole documentation to mention the Console Host#348

Open
logiclrd wants to merge 2 commits intoMicrosoftDocs:mainfrom
logiclrd:createpseudoconsole-conhost-process
Open

Update CreatePseudoConsole documentation to mention the Console Host#348
logiclrd wants to merge 2 commits intoMicrosoftDocs:mainfrom
logiclrd:createpseudoconsole-conhost-process

Conversation

@logiclrd
Copy link
Copy Markdown

This PR adds a paragraph to the CreatePseudoConsole documentation that clarifies that this API function creates a process, and that it is possible to inadvertently isolate console applications from that process, interfering with signalling.

…aborating on how the Console Host process is launched by CreatePseudoConsole and can be inadvertently isolated from console applications by CreateProcess flags.
@logiclrd
Copy link
Copy Markdown
Author

When I was writing code recently that used this function, I ran into exactly this issue, and it took a fair bit of time and a Q&A discussion to understand what I was doing wrong. This paragraph would have saved me probably 5 hours. 🙂

@learn-build-service-prod
Copy link
Copy Markdown
Contributor

Learn Build status updates of commit 888d69d:

⚠️ Validation status: warnings

File Status Preview URL Details
docs/createpseudoconsole.md ⚠️Warning View Details

docs/createpseudoconsole.md

  • Line 77, Column 527: [Warning: hard-coded-locale - See documentation] Link 'https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags#CREATE_NEW_PROCESS_GROUP' contains locale code 'en-us'. For localizability, remove 'en-us' from links to most Microsoft sites.
  • Line 77, Column 527: [Suggestion: docs-link-absolute - See documentation] Absolute link 'https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags#CREATE_NEW_PROCESS_GROUP' will be broken in isolated environments. Replace with a relative link.

For more details, please refer to the build report.

Note: Your PR may contain errors or warnings or suggestions unrelated to the files you changed. This happens when external dependencies like GitHub alias, Microsoft alias, cross repo links are updated. Please use these instructions to resolve them.

@learn-build-service-prod
Copy link
Copy Markdown
Contributor

PoliCheck Scan Report

The following report lists PoliCheck issues in PR files. Before you merge the PR, you must fix all severity-1 and severity-2 issues. The AI Review Details column lists suggestions for either removing or replacing the terms. If you find a false positive result, mention it in a PR comment and include this text: #policheck-false-positive. This feedback helps reduce false positives in future scans.

✅ No issues found

More information about PoliCheck

Information: PoliCheck | Severity Guidance | Term
For any questions: Try searching the learn.microsoft.com contributor guides or post your question in the Learn support channel.

@logiclrd
Copy link
Copy Markdown
Author

I see the linter warning. I can't view the documentation because it's internal to the Microsoft organization, but based on it I looked for other cross-area links in the Console-Docs repository and made mine look the same. Hopefully I did it right. 🙂

@learn-build-service-prod
Copy link
Copy Markdown
Contributor

Learn Build status updates of commit 16d77ac:

✅ Validation status: passed

File Status Preview URL Details
docs/createpseudoconsole.md ✅Succeeded View

For more details, please refer to the build report.

@learn-build-service-prod
Copy link
Copy Markdown
Contributor

PoliCheck Scan Report

The following report lists PoliCheck issues in PR files. Before you merge the PR, you must fix all severity-1 and severity-2 issues. The AI Review Details column lists suggestions for either removing or replacing the terms. If you find a false positive result, mention it in a PR comment and include this text: #policheck-false-positive. This feedback helps reduce false positives in future scans.

✅ No issues found

More information about PoliCheck

Information: PoliCheck | Severity Guidance | Term
For any questions: Try searching the learn.microsoft.com contributor guides or post your question in the Learn support channel.

@DHowett
Copy link
Copy Markdown
Collaborator

DHowett commented Mar 24, 2026

thank you for documenting this! it's news to us too :)

@lhecker
Copy link
Copy Markdown
Contributor

lhecker commented Mar 24, 2026

I have approved this PR under the assumption that you've done your due diligence narrowing this problem down to passing CREATE_NEW_PROCESS_GROUP.

However, this is not intended to be an issue, as Dustin said. If you'd be willing to post a tiny repro to https://github.com/microsoft/terminal we could investigate this further. (It would be entirely acceptable even if you just AI vibe code some C/C++ test code.)

@logiclrd
Copy link
Copy Markdown
Author

I initially encountered this issue because in my application, which presents a terminal, if I ran ping, I could not interrupt it with ^C. I opened a Q&A question about that, which spawned some discussion:

https://learn.microsoft.com/en-us/answers/questions/5832200/c-sent-to-the-stdin-of-a-program-running-under-a-p

Immediately after posting the question, an AI response was posted that was 100% factually incorrect. It stated very authoritatively that CreatePseudoConsole doesn't create any processes, that the pipes supplied are simply handed to the child process verbatim, and that the important thing was to make sure that the ENABLE_PROCESSED_INPUT flag was set on the console input handle. This seemed very wrong to me, and I proceeded to dig into the underlying code from the microsoft/terminal repo. I wrote up a reply to the misleading AI response, and my reply, ironically, got flagged by automated spam detection and "deleted". Fortunately, the Q&A moderators came to the rescue, and the incorrect AI response was removed and my response to it was restored. I then merged my response into the main body of the question, so you can see the details there.

In response to the question, another user did some experimentation, adapting the EchoCon example application and finding that it did respond to ^C as expected. I was asked if I could put together a minimal reproduction, which I did. I placed it in the following repository:

https://github.com/logiclrd/CtrlCRepro/

The initial version of this reproduction only had one button. When clicked, it launched ping.exe, and then sent it ^C after 1250 ms.

I got a reply back to see what happened if I removed ProcessCreationFlags.NewProcessGroup, and when I did that, lo and behold, ^C started working.

I looked in more detail at the code, eventually writing up notes on the call sequences involved in input processing and ^C dispatch, which I have stashed in a file in another project:

https://github.com/logiclrd/QBX/blob/main/References/ConPTY%20control%20key%20processing.txt

In the course of my investigation, I tried a number of variations on the relative arrangements of processes and how exactly characters were sent/signals were raised (including calling the internal system function ConsoleControl myself, copying the necessary definitions and prototypes from the microsoft/terminal codebase. The common theme was that a process attached to a console would only receive CTRL_C_EVENTs if it was also in the same process group. Using CREATE_NEW_PROCESS_GROUP did not preclude attaching to the console and interacting with its input & output buffer, but it did break the signalling.

I just made an update to the repro application to make it easier to compare with & without ProcessCreationFlags.NewProcessGroup. With the update, there are various buttons you can click. The cluster on the left start a new child process, and you can choose between ping.exe (configured for 8 localhost pings) and CMD.exe, and also between CREATE_NEW_PROCESS_GROUP or not. The other buttons send input to the child app using the ConPTY stdin pipe: "exit" types the literal word "exit", "Enter" simulates a Return keypress, and "Ctrl+C" sends an ETX character to the input pipe.

With this, it is very easy to switch back and forth and observe the difference in ping.exe with and without CREATE_NEW_PROCESS_GROUP.

Confusingly, though, cmd.exe seems to react to ^C whether or not it is in the same process group. This one little datum calls into doubt the understanding of what exactly is going on...

I will see if I can resume the discussion on the Q&A and see if anyone can explain the difference between ping.exe and cmd.exe.

Copy link
Copy Markdown
Contributor

@lhecker lhecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll retract my approval until I (or someone else) had a look at the repro.

@logiclrd
Copy link
Copy Markdown
Author

logiclrd commented Mar 25, 2026

Okay upon further investigation, it appears what's going on is that ReadConsole checks if the input it has read is a ^C. When the ETX is written to the pipe, a Ctrl+C event is generated, but also a KEY_INPUT_RECORD describing C with the Ctrl key held down is inserted into the console input buffer. If the app isn't trying to read, then this doesn't do anything, and the app only breaks if it receives the out-of-band control signal. If it is trying to read, then this simply causes the read operation to abort, and CMD just loops and reads again from a fresh prompt.

Sources of these observations:

  1. I wrote a program that pauses for a bit, reads from the console, and then spends some time actively outputting to the console. It logs to the console every control event it receives. When it is run in the same process group, ^C always results in a control event. When it is run in a new process group, ^C never results in a control event. But regardless of whether it is in the same process group or a new one, if it's in the middle of a call reading from the console, then ^C aborts the read.

  2. If CMD is at the prompt, then ^C aborts the read, and CMD displays a new prompt and starts a fresh read. If CMD is busy doing something, such as dir /a/b/s, then ^C only has an effect if CMD is able to receive the control signal. If it's run in a new process group, then the dir /a/b/s does not appear to be interruptible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants