C++ Win32检测窗口是否最大化/停靠到半屏幕(Win键左/右)
我有一个经典的Win32 API(C++)应用程序,需要检测窗口是否停靠在屏幕的左/右半部分 问题的背景是,窗口的大小仅以网格步长为单位,比如说32像素。在全屏模式下,程序检测到该状态,允许大小与全屏匹配,并填充多余的空间。在Windows 8和更高版本中,我也希望这样做,而不是当前保留边框(因为大小捕捉到32像素的倍数)。Aero捕捉功能内置于外壳中,而不是窗口管理器。因此,没有特定的窗口样式或标志指示停靠状态。Shell只是根据某些操作重新定位窗口(并在内部记录状态)。这样做的方式与用鼠标或键盘手动重新定位窗口是无法区分的 您无法可靠地确定窗口是停靠在屏幕的左侧还是右侧。外壳没有发送特定的消息,窗口相对于工作区域的大小和位置也不是一个足够的属性C++ Win32检测窗口是否最大化/停靠到半屏幕(Win键左/右),c++,winapi,C++,Winapi,我有一个经典的Win32 API(C++)应用程序,需要检测窗口是否停靠在屏幕的左/右半部分 问题的背景是,窗口的大小仅以网格步长为单位,比如说32像素。在全屏模式下,程序检测到该状态,允许大小与全屏匹配,并填充多余的空间。在Windows 8和更高版本中,我也希望这样做,而不是当前保留边框(因为大小捕捉到32像素的倍数)。Aero捕捉功能内置于外壳中,而不是窗口管理器。因此,没有特定的窗口样式或标志指示停靠状态。Shell只是根据某些操作重新定位窗口(并在内部记录状态)。这样做的方式与用鼠标或
你想要完成的是不可能的。您必须实现一个解决方案,它不需要不可用的信息。一个这样的实现是始终对窗口大小使用填充,这不允许使用整个客户端区域。另一个解决方案是实现相反的方法:允许窗口大小调整为任意大小,除非您知道用户正在手动调整窗口大小。您可以通过处理消息来确定后者。除了IInspectable已经提到的内容之外,还有另一种方法来确定此信息并相应地采取行动
lParam
中的指针读取其x
、y
、cx
和cy
值cbSize
字段设置为sizeof(MONITORINFO)
MONITORINFO
变量的地址来调用李>
MONITORINFO
变量中读取rcWork
值。
-窗口“停靠”在左侧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 ...
}
}
使用函数
GetWindowPlacement()
可以使用WINDOWPLACEMENT
的成员rcNormalPosition
检索正常窗口矩形。然后将普通矩形与实际窗口矩形进行比较。如果它们不匹配,则窗口很可能处于停靠状态
例如:
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%可靠。即使窗口停靠在显示器侧面,普通矩形和当前窗口矩形也很可能匹配。窗口在这样“停靠”时没有特殊状态。shell只是为用户提供了一个快捷方式,可以将窗口移动到那里,这与使用鼠标将窗口移动到那里没有什么不同。使用GetWindowRect和GetMonitorInfo检测它。此算法无法区分使用Aero Snap停靠的窗口和大小和位置相同但由用户手动完成的窗口。处理
WM_size
是区分用户调整窗口大小和系统调整窗口大小的关键。@IInspectable:您完全正确。我写这个答案的想法是,如果用户将窗口调整到精确的“停靠”尺寸,或者使用Aero Snap,因为它实际上是相同的,那么这并不重要,但是调整大小的方法实际上可能会影响可用性。关于这一点,我将添加另一个建议。WM_ENTERSIZEMOVE
会在用户启动移动操作时发送到窗口,这可能会导致停靠窗口。这将导致假阴性。事实上,情况更糟WM_WINDOWPOSCHANGED
在整个尺寸调整和移动操作中发送。建议的实现只产生假阴性。我没有尝试过,但我相信可靠的实现会在WM\u size
中设置标志,并在WM\u EXITSIZEMOVE
期间重置它。在调整停靠窗口的未停靠边的大小时,会出现角点情况。我甚至不确定,在这种情况下会发生什么。我目前还不能测试它,但是是的,我忘记了在移动/大小期间调用WM\u WINDOWPOSCHANGED
——这就是WinForms处理实时控件布局的方式。问题是,我不知道WM_EXITSIZEMOVE
是在WM_WINDOWPOSCHANGED
之前还是之后发送的,没有testi