Skip to content

Windows: bring the main window back on screen when its restore position is off screen - #1858

Open
amail80 wants to merge 1 commit into
veracrypt:masterfrom
amail80:fix/restore-offscreen-window
Open

Windows: bring the main window back on screen when its restore position is off screen#1858
amail80 wants to merge 1 commit into
veracrypt:masterfrom
amail80:fix/restore-offscreen-window

Conversation

@amail80

@amail80 amail80 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Windows can leave the main window in a state where it cannot be shown by any means, and only restarting VeraCrypt recovers it.

What happens

A minimized window is parked by Windows at (-32000,-32000). When the display topology changes while the window is minimized — which happens on every session lock that powers off a monitor — Windows can copy that parking position into the window's restore rectangle and clear WS_MINIMIZE.

The window is then, by every API, a normal visible window that happens to live 32000 pixels off screen. IsIconic returns FALSE, IsWindowVisible returns TRUE, IsHungAppWindow returns FALSE, and the process is fully responsive — the notification area menu works, the message loop runs. But the window is nowhere, and ShowWindow (SW_RESTORE) is a no-op because the restore rectangle is exactly where the window already is.

Evidence

Captured during an occurrence, three snapshots half an hour apart, byte-for-byte identical:

HWND      : 0x13A3C8E
Class     : VeraCryptCustomDlg
Visible   : True
Enabled   : True
Minimized : False          <- WS_MINIMIZE cleared
HungApp   : False
Rect      : (-32000,-32000) - (-31840,-31972)   160x28
Style     : 0x94CA0A4C

The same window before the occurrence, normally minimized:

Minimized : True
Rect      : (-32000,-32000) - (-31840,-31972)
Style     : 0xB4CA0A4C

The styles differ by exactly 0x20000000, that is WS_MINIMIZE. The coordinates are the same.

GetWindowPlacement showed the cause directly:

showCmd          = 1  (SW_SHOWNORMAL)
rcNormalPosition = (-32000,-32000) - (-31840,-31972)

The position the window is supposed to return to points into the parking area. Every normal path therefore restores it there: a notification area click, an explicit SW_RESTORE, and a forced minimize/restore cycle all put the window back in the same place. Only a direct SetWindowPos, which bypasses the stored placement, brought it back.

Not specific to VeraCrypt

The same loss was observed on the same machine with BiglyBT (Java/SWT) and Clash Verge — unrelated applications on unrelated toolkits, sharing only the fact that they were minimized when the topology changed. VeraCrypt cannot fix the cause, only guard against it; there is currently no code in the project that touches the main window position at all.

The change

RepairMainWindowPlacement validates the restore rectangle and re-centers it when it is unusable. It runs from the three paths that show the window, and — because clicking the taskbar button of a visible window runs no application code at all — also on WM_DISPLAYCHANGE, on session unlock, and on SC_RESTORE.

Two conditions trigger a repair: the rectangle intersects no monitor, or it is no larger than a minimized window. The second case matters because Windows may pull a corrupted position back inside the desktop while leaving the parking dimensions behind, which would restore the dialog as a sliver a few pixels high. The comparison uses SM_CXMINIMIZED / SM_CYMINIMIZED rather than the dialog's own size, so a legitimate placement can never trigger it.

showCmd is left as GetWindowPlacement returned it, so a legitimately minimized window stays minimized and only its future restore position is corrected. SetWindowPlacement is used rather than SetWindowPos so the correction also survives the next minimize/restore cycle.

The window size is recorded at the end of InitMainDialog, after the large-font compensation that may resize the dialog, so all three callers — including the language switch — refresh it. A parked window carries the parking size and is skipped.

rcNormalPosition is in workspace coordinates, offset by the origin of the primary monitor's work area; the correction accounts for that with SPI_GETWORKAREA. On a default taskbar layout the offset is zero, but it is not with an appbar docked to the top or the left.

When the placement is valid the whole thing costs one MonitorFromRect call on a path that only runs when the user asks for the window, and changes nothing.

Testing

Built on Windows 11 26200.8875, MSVC, /t:Mount /p:Configuration=Debug /p:Platform=x64. Two monitors at different scale factors, the primary one being the external display that the session lock powers off.

The corrupted state can be produced on demand with SetWindowPlacement, writing the parking rectangle into rcNormalPosition and then cycling SW_MINIMIZE/SW_RESTORE so it settles. Verified that before the change no path shows the window, and after it the window returns centered.

Verified unchanged behaviour for: a normally minimized window restored from the taskbar and from the notification area, a window shown and hidden through the notification area menu, and a resolution change with a valid placement (no repair triggered).

…on is off screen

A minimized window is parked by Windows at (-32000,-32000). When the display
topology changes while it is minimized - which happens on every session lock
that powers off a monitor - Windows can copy that parking position into the
window's restore rectangle and clear WS_MINIMIZE. The window then is, by every
API, a normal visible window living 32000 pixels off screen: IsIconic returns
FALSE, IsWindowVisible returns TRUE, and ShowWindow (SW_RESTORE) is a no-op
because the restore rectangle is exactly where the window already is.

The user is left with an application that answers its notification area icon
but whose main window cannot be shown by any means, and only restarting
VeraCrypt recovers it.

Validate the restore rectangle and re-center it on the primary monitor when it
intersects none. The repair runs from the three paths that show the window and,
because clicking the taskbar button of an already visible window runs no
application code at all, also on WM_DISPLAYCHANGE. showCmd is left as
GetWindowPlacement returned it, so a legitimately minimized window stays
minimized and only its future restore position is corrected.

The window size is captured once after the dialog has been laid out: a parked
window carries the parking size, so it cannot serve as a source for the normal
dimensions.

Verified on Windows 11 26200.8875 with two monitors at different scale factors,
the primary one being powered off by the session lock: GetWindowPlacement
returned rcNormalPosition = (-32000,-32000)-(-31840,-31972), and neither a
notification area click nor an explicit minimize/restore cycle brought the
window back.
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.

1 participant