Skip to content

Conversation

@lhecker
Copy link
Member

@lhecker lhecker commented Jul 1, 2025

This changes the ConPTY handoff COM server from REGCLS_SINGLEUSE
to REGCLS_MULTIPLEUSE. The former causes a race condition, because
handoff runs concurrently with the creation of WinUI windows.
This can then result in the a window getting the wrong handoff.

It then moves the "root" of ConPTY handoff from TerminalPage
(WindowEmperor -> AppHost -> TerminalWindow -> TerminalPage)
into WindowEmperor (WindowEmperor).

Closes #19049

Validation Steps Performed

  • Launching cmd from the Start Menu shows a "Command Prompt" tab ✅
  • Win+R -> cmd creates windows in the foreground ✅
  • Win+R -> cmd /c start /max cmd creates a fullscreen tab ✅
    • This even works for multiple windows, unlike with Canary ✅
  • Win+R -> cmd /c start /min cmd does not work ❌
    • It also doesn't work in Canary, so it's not a bug in this PR ✅

Copy link
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

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

11/16

winrt::TerminalApp::CommandlineArgs c;
c.Commandline(args);
c.CurrentDirectory(currentDirectory);
c.CurrentEnvironment(envString);
Copy link
Member

Choose a reason for hiding this comment

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

make sure to test env stuff. the double null terminated strings can be a pain

Copy link
Member Author

Choose a reason for hiding this comment

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

FWIW this doesn't actually change the code at all. The only difference is constructor VS property.

const auto args = commandlineToArgArray(GetCommandLineW());
if (_windows.empty() || args.size() != 1)

if (args.size() == 2 && args[1] == L"-Embedding")
Copy link
Member

Choose a reason for hiding this comment

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

just to confirm: wt -Embedding should result in a headless terminal, which can respond to ConPTY handoffs, but otherwise keeps running?

Copy link
Member

Choose a reason for hiding this comment

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

e.g. it creates the toplevel hidden window to receive messages and all, and you can add new windows with wt nt?

does this cause any session restoration? how does that work

Copy link
Member Author

Choose a reason for hiding this comment

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

just to confirm: wt -Embedding should result in a headless terminal, which can respond to ConPTY handoffs, but otherwise keeps running?

Yes

e.g. it creates the toplevel hidden window to receive messages and all, and you can add new windows with wt nt?

Yes

does this cause any session restoration?

Yes

how does that work

Session restoration is relatively simple nowadays, not like in the past. Check out line 338 right above this.

@DHowett
Copy link
Member

DHowett commented Jul 1, 2025

Does this make any dent in the incidence of "Terminal window opens without any tabs" problem?

@DHowett
Copy link
Member

DHowett commented Jul 1, 2025

  • Win+R -> cmd /c start /min cmd does not work ❌
    • It also doesn't work in Canary, so it's not a bug in this PR ✅

This was by design. It is rejected before handoff starts, because we thought that starting up minimized usually indicates non-foreground workloads. We didn't want to mess with those

@lhecker
Copy link
Member Author

lhecker commented Jul 1, 2025

Does this make any dent in the incidence of "Terminal window opens without any tabs" problem?

Yes, that's fixed now to my knowledge.

if (auto conn = args.Connection())
{
_root->SetInboundListener(true);
_root->CreateTabFromConnection(std::move(conn));
Copy link
Member

Choose a reason for hiding this comment

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

when will we get a connection in an Args versus in SetStartupCommandline where it will get moved into _startupConnection?

Is this a case where handoff (from cold start) is differnet from handoff (already running)?

Copy link
Member Author

Choose a reason for hiding this comment

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

If you meant what I think you meant, then the difference is "dispatch into a new window vs into an existing one". But I'm not sure I understand your question. Can you rephrase it if needed?

Copy link
Member

Choose a reason for hiding this comment

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

There is one path through handoff which populates _startupConnection. There is a second path through handoff which populates an Args Connection property.

How are they different?

Is it that one is used during cold startup (handoff causes Terminal to start), and one is used when Terminal is already running (handoff causes a new tab or a new window in a running Terminal instance)?

Copy link
Member Author

@lhecker lhecker Jul 1, 2025

Choose a reason for hiding this comment

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

Oh, so it's what I thought you meant. It's indeed "dispatch into a new window vs into an existing one". Put differently: _startupConnection is for when a window is created, WinUI has not yet finished initialization, and we're deferring the creation of a tab until after WinUI is done. The other path which uses the Connection property right here is for when the window already exists and we're simply creating another new tab in an existing window.

There is a second path through handoff which populates an Args Connection property.

To be clear, both populate the Connection property. WindowEmperor works "correctly" and makes no distinction between creating a new window with 1 tab and adding 1 tab to an existing window. In this spirit, there's only 1 place which populates the Connection property. It then goes through the same code inside WindowEmperor.

It's TerminalWindow which makes this distinction, and I don't think it should.

@DHowett DHowett closed this Jul 1, 2025
@DHowett DHowett reopened this Jul 1, 2025
@lhecker lhecker enabled auto-merge (squash) July 1, 2025 18:35
@lhecker lhecker merged commit a25d968 into main Jul 1, 2025
21 of 23 checks passed
@lhecker lhecker deleted the dev/lhecker/19049-handoff-done-right-within-limits branch July 1, 2025 19:00
@DHowett
Copy link
Member

DHowett commented Jul 29, 2025

Can't backport to 1.22; too much architectural change

DHowett pushed a commit that referenced this pull request Jul 29, 2025
This changes the ConPTY handoff COM server from `REGCLS_SINGLEUSE`
to `REGCLS_MULTIPLEUSE`. The former causes a race condition, because
handoff runs concurrently with the creation of WinUI windows.
This can then result in the a window getting the wrong handoff.

It then moves the "root" of ConPTY handoff from `TerminalPage`
(WindowEmperor -> AppHost -> TerminalWindow -> TerminalPage)
into `WindowEmperor` (WindowEmperor).

Closes #19049

## Validation Steps Performed
* Launching cmd from the Start Menu shows a "Command Prompt" tab ✅
* Win+R -> `cmd` creates windows in the foreground ✅
* Win+R -> `cmd /c start /max cmd` creates a fullscreen tab ✅
  * This even works for multiple windows, unlike with Canary ✅
* Win+R -> `cmd /c start /min cmd` does not work ❌
  * It also doesn't work in Canary, so it's not a bug in this PR ✅

(cherry picked from commit a25d968)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgbpecU
Service-Version: 1.23
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.

Creating new processes in new Windows Terminals somehow crashes all instances of Windows Terminal when those processes exit

3 participants