Win32 检测窗口是否最大化/停靠到半屏(Win 键 + 向左/向右)

问题描述 投票:0回答:4

我有一个经典的 Win32-API (C++) 应用程序,需要检测窗口是否停靠在屏幕的左/右半部分。

问题的背景是窗口的大小仅以网格为单位,假设为 32 像素。在全屏模式下,程序会检测到该状态,允许大小与全屏匹配并填充多余的空间。对于 Windows 8 及更高版本,我希望执行相同的操作,而不是当前保留边框(因为大小会对齐到 32 像素的倍数)。

c++ winapi
4个回答
6
投票

通过函数

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% 可靠。即使窗口停靠在显示器的一侧,正常矩形和当前窗口矩形匹配的可能性也很小。


4
投票

Aero Snap 功能内置于 Shell 中,而不是窗口管理器中。因此,没有特定的窗口样式或标志来指示停靠状态。 Shell 只是重新定位窗口以响应某些操作(并在内部记录状态)。它的执行方式与使用鼠标或键盘手动重新定位窗口没有什么区别。

您无法可靠地确定窗口是停靠在屏幕的左侧还是右侧。 Shell 没有发送任何特定消息,窗口的大小和相对于工作区域的位置也不是足够的属性。

你想要完成的事情是不可能的。您必须实施一个不需要不可用信息的解决方案。一种这样的实现是始终对窗口大小使用填充,这不允许使用整个客户区域。另一种解决方案是实施相反的方法:允许将窗口大小调整为任意大小,除非您知道用户正在手动调整窗口大小。您可以通过处理

WM_SIZING 消息来确定后者。


2
投票
除了 IInspectable 已经提到的之外,还有另一种方法可以确定此信息并采取相应行动。

    等待
  1. WM_WINDOWPOSCHANGED
     消息并从存储在 
    x
     中的 
    y
     指针读取其 
    cx
    cy
    WINDOWPOS
    lParam
     值。
  2. 通过调用
  3. MonitorFromWindow
    获取放置窗口的当前监视器的句柄。
  4. 创建一个
  5. MONITORINFO
     变量并将其 
    cbSize
     字段设置为 
    sizeof(MONITORINFO)
  6. 使用监视器句柄和
  7. MONITORINFO
     变量的地址来调用 
    GetMonitorInfo
  8. 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

这里有一个类似的 MSDN 示例。

请注意,可能更需要缓存

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 ... } }

从 Windows 10 1903 开始,您可以使用 

0
投票
© www.soinside.com 2019 - 2024. All rights reserved.