338 lines
9.3 KiB
C++
338 lines
9.3 KiB
C++
|
/*++
|
||
|
|
||
|
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
|