/*++ Copyright (c) 2001 Microsoft Corporation Module Name: KeepWindowOnMonitor.cpp Abstract: Do not allow a window to be placed off the Monitor. History: 04/24/2001 robkenny Created 09/10/2001 robkenny Made shim more generic. --*/ #include "precomp.h" IMPLEMENT_SHIM_BEGIN(KeepWindowOnMonitor) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY(SetWindowPos) APIHOOK_ENUM_ENTRY(MoveWindow) APIHOOK_ENUM_ENTRY(CreateWindowA) APIHOOK_ENUM_ENTRY(CreateWindowExA) APIHOOK_ENUM_END /*++ Are these two RECTs equal --*/ BOOL operator == (const RECT & rc1, const RECT & rc2) { return rc1.left == rc2.left && rc1.right == rc2.right && rc1.top == rc2.top && rc1.bottom == rc2.bottom; } /*++ Are these two RECTs different --*/ BOOL operator != (const RECT & rc1, const RECT & rc2) { return ! (rc1 == rc2); } /*++ Is rcWindow entirely visible on rcMonitor --*/ BOOL EntirelyVisible(const RECT & rcWindow, const RECT & rcMonitor) { return rcWindow.left >= rcMonitor.left && rcWindow.right <= rcMonitor.right && rcWindow.top >= rcMonitor.top && rcWindow.bottom <= rcMonitor.bottom; } #define MONITOR_CENTER 0x0001 // center rect to monitor #define MONITOR_CLIP 0x0000 // clip rect to monitor #define MONITOR_WORKAREA 0x0002 // use monitor work area #define MONITOR_AREA 0x0000 // use monitor entire area // // ClipOrCenterRectToMonitor // // The most common problem apps have when running on a // multimonitor system is that they "clip" or "pin" windows // based on the SM_CXSCREEN and SM_CYSCREEN system metrics. // Because of app compatibility reasons these system metrics // return the size of the primary monitor. // // This shows how you use the new Win32 multimonitor APIs // to do the same thing. // BOOL ClipOrCenterRectToMonitor( LPRECT prcWindowPos, UINT flags) { HMONITOR hMonitor; MONITORINFO mi; RECT rcMonitorRect; int w = prcWindowPos->right - prcWindowPos->left; int h = prcWindowPos->bottom - prcWindowPos->top; // // get the nearest monitor to the passed rect. // hMonitor = MonitorFromRect(prcWindowPos, MONITOR_DEFAULTTONEAREST); // // get the work area or entire monitor rect. // mi.cbSize = sizeof(mi); if ( !GetMonitorInfo(hMonitor, &mi) ) { return FALSE; } if (flags & MONITOR_WORKAREA) rcMonitorRect = mi.rcWork; else rcMonitorRect = mi.rcMonitor; // We only want to move the window if it is not entirely visible. if (EntirelyVisible(*prcWindowPos, rcMonitorRect)) { return FALSE; } // // center or clip the passed rect to the monitor rect // if (flags & MONITOR_CENTER) { prcWindowPos->left = rcMonitorRect.left + (rcMonitorRect.right - rcMonitorRect.left - w) / 2; prcWindowPos->top = rcMonitorRect.top + (rcMonitorRect.bottom - rcMonitorRect.top - h) / 2; prcWindowPos->right = prcWindowPos->left + w; prcWindowPos->bottom = prcWindowPos->top + h; } else { prcWindowPos->left = max(rcMonitorRect.left, min(rcMonitorRect.right-w, prcWindowPos->left)); prcWindowPos->top = max(rcMonitorRect.top, min(rcMonitorRect.bottom-h, prcWindowPos->top)); prcWindowPos->right = prcWindowPos->left + w; prcWindowPos->bottom = prcWindowPos->top + h; } return TRUE; } /*++ If hwnd is not entirely visible on a single monitor, move/resize the window as necessary. --*/ void ClipOrCenterWindowToMonitor( HWND hwnd, HWND hWndParent, UINT flags, const char * API) { // We only want to forcibly move top-level windows if (hWndParent == NULL || hWndParent == GetDesktopWindow()) { // Grab the current position of the window RECT rcWindowPos; if ( GetWindowRect(hwnd, &rcWindowPos) ) { RECT rcOrigWindowPos = rcWindowPos; // Calculate the new position of the window, based on flags if ( ClipOrCenterRectToMonitor(&rcWindowPos, flags) ) { if (rcWindowPos != rcOrigWindowPos) { DPFN( eDbgLevelInfo, "[%s] HWnd(0x08x) OrigWindowRect (%d, %d) x (%d, %d) moved to (%d, %d) x (%d, %d)\n", API, hwnd, rcOrigWindowPos.left, rcOrigWindowPos.top, rcOrigWindowPos.right, rcOrigWindowPos.bottom, rcWindowPos.left, rcWindowPos.top, rcWindowPos.right, rcWindowPos.bottom); SetWindowPos(hwnd, NULL, rcWindowPos.left, rcWindowPos.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } } } } } /*++ Call SetWindowPos, but if the window is not entirely visible, the window will be centered on the nearest monitor. --*/ BOOL APIHOOK(SetWindowPos)( HWND hWnd, // handle to window HWND hWndInsertAfter, // placement-order handle int X, // horizontal position int Y, // vertical position int cx, // width int cy, // height UINT uFlags // window-positioning options ) { BOOL bReturn = ORIGINAL_API(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); ClipOrCenterWindowToMonitor(hWnd, GetParent(hWnd), MONITOR_CENTER | MONITOR_WORKAREA, "SetWindowPos"); return bReturn; } /*++ Call MoveWindow, but if the window is not entirely visible, the window will be centered on the nearest monitor. --*/ BOOL APIHOOK(MoveWindow)( HWND hWnd, // handle to window int X, // horizontal position int Y, // vertical position int nWidth, // width int nHeight, // height BOOL bRepaint // repaint option ) { BOOL bReturn = ORIGINAL_API(MoveWindow)(hWnd, X, Y, nWidth, nHeight, bRepaint); ClipOrCenterWindowToMonitor(hWnd, GetParent(hWnd), MONITOR_CENTER | MONITOR_WORKAREA, "MoveWindow"); return bReturn; } /*++ Call CreateWindowA, but if the window is not entirely visible, the window will be centered on the nearest monitor. --*/ HWND APIHOOK(CreateWindowA)( LPCSTR lpClassName, // registered class name LPCSTR lpWindowName, // window name DWORD dwStyle, // window style int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width int nHeight, // window height HWND hWndParent, // handle to parent or owner window HMENU hMenu, // menu handle or child identifier HINSTANCE hInstance, // handle to application instance LPVOID lpParam // window-creation data ) { HWND hWnd = ORIGINAL_API(CreateWindowA)(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); if (hWnd) { ClipOrCenterWindowToMonitor(hWnd, hWndParent, MONITOR_CENTER | MONITOR_WORKAREA, "CreateWindowA"); } return hWnd; } /*++ Call CreateWindowExA, but if the window is not entirely visible, the window will be centered on the nearest monitor. --*/ HWND APIHOOK(CreateWindowExA)( DWORD dwExStyle, // extended window style LPCSTR lpClassName, // registered class name LPCSTR lpWindowName, // window name DWORD dwStyle, // window style int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width int nHeight, // window height HWND hWndParent, // handle to parent or owner window HMENU hMenu, // menu handle or child identifier HINSTANCE hInstance, // handle to application instance LPVOID lpParam // window-creation data ) { HWND hWnd = ORIGINAL_API(CreateWindowExA)(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); if (hWnd) { ClipOrCenterWindowToMonitor(hWnd, hWndParent, MONITOR_CENTER | MONITOR_WORKAREA, "CreateWindowExA"); } return hWnd; } /*++ Register hooked functions --*/ HOOK_BEGIN APIHOOK_ENTRY(USER32.DLL, SetWindowPos) APIHOOK_ENTRY(USER32.DLL, MoveWindow) APIHOOK_ENTRY(USER32.DLL, CreateWindowA) APIHOOK_ENTRY(USER32.DLL, CreateWindowExA) HOOK_END IMPLEMENT_SHIM_END