First, thank you for #5689 — it successfully resolved the os.Exit(1) crash from #5650, and we appreciate the quick fix.
We'd like to share follow-up findings from testing #5689 on real mixed-DPI hardware. The crash is gone, but a visual artifact remains: after dragging a window across a mixed-DPI monitor boundary (scale-up direction, e.g. 100% → 150%), WebView2 content shrinks then disappears permanently until app restart.
We've done extensive diagnosis and believe the root cause is deeper than the missing Resize() that #5689 addressed. We'd like to share what we found and ask whether exposing ShouldDetectMonitorScaleChanges as a per-window option might be a feasible direction.
To Reproduce
- Wails v3 app on Windows, two monitors with different DPI (primary 1920×1080 @ 100%, secondary 1920×1080 @ 150%)
- Drag window from 100% monitor to 150% monitor
- Window freezes ~3 seconds, content briefly appears shrunk, then disappears entirely
- Only the window background colour remains; app must be restarted
- Reverse direction (150% → 100%) works fine
What we observed
We added debug logging to resyncWebviewRasterizationScale and the WM_DPICHANGED handler. On scale-up (100% → 150%):
WM_DPICHANGED enter dpi=144
resync: dpiX=144 computedScale=1.5000 currentScale=1.0000 ← controller alive
resync: PutRasterizationScale(1.5000) succeeded ← COM returns S_OK
resync returned true
chromium.Resize() called ← #5689 fix executes
WM_DPICHANGED exit
# 3 seconds later:
resync: dpiX=144 computedScale=1.5000 currentScale=0.0000 ← unexpected: scale is 0
resync: PutRasterizationScale(1.5000) succeeded ← COM S_OK but no effect
[WebView2] Eval failed: ERROR_INVALID_STATE (continuous 10+ seconds)
[WebView2] SetSize failed: ERROR_INVALID_STATE
The currentScale=0.0000 value appears to indicate the render process has terminated and failed to recreate. After that point, all COM calls (PutRasterizationScale, PutBounds, Resize, even Navigate) return S_OK but have no effect — the COM proxy seems to be talking to a dead render process.
Scale-down (150% → 100%) does not exhibit this: currentScale stays at valid values throughout.
What we tried
We explored several recovery approaches in the WM_DPICHANGED handler, but none could revive the render process once currentScale reached 0:
| Approach |
Outcome |
chromium.Resize() after resync (#5689) |
PutBounds also fails with ERROR_INVALID_STATE |
Unconditional Resize() (ignoring resync gate) |
Same — PutBounds fails |
| Deferred retry with backoff (7 attempts, 500ms–15s) |
currentScale stays 0 across all retries |
NotifyParentWindowPositionChanged() |
No effect |
Navigate() to force render process recreation |
No effect — COM proxy appears non-functional |
Our conclusion: once the render process terminates on scale-up, application-level COM calls cannot recover it. The issue needs to be prevented rather than recovered from.
What resolved it for us
We noticed PutShouldDetectMonitorScaleChanges(false) is set in chromium.go (line ~359), which we understand is intentional for the raw-pixels bounds mode. Out of curiosity, we tried enabling it (true) to let WebView2 handle DPI transitions natively, and it resolved the issue completely:
controller3.PutShouldDetectMonitorScaleChanges(true)
With native DPI detection enabled, the scale-up log looks like this — no currentScale=0, no errors:
WM_DPICHANGED enter dpi=144
resync: dpiX=144 computedScale=1.5000 currentScale=1.0000 ← alive
resync: PutRasterizationScale(1.5000) succeeded
WM_DPICHANGED exit
# No errors, content flashes once and recovers
Both drag directions now work correctly on our hardware.
To be transparent: we're currently running with a local patch
(go.mod replace directive) that enables this flag, which fully
resolves the issue for our use case. We're sharing these findings
in case they're useful — there's no urgency on our end, and we
respect that the maintainers may have considerations we're not
aware of.
A question about direction
We understand the raw-pixels-bounds-mode rationale for disabling native DPI detection by default, and we're not suggesting that default be changed. However, for developers on mixed-DPI multi-monitor setups, this behavior makes the app unusable without a workaround.
Would exposing ShouldDetectMonitorScaleChanges as a WebviewWindowOptions field (e.g. EnableNativeMonitorScaleDetection bool // default false) be a feasible approach? This would:
- Preserve the existing default behavior and performance characteristics
- Add zero regression risk (opt-in only)
- Let affected developers self-resolve without maintaining a fork
We'd be happy to open a PR for this if the direction seems reasonable.
Environment
- Wails: v3.0.0-alpha2.109
- webview2 wrapper:
github.com/wailsapp/wails/webview2@v1.0.27
- OS: Windows 11 (Build 26200)
- Monitors: 27" 1920×1080 @ 100% + 15" 1920×1080 @ 150%, extended display
- Go: 1.26.2
Related issues
First, thank you for #5689 — it successfully resolved the
os.Exit(1)crash from #5650, and we appreciate the quick fix.We'd like to share follow-up findings from testing #5689 on real mixed-DPI hardware. The crash is gone, but a visual artifact remains: after dragging a window across a mixed-DPI monitor boundary (scale-up direction, e.g. 100% → 150%), WebView2 content shrinks then disappears permanently until app restart.
We've done extensive diagnosis and believe the root cause is deeper than the missing
Resize()that #5689 addressed. We'd like to share what we found and ask whether exposingShouldDetectMonitorScaleChangesas a per-window option might be a feasible direction.To Reproduce
What we observed
We added debug logging to
resyncWebviewRasterizationScaleand theWM_DPICHANGEDhandler. On scale-up (100% → 150%):The
currentScale=0.0000value appears to indicate the render process has terminated and failed to recreate. After that point, all COM calls (PutRasterizationScale,PutBounds,Resize, evenNavigate) return S_OK but have no effect — the COM proxy seems to be talking to a dead render process.Scale-down (150% → 100%) does not exhibit this:
currentScalestays at valid values throughout.What we tried
We explored several recovery approaches in the
WM_DPICHANGEDhandler, but none could revive the render process oncecurrentScalereached 0:chromium.Resize()after resync (#5689)PutBoundsalso fails with ERROR_INVALID_STATEResize()(ignoring resync gate)PutBoundsfailscurrentScalestays 0 across all retriesNotifyParentWindowPositionChanged()Navigate()to force render process recreationOur conclusion: once the render process terminates on scale-up, application-level COM calls cannot recover it. The issue needs to be prevented rather than recovered from.
What resolved it for us
We noticed
PutShouldDetectMonitorScaleChanges(false)is set inchromium.go(line ~359), which we understand is intentional for the raw-pixels bounds mode. Out of curiosity, we tried enabling it (true) to let WebView2 handle DPI transitions natively, and it resolved the issue completely:With native DPI detection enabled, the scale-up log looks like this — no
currentScale=0, no errors:Both drag directions now work correctly on our hardware.
To be transparent: we're currently running with a local patch
(
go.modreplace directive) that enables this flag, which fullyresolves the issue for our use case. We're sharing these findings
in case they're useful — there's no urgency on our end, and we
respect that the maintainers may have considerations we're not
aware of.
A question about direction
We understand the raw-pixels-bounds-mode rationale for disabling native DPI detection by default, and we're not suggesting that default be changed. However, for developers on mixed-DPI multi-monitor setups, this behavior makes the app unusable without a workaround.
Would exposing
ShouldDetectMonitorScaleChangesas aWebviewWindowOptionsfield (e.g.EnableNativeMonitorScaleDetection bool // default false) be a feasible approach? This would:We'd be happy to open a PR for this if the direction seems reasonable.
Environment
github.com/wailsapp/wails/webview2@v1.0.27Related issues
chromium.Resize()toWM_DPICHANGEDhandler; resolves the crash but the visual artifact persists on mixed-DPI hardwareResize()works there because the render process stays alive