Fix DPI scaling for embedded PuTTY and PowerShell windows on multi-monitor setups#3352
Fix DPI scaling for embedded PuTTY and PowerShell windows on multi-monitor setups#3352mergify[bot] merged 15 commits intomainfrom
Conversation
Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
|
@copilot this doesn't fix the behavior. Still the font is to small / doesn't scale |
Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
The previous fix only resized the embedded window's bounds but didn't tell the embedded process to re-render its fonts at the new DPI. The real issue is that after The fix in commit |
|
@copilot this still doesnt work. Check what controls are in use, why this happens and how to fix it properly. There is a mix between WPF and Win32/embedded process. |
…o embedded window Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
The root cause is the cross-process window embedding architecture. Here's what happens: Why it wasn't working: The previous The fix (commit Order is also fixed:
After researching SuperPuTTY (the project NETworkManager's embedding code originates from) and other cross-process window embedding implementations, this is a well-known unsolved problem — no application in this space has a clean in-place fix for it. The root cause is a hard Windows OS limitation: DPI change notifications are never forwarded across process boundaries after New approach in commit When the DPI changes:
This is the only approach that lets each process's native DPI handler run through the normal Windows code path, without relying on cross-process message injection. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
…ANGED natively Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
|
@copilot Doesn't work, Do some more research. are there any undocumented win32 api maybe you could use? Are there other tools who successfully did this? |
…TTY + Console API for PowerShell Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
After deeper research and analysis, I found three independent bugs in the previous approach and implemented two entirely different strategies for the two tools (commit Why nothing worked before:
What other tools do:
Three fixes in commit
Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
…DpiChanged delivery Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
…hreadDpiHostingBehavior from NativeMethods Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
|
@copilot Add this PR / Change regarding DPI / Font to the changelog next-release to the "Whats new?" section (2 entries, for PowerShell and for PuTTY - see other changelogs as reference). Also add to the "Bug Fixes" that the intial size (width/heigth) has been fixed for PuTTY, TigerVNV and Powershell. |
… changelog Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
Added in commit
|
|
@copilot Adjust the blog post:
Review the whole document again, validate it, etc. |
…remove "What Does Not Work" table, rewrite summary Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
|
@copilot Adjust this Pull Request description to match what this PR actually does... |
Updated in commit
|
Updated the PR description to accurately reflect what was actually implemented: PowerShell DPI fix via Console API ( |
Changes proposed in this pull request
Fix: #2974
Fix: #2973
PowerShell — DPI font scaling
GetDpiForWindow(_appWin)beforeSetParentinConnect()to record conhost's initial DPI baseline, then callTryRescaleConsoleFont(pid, currentPanelDpi / initialWindowDpi)immediately after embedding if the DPIs differ — prevents compounding errors from subsequent relative rescalesWindowsFormsHost.DpiChanged: callTryRescaleConsoleFontwith theNewDpi / OldDpiratio to rescale the console font cross-process via the Windows Console API (AttachConsole+SetCurrentConsoleFontEx,kernel32.dll), bypassing theWM_DPICHANGEDcross-process delivery limitationPuTTY — DPI font scaling
GetDpiForWindow(_appWin)beforeSetParentinConnect(), then callTrySendDpiChangedMessage(_appWin, initialWindowDpi, currentPanelDpi)after embedding if the DPIs differ — corrects the initial DPI baselineWindowsFormsHost.DpiChanged: sendWM_DPICHANGED(0x02E0) explicitly to the PuTTY window with the new DPI packed intowParam(LOWORD = DPI X, HIWORD = DPI Y) and aRECT*inlParam, sinceWM_DPICHANGEDis not forwarded across process boundaries afterSetParentInitial
WindowsFormsHostsizing fix (PuTTY, PowerShell, TigerVNC)WindowHost.Height/WindowHost.Widthin physical pixels at load time usingVisualTreeHelper.GetDpiscale factors — fixes the embedded panel being undersized on high-DPI monitors at startupNativeMethods.cschangesGetDpiForWindowP/Invoke (user32.dll, Windows 10 1607+)TrySendDpiChangedMessagehelper (PuTTY DPI injection)TryRescaleConsoleFonthelper (PowerShell Console API font rescaling)GetWindowRect,AttachConsole,FreeConsole,CreateFile,GetCurrentConsoleFontEx,SetCurrentConsoleFontEx,RECT,COORD,CONSOLE_FONT_INFOEXSWP_SHOWWINDOW,WS_CHILD,MONITOR_DEFAULTTONEAREST,HWND_BOTTOM,DPI_HOSTING_BEHAVIORenum,SetThreadDpiHostingBehaviorDocumentation
Website/blog/2026-03-04-high-dpi-embedded-processes/index.md— technical write-up for .NET/Windows developers explaining the Console API approach (conhost-based processes) and the explicitWM_DPICHANGEDapproach (GUI processes) with full C#/XAML source codeWebsite/docs/changelog/next-release.md— "What's new?" entries for PowerShell and PuTTY DPI/font scaling; "Bug Fixes" entries for the initial embedded window size fix in PowerShell, PuTTY, and TigerVNCCopilot generated summary
Copilot summary
This PR fixes font scaling for PuTTY and PowerShell sessions embedded via
SetParent()into aWindowsFormsHostpanel when NETworkManager moves between monitors with different DPI scaling factors. It also fixes the initial embedded window size for PuTTY, PowerShell, and TigerVNC on high-DPI monitors.Root cause: Windows never forwards
WM_DPICHANGEDacross process boundaries afterSetParent. The two embedded process types require completely different strategies.PowerShell (conhost-based):
Connect()capturesGetDpiForWindow(_appWin)beforeSetParentto record conhost's initial DPI, then appliesTryRescaleConsoleFont(pid, currentPanelDpi / initialWindowDpi)after embedding if the DPIs differ. On subsequent monitor moves,WindowsFormsHost_DpiChangedcallsTryRescaleConsoleFontwith theNewDpi / OldDpiratio. The Console API (AttachConsole+SetCurrentConsoleFontEx) is a true cross-process interface that requires no window message delivery.PuTTY (GUI process):
Connect()captures the initial window DPI and, if it differs from the panel DPI, callsTrySendDpiChangedMessageto sendWM_DPICHANGED(0x02E0) directly to PuTTY's HWND with the corrected DPI packed intowParam. On subsequent monitor moves,WindowsFormsHost_DpiChangedcalls the same helper.Initial sizing fix:
WindowHost.Height/Widthare now set in physical pixels usingVisualTreeHelper.GetDpiscale factors at load time for PuTTY, PowerShell, and TigerVNC, preventing the embedded window from being undersized on high-DPI monitors.NativeMethods cleanup: Removed unused constants, enum, and P/Invoke (
SWP_SHOWWINDOW,WS_CHILD,MONITOR_DEFAULTTONEAREST,HWND_BOTTOM,DPI_HOSTING_BEHAVIOR,SetThreadDpiHostingBehavior) that were leftover scaffolding from earlier approaches.To-Do
Contributing
By submitting this pull request, I confirm the following:
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.