给定一个 hwnd,确定 window 是否未被其他 windows 隐藏(Z 排序)
Given a hwnd, determine if that window is not hidden by other windows (Z-Ordering)
我有一个 NativeWindow
,我已经覆盖了 WndProc
函数来处理 WM_WINDOWPOSCHANGING 消息,以便移动我的 [=72] =] 我会把它贴在桌面上最近的 window 的边框上。
我遇到的问题是我的 window 正在对接到 windows,它们在其他 top-windows 的背景中,例如,如果我有一个 explorer [=72] =] open and other window below the explorer window, 然后我的 window 可以停靠到另一个 window 下面的 window, 这是一个z 顺序级别低于资源管理器 window。我想避免这种情况。
问题演示:
在上面的GIF中,有我的window(Form1),Visual StudioIDEwindow,一个探险家window,还有一个window 的名称为 "Hot Corners" 的应用程序。当我将 "Hot Corners" window 发送到后台时,我仍然可以将 window 粘贴到 "Hot corners" 边框。我想避免这种情况。
注意 Visual Studio 的 Output window 中的调试信息。
我在 Wikipedia, and I also seen this example and the MSDN docs here and here 上阅读了有关 Z 顺序的信息,但是,我仍然不明白如何实现它。
当我尝试将我的 window 坚持到其他 window 时,我需要确定该目标 window 是否低于其他 windows 或不是,以避免我解释的问题。
我希望我能很好地解释问题并清楚我需要什么,在上面的 GIF 中我的 window 不应该遵守 "Hot Corners" window 因为不可见,因为探险家window在上面
这是相关代码,该方法将我的 window (a Form
) 作为参数,WINDOWPOS 的句柄我在 window 的 WndProc
过程中过滤 WM_WINDOWPOSCHANGING
消息时得到的结构,最后一个参数 threshold
是之间所需的最小值 space我的 window 对其他 windows 的限制要遵守。
Protected Overridable Sub DockToNearestWindowBorder(ByVal window As IWin32Window,
ByVal windowPosHandle As IntPtr,
ByVal threshold As Integer)
Dim windowPos As WindowPos =
CType(Marshal.PtrToStructure(windowPosHandle, GetType(WindowPos)), WindowPos)
If (windowPos.Y = 0) OrElse (windowPos.X = 0) Then
' Nothing to do.
Exit Sub
End If
' Enumerate all the visible windows in the current desktop.
Dim desktopWindows As New List(Of IntPtr)()
Dim callBack As EnumWindowsProc =
Function(hwnd As IntPtr, lParam As IntPtr) As Boolean
If (NativeMethods.IsWindowVisible(hwnd)) Then
desktopWindows.Add(hwnd)
End If
Return True
End Function
If (NativeMethods.EnumDesktopWindows(IntPtr.Zero, callBack, IntPtr.Zero)) Then
' Window rectangles
Dim srcRect As Rectangle
Dim tgtRect As Rectangle
NativeMethods.GetWindowRect(window.Handle, srcRect)
For Each hwnd As IntPtr In desktopWindows
' This is just for testing purposes.
Dim pid As Integer
NativeMethods.GetWindowThreadProcessId(hwnd, pid)
If Process.GetProcessById(pid).ProcessName.EndsWith("vshost") Then
Continue For
End If
NativeMethods.GetWindowRect(hwnd, tgtRect)
' Right border of the source window
If ((windowPos.X + srcRect.Width) <= (tgtRect.Left + threshold)) AndAlso
((windowPos.X + srcRect.Width) >= (tgtRect.Left - threshold)) AndAlso
((windowPos.Y) <= (tgtRect.Y + tgtRect.Height)) AndAlso
((windowPos.Y + srcRect.Height) >= (tgtRect.Y)) Then
windowPos.X = (tgtRect.Left - srcRect.Width)
Console.WriteLine("Window adhered to: " & Process.GetProcessById(pid).ProcessName)
' This is not working as expected.
' If hwnd = NativeMethods.GetWindow(window.Handle, GetWindowCmd.HwndNext) Then
' windowPos.X = (tgtRect.Left - srcRect.Width)
' Exit For
' End If
End If
Next hwnd
End If
' Marshal it back.
Marshal.StructureToPtr(structure:=windowPos, ptr:=windowPosHandle, fDeleteOld:=True)
End Sub
请注意,在上面的代码中,我只显示了将我的 window 的右边框粘附到其他 windows 的威胁,这是为了避免为此增加代码问题,和缺少P/Invokes.
的原因相同
使用 GetWindowPlacement
避免检查最小化 window:
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参考:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633518(v=vs.85).aspx
给定一个 window 句柄,您应该能够使用一些 Win32 函数来确定 window 是否被其他 windows 完全或部分遮挡:
调用 GetWindowDC()
以检索包含整个 window 的设备上下文句柄 (HDC
),包括 non-client 区域(例如,标题栏、菜单、边框等)
使用上面的 HDC
return 调用 GetClipBox()
。这将 return 实际可见的最小边界矩形(即在屏幕上且未被其他 windows 覆盖)。此外,return 值可以告诉您 window 是否完全被遮挡(NULLREGION
)。
别忘了打电话给ReleaseDC()
.
API参考:
https://msdn.microsoft.com/en-us/library/dd144865%28v=vs.85%29.aspx
您需要做的第一件事(也是最困难的)是找到所有 "real" 非最小化(使用 IsIconic函数)可见windows。我的意思是,如果你使用 EnumWindows 或 EnumDesctopWindows 你会得到一堆不需要的 windows 例如: windows 开始图标、任务栏、程序管理器等 都是可见 并且 未最小化。如果您解决了这个问题,其他一切都非常简单:像您在数组中所做的那样存储句柄。顺序是从上到下的 z 顺序,例如。
arrayHandles[0]->顶部,arrayHandles[1]->一级波纹管等...
然后取出矩形并将它们存储在另一个数组中。最后一步是排除抽象的windows(显然rect0是Ok):
检查 rect1 和 rect0 是否相交。如果为真,则移除 rect1
用 rect1 和 rect0
检查 rect2
用 rect2 和 rect1 和 rect0
检查 rect3
....
最后你有一个 clean 矩形数组,你可以在移动 window 时检查它们。
重要提示:以上所有步骤你只做一次一次,当你开始拖动你的window,在 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);
我有一个 NativeWindow
,我已经覆盖了 WndProc
函数来处理 WM_WINDOWPOSCHANGING 消息,以便移动我的 [=72] =] 我会把它贴在桌面上最近的 window 的边框上。
我遇到的问题是我的 window 正在对接到 windows,它们在其他 top-windows 的背景中,例如,如果我有一个 explorer [=72] =] open and other window below the explorer window, 然后我的 window 可以停靠到另一个 window 下面的 window, 这是一个z 顺序级别低于资源管理器 window。我想避免这种情况。
问题演示:
在上面的GIF中,有我的window(Form1),Visual StudioIDEwindow,一个探险家window,还有一个window 的名称为 "Hot Corners" 的应用程序。当我将 "Hot Corners" window 发送到后台时,我仍然可以将 window 粘贴到 "Hot corners" 边框。我想避免这种情况。
注意 Visual Studio 的 Output window 中的调试信息。
我在 Wikipedia, and I also seen this example and the MSDN docs here and here 上阅读了有关 Z 顺序的信息,但是,我仍然不明白如何实现它。
当我尝试将我的 window 坚持到其他 window 时,我需要确定该目标 window 是否低于其他 windows 或不是,以避免我解释的问题。
我希望我能很好地解释问题并清楚我需要什么,在上面的 GIF 中我的 window 不应该遵守 "Hot Corners" window 因为不可见,因为探险家window在上面
这是相关代码,该方法将我的 window (a Form
) 作为参数,WINDOWPOS 的句柄我在 window 的 WndProc
过程中过滤 WM_WINDOWPOSCHANGING
消息时得到的结构,最后一个参数 threshold
是之间所需的最小值 space我的 window 对其他 windows 的限制要遵守。
Protected Overridable Sub DockToNearestWindowBorder(ByVal window As IWin32Window,
ByVal windowPosHandle As IntPtr,
ByVal threshold As Integer)
Dim windowPos As WindowPos =
CType(Marshal.PtrToStructure(windowPosHandle, GetType(WindowPos)), WindowPos)
If (windowPos.Y = 0) OrElse (windowPos.X = 0) Then
' Nothing to do.
Exit Sub
End If
' Enumerate all the visible windows in the current desktop.
Dim desktopWindows As New List(Of IntPtr)()
Dim callBack As EnumWindowsProc =
Function(hwnd As IntPtr, lParam As IntPtr) As Boolean
If (NativeMethods.IsWindowVisible(hwnd)) Then
desktopWindows.Add(hwnd)
End If
Return True
End Function
If (NativeMethods.EnumDesktopWindows(IntPtr.Zero, callBack, IntPtr.Zero)) Then
' Window rectangles
Dim srcRect As Rectangle
Dim tgtRect As Rectangle
NativeMethods.GetWindowRect(window.Handle, srcRect)
For Each hwnd As IntPtr In desktopWindows
' This is just for testing purposes.
Dim pid As Integer
NativeMethods.GetWindowThreadProcessId(hwnd, pid)
If Process.GetProcessById(pid).ProcessName.EndsWith("vshost") Then
Continue For
End If
NativeMethods.GetWindowRect(hwnd, tgtRect)
' Right border of the source window
If ((windowPos.X + srcRect.Width) <= (tgtRect.Left + threshold)) AndAlso
((windowPos.X + srcRect.Width) >= (tgtRect.Left - threshold)) AndAlso
((windowPos.Y) <= (tgtRect.Y + tgtRect.Height)) AndAlso
((windowPos.Y + srcRect.Height) >= (tgtRect.Y)) Then
windowPos.X = (tgtRect.Left - srcRect.Width)
Console.WriteLine("Window adhered to: " & Process.GetProcessById(pid).ProcessName)
' This is not working as expected.
' If hwnd = NativeMethods.GetWindow(window.Handle, GetWindowCmd.HwndNext) Then
' windowPos.X = (tgtRect.Left - srcRect.Width)
' Exit For
' End If
End If
Next hwnd
End If
' Marshal it back.
Marshal.StructureToPtr(structure:=windowPos, ptr:=windowPosHandle, fDeleteOld:=True)
End Sub
请注意,在上面的代码中,我只显示了将我的 window 的右边框粘附到其他 windows 的威胁,这是为了避免为此增加代码问题,和缺少P/Invokes.
的原因相同使用 GetWindowPlacement
避免检查最小化 window:
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参考: https://msdn.microsoft.com/en-us/library/windows/desktop/ms633518(v=vs.85).aspx
给定一个 window 句柄,您应该能够使用一些 Win32 函数来确定 window 是否被其他 windows 完全或部分遮挡:
调用
GetWindowDC()
以检索包含整个 window 的设备上下文句柄 (HDC
),包括 non-client 区域(例如,标题栏、菜单、边框等)使用上面的
HDC
return 调用GetClipBox()
。这将 return 实际可见的最小边界矩形(即在屏幕上且未被其他 windows 覆盖)。此外,return 值可以告诉您 window 是否完全被遮挡(NULLREGION
)。别忘了打电话给
ReleaseDC()
.
API参考: https://msdn.microsoft.com/en-us/library/dd144865%28v=vs.85%29.aspx
您需要做的第一件事(也是最困难的)是找到所有 "real" 非最小化(使用 IsIconic函数)可见windows。我的意思是,如果你使用 EnumWindows 或 EnumDesctopWindows 你会得到一堆不需要的 windows 例如: windows 开始图标、任务栏、程序管理器等 都是可见 并且 未最小化。如果您解决了这个问题,其他一切都非常简单:像您在数组中所做的那样存储句柄。顺序是从上到下的 z 顺序,例如。
arrayHandles[0]->顶部,arrayHandles[1]->一级波纹管等...
然后取出矩形并将它们存储在另一个数组中。最后一步是排除抽象的windows(显然rect0是Ok):
检查 rect1 和 rect0 是否相交。如果为真,则移除 rect1
用 rect1 和 rect0
检查 rect2
用 rect2 和 rect1 和 rect0
检查 rect3
....
最后你有一个 clean 矩形数组,你可以在移动 window 时检查它们。
重要提示:以上所有步骤你只做一次一次,当你开始拖动你的window,在 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);