Repository navigation
Parent the ConPTY console window to the VS Code window - #342784
Conversation
Pass the native handle of the window hosting each terminal down to the pty host and node-pty, which calls ConptyReparentPseudoConsole. Windows that console apps parent to GetConsoleWindow(), such as the WAM sign-in picker used by Connect-AzAccount, then open above VS Code instead of behind it.
There was a problem hiding this comment.
🟡 Changes recommended
The required node-pty dependency update is missing, and overlapping window lookups can let startup proceed before parenting.
2 open issues
What changed in this PR
Adds Windows console ownership to local terminals so console-app dialogs appear above their hosting VS Code window.
Changes:
- Threads native window handles through the terminal process and PTY services.
- Sets ownership before startup and updates it when terminals move between windows.
- Adds process-manager tests and updates the agent-host PTY mock.
| File | Description |
|---|---|
| src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts | Tests startup ordering and window changes. |
| src/vs/workbench/contrib/terminal/electron-browser/localPty.ts | Forwards parent handles over IPC. |
| src/vs/workbench/contrib/terminal/common/terminal.ts | Adds the process-manager parenting contract. |
| src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts | Resolves and forwards native handles. |
| src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | Requests parenting during startup and attachment. |
| src/vs/platform/terminal/node/terminalProcess.ts | Applies ownership through node-pty. |
| src/vs/platform/terminal/node/ptyService.ts | Routes parenting to persistent processes. |
| src/vs/platform/terminal/node/ptyHostService.ts | Proxies parenting requests. |
| src/vs/platform/terminal/common/terminal.ts | Adds PTY parenting interfaces. |
| src/vs/platform/agentHost/test/node/agentHostTerminalManager.test.ts | Updates the PTY test implementation. |
Review effort: Balanced
Anthony Kim (anthonykim1)
left a comment
There was a problem hiding this comment.
No correctness problem found in the plumbing: setParentWindow is sent ahead of start() for new, revived and reattached processes, remoteAuthority is already set in the manager constructor so the early return is right, and the stale-window check in setParentWindow holds for A→B→A moves. Inline comments are design, all p2:
_parentWindowduplicates the per-window memoizationNativeHostService.getNativeWindowHandlealready has.readUInt32LEzero-extends the HWND; Windows' interop rule is sign-extension, whichreadInt32LEplus node-pty'sInt64Valuepath gives for free.- The
awaitinTerminalInstanceserializes the handle lookup ahead of_createProcess;createProcessalready has the right spot to await it.
Out of scope here but worth tracking: AgentHostPty and RemotePty don't implement setParentWindow, so agent-host terminals on Windows keep an unowned console window.
Blocked on microsoft/node-pty#995 shipping either way.
Anthony Kim (anthonykim1)
left a comment
There was a problem hiding this comment.
Three more p2s inline:
- Dialogs that are modal to the console window's root owner now disable the VS Code window, and nothing re-enables it if the console process dies first.
windowsUseConptyDll: falseis the only off switch.setParentWindowisn't trace-logged like the other node-pty calls.
- Start the window handle lookup without awaiting it in TerminalInstance; createProcess now waits for the handle of the latest window before start(), so a move during startup can't let the shell start unowned. - Drop the second per-window cache; NativeHostService already memoizes the lookup. - Sign-extend the low 32 bits of the HWND, like Windows does for handles. - Add terminal.integrated.windowsReparentConsoleWindow (experimental, ExP-controllable) as an off switch that keeps conpty.dll. - Trace-log node-pty's setParentWindow and whether it applied. - Drop the agent host TestPty stub now that IPty.setParentWindow is optional.
|
Pushed ae6b450 for the review; node-pty is at 909b13e (microsoft/node-pty#995). Verified on Windows 11. The unit tests ran on my local checkout, which has the same production changes:
Then in a dev build with both branches:
On agent host terminals (from the first review): agreed, that's out of scope here. They spawn with in-box ConPTY, so they'd need conpty.dll before they can reparent. I'll track that separately. |
The setting stays ExP-controllable through `experiment`, but it is not an experimental setting.
node-pty only re-enables the parent window on exit when asked to (conptyReenableParentWindowOnExit). VS Code only disables its windows while showing a window they own, so it opts in. Also log setParentWindow's result as "sent", since ConPTY applies the new owner asynchronously.
|
Pushed a274011. microsoft/node-pty#995 now only adds Then e4ae49e: node-pty now takes the handle as the Re-verified in a dev build with node-pty f699175:
Electron unit tests: 4454 passing, including the 5 parent window tests; node tests: 122. Type check, ESLint, and hygiene are clean. |
microsoft/node-pty#995 no longer has conptyParentWindow or conptyReenableParentWindowOnExit, so TerminalProcess calls setParentWindow right after spawn, which is before node-pty launches the shell, and no longer opts in to the re-enable.
node-pty now rejects negative window handles, since HWNDs are pointers. Sign-extend the signed 32-bit handle to an unsigned 64-bit value, the same bits node-pty used before, and never let setting the parent window fail the terminal launch.
node-pty now takes the HWND as the Buffer that Electron's getNativeWindowHandle() returns, so send the VSBuffer from IHostService.getNativeWindowHandle to the pty host unchanged instead of converting it to a number and back.
|
Base:
|
Adds IPty.setParentWindow() from microsoft/node-pty#995, which this PR uses.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Connect-AzAccountsign-in picker, then open in front of VS Code instead of behind it. Modal ones disable VS Code while they're open, as in Windows Terminal.start()for new, revived, and reattached processes, using the window the terminal is in by then. Send it again whenattachToElement()moves the terminal to another window.IHostService.getNativeWindowHandleand send its bytes to the pty host unchanged as aVSBuffer, since node-pty takes theBufferthat Electron'sgetNativeWindowHandle()returns. Skip remote terminals, and never fail terminal startup if the lookup, the IPC, or the node-pty call fails.setParentWindowalong theclearBufferIPC path. The pty host calls node-pty'ssetParentWindowright afterspawn(), before node-pty launches the shell, or right away if the pty is already running, and trace-logs whether it was sent. Only on Windows withwindowsUseConptyDll.ITerminalProcessOptions, which are persisted for revive and would replay a stale HWND.terminal.integrated.windowsReparentConsoleWindow(default on, ExP-controllable) to turn this off without turning off conpty.dll.TerminalProcessManagertests for sending the handle beforestart(), window moves, startup races, passing the handle bytes through unchanged, and the setting.node-ptyto1.2.0-beta.17, which addssetParentWindowfrom Add ConptyReparentPseudoConsole support node-pty#995.Inspirations from:
clearBuffer:LocalPty.clearBuffer,IPtyService.clearBuffer,PtyService.clearBuffer,PersistentTerminalProcess.clearBuffer, andTerminalProcess.clearBuffer.ConptyConnection::ReparentWindow; reparenting before the client launches comes fromConptyConnection::Start.IHostService.getNativeWindowHandle(windowId)comes fromMainThreadWindow.registerNativeHandle, relying on the per-window memoization inNativeHostService.getNativeWindowHandleinstead of a second cache.VSBufferrelies on the IPC protocol serializing and deserializing it as is.PtyService.serializeTerminalState, which persistsprocessLaunchConfigfor revive.terminal.integrated.enableWin32InputMode.Resolves https://github.com/microsoft/vscode-internalbacklog/issues/10198