我有一个经典的 Win32-API (C++) 应用程序,需要检测窗口是否停靠在屏幕的左/右半部分。
问题的背景是窗口的大小仅以网格为单位,假设为 32 像素。在全屏模式下,程序会检测到该状态,允许大小与全屏匹配并填充多余的空间。对于 Windows 8 及更高版本,我希望执行相同的操作,而不是当前保留边框(因为大小会对齐到 32 像素的倍数)。
通过函数
GetWindowPlacement()
,您可以使用rcNormalPosition
的成员WINDOWPLACEMENT
来检索正常的窗口矩形。然后将正常矩形与实际窗口矩形进行比较。如果它们不匹配,则窗口很可能处于停靠状态。
示例:
bool IsDockedToMonitor(HWND hWnd)
{
WINDOWPLACEMENT placement = {sizeof(WINDOWPLACEMENT)};
GetWindowPlacement(hWnd, &placement);
RECT rc;
GetWindowRect(hWnd, &rc);
return placement.showCmd == SW_SHOWNORMAL
&& (rc.left != placement.rcNormalPosition.left ||
rc.top != placement.rcNormalPosition.top ||
rc.right != placement.rcNormalPosition.right ||
rc.bottom != placement.rcNormalPosition.bottom);
}
请注意,此解决方案并非 100% 可靠。即使窗口停靠在显示器的一侧,正常矩形和当前窗口矩形匹配的可能性也很小。
Aero Snap 功能内置于 Shell 中,而不是窗口管理器中。因此,没有特定的窗口样式或标志来指示停靠状态。 Shell 只是重新定位窗口以响应某些操作(并在内部记录状态)。它的执行方式与使用鼠标或键盘手动重新定位窗口没有什么区别。
您无法可靠地确定窗口是停靠在屏幕的左侧还是右侧。 Shell 没有发送任何特定消息,窗口的大小和相对于工作区域的位置也不是足够的属性。你想要完成的事情是不可能的。您必须实施一个不需要不可用信息的解决方案。一种这样的实现是始终对窗口大小使用填充,这不允许使用整个客户区域。另一种解决方案是实施相反的方法:允许将窗口大小调整为任意大小,除非您知道用户正在手动调整窗口大小。您可以通过处理
WM_SIZING 消息来确定后者。
WM_WINDOWPOSCHANGED
消息并从存储在 x
中的
y
指针读取其
cx
、
cy
、WINDOWPOS
和 lParam
值。
MonitorFromWindow
获取放置窗口的当前监视器的句柄。
MONITORINFO
变量并将其 cbSize
字段设置为
sizeof(MONITORINFO)
。
MONITORINFO
变量的地址来调用
GetMonitorInfo
。
rcWork
变量中读取
MONITORINFO
值。
rcWork.top == WINDOWPOS.y && rcWork.bottom == (WINDOWPOS.y + WINDOWPOS.cx) && rcWork.left == WINDOWPOS.x
- 窗口“停靠”在左侧
rcWork.top == WINDOWPOS.y && rcWork.bottom == (WINDOWPOS.y + WINDOWPOS.cx) && rcwork.right == (WINDOWPOS.x + WINDOWPOS.cx)
- 窗口“停靠”在右侧
rcWork.top == WINDOWPOS.y && rcWork.left == WINDOWPOS.x && rcWork.right == (WINDOWPOS.x + WINDOWPOS.cx)
- 窗口“停靠”到顶部
rcWork.top == (WINDOWPOS.y + WINDOWPOS.cy) && rcWork.left == WINDOWPOS.x && rcWork.right == (WINDOWPOS.x + WINDOWPOS.cx)
- 窗口“停靠”到底部
left == x && top == y && right == x + cx && bottom == y + cy
。请注意,可能更需要缓存
MONITORINFO
值,这样您就不需要每次重新定位窗口时都调用它。
LRESULT CALLBACK windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static bool userSizing = false;
switch (msg)
{
// could also catch WM_ENTERSIZEMOVE here, but this will trigger on
// moves as well as sizes
case WM_SIZING:
userSizing = true;
break;
case WM_EXITSIZEMOVE:
userSizing = false;
break;
case WM_WINDOWPOSCHANGED:
if (userSizing)
{
break;
}
// do logic to check to see if the window is sized in a "docked"
// manner here
break;
// handle other window messages ...
}
}