给定一个hwnd,确定该窗口是否未被其他窗口隐藏(Z-Ordering)

时间:2015-12-01 17:00:28

标签: .net vb.net winapi window z-order

我有一个NativeWindow,我已覆盖WndProc功能,以便在移动我的窗口时处理 WM_WINDOWPOSCHANGING 消息我将会这样做将其粘贴到桌面上最近窗口的边框。

我遇到的问题是我的窗口停靠在其他顶窗背景下的窗口,例如,如果我打开一个资源管理器窗口,而另一个窗口位于资源管理器窗口下方,那么我的窗口可以停靠到窗口低于另一个窗口,这是一个低于资源管理器窗口的z顺序级别。我想避免这种情况。

问题的演示:

enter image description here

在上面的GIF中,有我的窗口(Form1),Visual Studio IDE窗口,资源管理器窗口以及名称为" Hot Corners"的应用程序窗口。当我发送" Hot Corners"窗口到背景,我仍然可以把我的窗户贴在"热点角落"边界。我想避免这种情况。

请注意Visual Studio中捕获的输出窗口中的调试信息。

我在 Wikipedia 上阅读了Z-Ordering,我还看到了 this 示例和 MSDN 文档 here here ,但是,我仍然不明白如何实现这一目标。

当我尝试将我的窗口粘贴到其他窗口时,我需要确定该目标窗口是否低于其他窗口或不是,以避免我解释的问题。

我希望我能解释好问题,并清楚我需要什么,在我窗口上方的GIF中不应该遵守" Hot Corners"窗口因为不可见,因为资源管理器窗口位于上方。

这是相关的代码,该方法将我的窗口(Form)作为参数,是过滤{{{}时获得的 WINDOWPOS 结构的句柄1}}我的窗口的WM_WINDOWPOSCHANGING过程中的消息,最后一个参数WndProc是我的窗口边界到其他窗口之间所需的最小空间。

threshold

请注意,在上面的代码中我只显示了将窗口的右边框粘贴到其他窗口的威胁,这是为了避免增加此问题的代码,以及缺少P / Invokes的相同原因

3 个答案:

答案 0 :(得分:2)

使用GetWindowPlacement来避免检查最小化窗口:

Public Class YourUtilityClass

    Private Declare Function GetWindowPlacement Lib "user32" (ByVal hwnd As IntPtr, ByRef lpwndpl As WINDOWPLACEMENT) As Integer

    Private Structure WINDOWPLACEMENT
        Public Length As Integer
        Public flags As Integer
        Public showCmd As Integer
        Public ptMinPosition As POINTAPI
        Public ptMaxPosition As POINTAPI
        Public rcNormalPosition As RECT
    End Structure

    Private Structure POINTAPI
        Public x As Integer
        Public y As Integer
    End Structure

    Private Structure RECT
        Public Left As Integer
        Public Top As Integer
        Public Right As Integer
        Public Bottom As Integer
    End Structure

    Enum Placements
        Normal = 1
        Minimized = 2
        Maximized = 3
    End Enum


    Shared Function GetPlacement(ByVal hwnd As IntPtr) As Placements

        Dim wpTemp As WINDOWPLACEMENT

        wpTemp.Length = System.Runtime.InteropServices.Marshal.SizeOf(wpTemp)
        Return CType(GetWindowPlacement(hwnd, wpTemp), Placements)

    End Function
End Class

...

For Each hwnd As IntPtr In desktopWindows
   If YourUtilityClass.GetPlacement(hwnd) != YourUtilityClass.Placements.Normal Then
      Continue For
   End If

警告:代码未经过测试。

Api参考: Linkers and Loaders

答案 1 :(得分:2)

给定一个窗口句柄,您应该能够使用一些Win32函数确定窗口是否被其他窗口完全或部分遮挡:

  1. 调用GetWindowDC()以检索包含整个窗口的设备上下文句柄(HDC),包括非客户区域(例如标题栏,菜单,边框等)

  2. 使用上面返回的GetClipBox()致电HDC。这将返回实际可见的最小边界矩形(即,在屏幕上并且未被其他窗口覆盖)。此外,返回值可以告诉您窗口是否完全被遮挡(NULLREGION)。

  3. 请勿忘记致电ReleaseDC()

  4. API参考: https://msdn.microsoft.com/en-us/library/dd144865%28v=vs.85%29.aspx

答案 2 :(得分:2)

您需要做的第一件事(也是最困难的)是找到所有“真正的”非最小化(使用 IsIconic 功能)可见< / em> windows。我的意思是,如果您使用 EnumWindows EnumDesctopWindows ,您将获得一些不需要的窗口,例如: Windows启动图标,任务栏,程序管理器等全部可见未最小化。如果您覆盖此问题,其他一切都非常简单:像在数组中一样存储句柄。顺序是从z到中的自上而下,例如。

arrayHandles [0] - &gt; top,arrayHandles [1] - &gt; one level bellow等...

然后取出矩形并将它们存储在另一个数组中。最后一步是排除抽象的窗口(显然rect0是好的):

检查rect1和rect0是否相交。如果为true,则删除rect1
用rect1和rect0检查rect2 检查rect3与rect2和rect1和rect0
....

最后,您有一系列 clean rects,您可以在移动窗口时检查这些rects。

重要提示:您执行上述所有步骤只需一次,即开始拖动窗口的那一刻,即 WM_ENTERSIZEMOVE 消息

矩形交集的代码(C代码中的rect0和rect1,我太懒了):

HRGN rgn1 = {0, 0, 0, 0}, rgn2 = {0, 0, 0, 0}, rgn3 = {0, 0, 0, 0};

rgn1 = CreateRectRgn(rect0.left, rect0.top, rect0.right, rect0.bottom);

rgn2 = CreateRectRgn(rect1.left, rect1.top, rect1.right, rect1.bottom);

rgn3 = CreateRectRgn(0, 0, 0, 0);

int rslt = CombineRgn(rgn3, rgn1, rgn2, RGN_AND);

if( rslt == ERROR ){
    printf("Error in combineRgn function \n");

    //do something
}

if( rslt != NULLREGION ){
    //They DONT intersect
}

DeleteObject(rgn1);
DeleteObject(rgn2);
DeleteObject(rgn3);