Win32检测窗口是否最大化/停靠到半屏(Win-key + Left / Right)

时间:2017-12-19 14:01:59

标签: c++ winapi

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

问题的背景是窗口只有网格步长,比方说32像素。在全屏幕中,程序检测到该状态,允许大小与全屏匹配并填充多余空间。对于Windows 8及更高版本,我想做同样的事情而不是当前离开边框(因为大小会捕捉到32像素的倍数)。

3 个答案:

答案 0 :(得分:3)

使用函数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%的时间内都不可靠。即使窗口停靠在显示器侧面,普通矩形和当前窗口矩形也很有可能匹配。

答案 1 :(得分:2)

除了IInspectable已经提到过的内容之外,还有另一种方法可以确定这些信息并采取相应的行动。

  1. 等待WM_WINDOWPOSCHANGED消息,并从存储在其中的WINDOWPOS指针中读取其xycxcylParam
  2. 通过调用MonitorFromWindow来获取放置窗口的当前监视器的句柄。
  3. 创建MONITORINFO变量并将其cbSize字段设置为sizeof(MONITORINFO)
  4. 使用监视器句柄和MONITORINFO变量的地址来呼叫GetMonitorInfo
  5. 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) - 窗口是"停靠"
  6. 你说你已经有逻辑确定窗口是全屏的(你的意思是全屏还是最大化?),但是如果left == x && top == y && right == x + cx && bottom == y + cy可以确定有效的最大化。

    Here is an MSDN example of something similar.

    请注意,可能需要缓存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 ...
    
      }
    }
    

答案 2 :(得分:1)

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

您无法可靠地确定窗口是否停靠在屏幕的左侧或右侧。壳牌公司没有发送任何特定信息,窗户的相对于工作区域的大小和位置也没有足够的财产。

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