From 83b4fc23cb09618de44026989c72abd36bc360ab Mon Sep 17 00:00:00 2001 From: amail80 <261782908+amail80@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:44:22 +0500 Subject: [PATCH] Show Secure Desktop dialogs on the monitor of their parent window (#1912) * Show Secure Desktop dialogs on the monitor of their parent window Dialogs shown through SecureDesktopDialogBoxParam (the password prompt, mount options and the security token PIN prompt) are created on the Secure Desktop without an owner, because their parent window stays on the original desktop. Without an owner, DS_CENTER puts them on the primary monitor even when the main window is on another one, so the password is typed on one monitor and the wait dialog then appears on the other. Before switching desktops, take the middle of the work area of the monitor that Windows associates with the parent window, and create a hidden owner window at that point on the Secure Desktop. The DS_CENTER dialog is then centered on the same monitor as without Secure Desktop, and the dialogs opened from it follow it. If the parent window is hidden or minimized, or the owner can't be created, the dialog has no owner as before. The monitor lookup added to ShowWaitDialogEx in #1865 moves into a helper used by both. * Windows: read signed coordinates in the Keyfiles context menu handlers The WM_CONTEXTMENU handlers of the password dialog, the mount options dialog and the VeraCrypt Expander password dialog took the click position with LOWORD and HIWORD and compared it with the signed rectangle from GetWindowRect. On a monitor to the left of or above the primary one the screen coordinates are negative, LOWORD and HIWORD return them as large positive values, and a right click on the Keyfiles button was ignored. Use GET_X_LPARAM and GET_Y_LPARAM, as the other WM_CONTEXTMENU handlers in Mount.c already do. Now that the Secure Desktop prompt opens on the monitor of its parent window, this case is easier to reach. --- src/Common/Dlgcode.c | 76 ++++++++++++++++++++++++++++-------- src/ExpandVolume/WinMain.cpp | 4 +- src/Mount/Mount.c | 8 ++-- 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c index 537ba575..15137e38 100644 --- a/src/Common/Dlgcode.c +++ b/src/Common/Dlgcode.c @@ -9103,6 +9103,29 @@ void BringToForeground (HWND hWnd) #endif } +/* Gets the middle of the work area of the monitor that Windows associates with + hwnd. Returns FALSE and leaves *pt unchanged when hwnd is NULL, hidden or + minimized, or when the monitor information is not available. */ +static BOOL GetWindowMonitorWorkAreaCenter (HWND hwnd, POINT *pt) +{ + if (hwnd && IsWindowVisible (hwnd) && !IsIconic (hwnd)) + { + HMONITOR hMonitor = MonitorFromWindow (hwnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO monitorInfo; + + memset (&monitorInfo, 0, sizeof (monitorInfo)); + monitorInfo.cbSize = sizeof (monitorInfo); + if (hMonitor && GetMonitorInfoW (hMonitor, &monitorInfo)) + { + pt->x = monitorInfo.rcWork.left + (monitorInfo.rcWork.right - monitorInfo.rcWork.left) / 2; + pt->y = monitorInfo.rcWork.top + (monitorInfo.rcWork.bottom - monitorInfo.rcWork.top) / 2; + return TRUE; + } + } + + return FALSE; +} + static LRESULT CALLBACK ShowWaitDialogParentWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return DefWindowProcW (hWnd, message, wParam, lParam); @@ -9145,7 +9168,7 @@ void ShowWaitDialogEx(HWND hwnd, BOOL bUseHwndAsParent, WaitThreadProc callback, { /* create invisible window and use it as parent */ WNDCLASSEXW winClass; - int parentX = 0, parentY = 0; + POINT parentPos = {0, 0}; memset (&winClass, 0, sizeof (winClass)); winClass.cbSize = sizeof (WNDCLASSEX); @@ -9160,21 +9183,9 @@ void ShowWaitDialogEx(HWND hwnd, BOOL bUseHwndAsParent, WaitThreadProc callback, the primary one. The parent is kept well inside the monitor because Windows may make it larger than the requested 1x1 size. Falls back to (0, 0) when the creator window is unavailable, hidden or minimized. */ - if (creatorWnd && IsWindowVisible (creatorWnd) && !IsIconic (creatorWnd)) - { - HMONITOR hMonitor = MonitorFromWindow (creatorWnd, MONITOR_DEFAULTTONEAREST); - MONITORINFO monitorInfo; + GetWindowMonitorWorkAreaCenter (creatorWnd, &parentPos); - memset (&monitorInfo, 0, sizeof (monitorInfo)); - monitorInfo.cbSize = sizeof (monitorInfo); - if (hMonitor && GetMonitorInfoW (hMonitor, &monitorInfo)) - { - parentX = monitorInfo.rcWork.left + (monitorInfo.rcWork.right - monitorInfo.rcWork.left) / 2; - parentY = monitorInfo.rcWork.top + (monitorInfo.rcWork.bottom - monitorInfo.rcWork.top) / 2; - } - } - - hParent = CreateWindowExW (WS_EX_TOOLWINDOW | WS_EX_LAYERED, className, L"VeraCrypt ShowWaitDialog Parent", 0, parentX, parentY, 1, 1, NULL, NULL, hInst, NULL); + hParent = CreateWindowExW (WS_EX_TOOLWINDOW | WS_EX_LAYERED, className, L"VeraCrypt ShowWaitDialog Parent", 0, parentPos.x, parentPos.y, 1, 1, NULL, NULL, hInst, NULL); if (hParent) { SetLayeredWindowAttributes (hParent, 0, 1, LWA_ALPHA); @@ -14590,6 +14601,8 @@ typedef struct INT_PTR retValue; BOOL bDlgDisplayed; // set to TRUE if the dialog was displayed on secure desktop BOOL bEnableIMEInSecureDesktop; + BOOL bUseOwnerPos; // set to TRUE if ownerPos is valid: the dialog then gets a hidden owner there + POINT ownerPos; } SecureDesktopThreadParam; typedef struct @@ -14658,6 +14671,8 @@ static unsigned int __stdcall SecureDesktopThread( LPVOID lpThreadParameter ) BOOL bNewDesktopSet = FALSE; HDESK hSecureDesk; DWORD desktopAccess = DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS; + const wchar_t *ownerClassName = L"VeraCryptSecureDesktopOwner"; + HWND hOwner = NULL; hSecureDesk = CreateDesktop (pParam->szDesktopName, NULL, NULL, 0, desktopAccess, NULL); if (!hSecureDesk) @@ -14711,8 +14726,33 @@ static unsigned int __stdcall SecureDesktopThread( LPVOID lpThreadParameter ) hMonitoringThread = (HANDLE) _beginthreadex (NULL, 0, SecureDesktopMonitoringThread, (LPVOID) &monitorParam, 0, &monitoringThreadID); } + // the parent window stays on the original desktop and can't own the dialog, + // so the dialog gets a hidden owner on the monitor of the parent window: + // the DS_CENTER dialog is then centered on that monitor instead of the primary one + if (pParam->bUseOwnerPos) + { + WNDCLASSEXW winClass; + + memset (&winClass, 0, sizeof (winClass)); + winClass.cbSize = sizeof (winClass); + winClass.lpfnWndProc = DefWindowProcW; + winClass.hInstance = hInst; + winClass.lpszClassName = ownerClassName; + RegisterClassExW (&winClass); + + hOwner = CreateWindowExW (WS_EX_TOOLWINDOW, ownerClassName, L"VeraCrypt Secure Desktop Owner", WS_POPUP, + pParam->ownerPos.x, pParam->ownerPos.y, 1, 1, NULL, NULL, hInst, NULL); + } + pParam->retValue = DialogBoxParamW (pParam->hInstance, pParam->lpTemplateName, - NULL, pParam->lpDialogFunc, pParam->dwInitParam); + hOwner, pParam->lpDialogFunc, pParam->dwInitParam); + + if (pParam->bUseOwnerPos) + { + if (hOwner) + DestroyWindow (hOwner); + UnregisterClassW (ownerClassName, hInst); + } if (hMonitoringThread) { @@ -14844,6 +14884,10 @@ INT_PTR SecureDesktopDialogBoxParam( param.retValue = 0; param.bDlgDisplayed = FALSE; param.bEnableIMEInSecureDesktop = bEffectiveEnableIMEInSecureDesktop; + // remember where the parent window is, so that the dialog appears on the same monitor + param.ownerPos.x = 0; + param.ownerPos.y = 0; + param.bUseOwnerPos = GetWindowMonitorWorkAreaCenter (hWndParent, ¶m.ownerPos); // use _beginthreadex instead of CreateThread because lpDialogFunc may be using the C runtime library HANDLE hThread = (HANDLE) _beginthreadex (NULL, 0, SecureDesktopThread, (LPVOID) ¶m, 0, NULL); diff --git a/src/ExpandVolume/WinMain.cpp b/src/ExpandVolume/WinMain.cpp index 80c35039..a1ad391b 100644 --- a/src/ExpandVolume/WinMain.cpp +++ b/src/ExpandVolume/WinMain.cpp @@ -741,8 +741,8 @@ BOOL CALLBACK ExtcvPasswordDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARA RECT buttonRect; GetWindowRect (GetDlgItem (hwndDlg, IDC_KEY_FILES), &buttonRect); - if (LOWORD (lParam) >= buttonRect.left && LOWORD (lParam) <= buttonRect.right - && HIWORD (lParam) >= buttonRect.top && HIWORD (lParam) <= buttonRect.bottom) + if (GET_X_LPARAM (lParam) >= buttonRect.left && GET_X_LPARAM (lParam) <= buttonRect.right + && GET_Y_LPARAM (lParam) >= buttonRect.top && GET_Y_LPARAM (lParam) <= buttonRect.bottom) { // The "Keyfiles" button has been right-clicked diff --git a/src/Mount/Mount.c b/src/Mount/Mount.c index 8d72805d..d27a696f 100644 --- a/src/Mount/Mount.c +++ b/src/Mount/Mount.c @@ -3961,8 +3961,8 @@ BOOL CALLBACK PasswordDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lPa RECT buttonRect; GetWindowRect (GetDlgItem (hwndDlg, IDC_KEY_FILES), &buttonRect); - if (LOWORD (lParam) >= buttonRect.left && LOWORD (lParam) <= buttonRect.right - && HIWORD (lParam) >= buttonRect.top && HIWORD (lParam) <= buttonRect.bottom) + if (GET_X_LPARAM (lParam) >= buttonRect.left && GET_X_LPARAM (lParam) <= buttonRect.right + && GET_Y_LPARAM (lParam) >= buttonRect.top && GET_Y_LPARAM (lParam) <= buttonRect.bottom) { // The "Keyfiles" button has been right-clicked @@ -4530,8 +4530,8 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM GetWindowRect (GetDlgItem (hwndDlg, IDC_KEYFILES_HIDVOL_PROT), &buttonRect); if (IsButtonChecked (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL)) - && LOWORD (lParam) >= buttonRect.left && LOWORD (lParam) <= buttonRect.right - && HIWORD (lParam) >= buttonRect.top && HIWORD (lParam) <= buttonRect.bottom) + && GET_X_LPARAM (lParam) >= buttonRect.left && GET_X_LPARAM (lParam) <= buttonRect.right + && GET_Y_LPARAM (lParam) >= buttonRect.top && GET_Y_LPARAM (lParam) <= buttonRect.bottom) { // The "Keyfiles" button has been right-clicked