Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given a hwnd, determine if that window is not hidden by other windows (Z-Ordering)

I have a NativeWindow, I've overridden the WndProc function to process WM_WINDOWPOSCHANGING message for when moving my window I'll adhere it to the borders of the nearest window in the desktop.

The problem I have is that my window is docking to windows that are in the background of other top-windows, for example if I have an explorer window open and other window below the explorer window, then my window can dock to the window that is below of the other window, which is a z-order level lower than the explorer window. I want to avoid this.

A demostration of the problem:

enter image description here

In the GIF above, there is my window (Form1), the Visual Studio IDE window, an explorer window, and a window of an application with name "Hot Corners". When I send the "Hot Corners" window to background, I still can adhere my window to "Hot corners" borders. I want to avoid this.

Note the debug info in the captured Output window of Visual Studio.


I read about Z-Ordering on Wikipedia, and I also seen this example and the MSDN docs here and here, however, I still not understand how to achieve this.

When I attempt to adhere my window to other window, I need to determine if that target window is below other windows or isnt, to avoid the issue that I explained.

I hope that I explained good the problem and is clear what I need, in the GIF above my window shouldn't adhere to the "Hot Corners" window because is not visible, because the explorer window is above.

This is the relevant code, the method takes as arguments my window (a Form), the handle to a WINDOWPOS struct that I get when filtering the WM_WINDOWPOSCHANGING message in the WndProc procedure of my window, and the last parameter, threshold, is the minimum space required between the bounds of my window to other windows to adhere it.

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

Note that in the code above I only shown the threatment to adhere the right border of my window to other windows, this is to avoid increasing the code for this question, and the same reason for the missing P/Invokes.

like image 576
ElektroStudios Avatar asked Dec 01 '15 17:12

ElektroStudios


People also ask

How do you Z order a window?

You can use the GetTopWindow function to search all child windows of a parent window and return a handle to the child window that is highest in z-order. The GetNextWindow function retrieves a handle to the next or previous window in z-order.

What is Hwnd type?

HWND data types are "Handles to a Window", and are used to keep track of the various objects that appear on the screen. To communicate with a particular window, you need to have a copy of the window's handle. HWND variables are usually prefixed with the letters "hwnd", just so the programmer knows they are important.

What is overlapped window?

An overlapped window is a top-level window (non-child window) that has a title bar, border, and client area; it is meant to serve as an application's main window. It can also have a window menu, minimize and maximize buttons, and scroll bars.


1 Answers

Given a window handle, you should be able to determine whether the window is fully or partially obscured by other windows using a few Win32 functions:

  1. Call GetWindowDC() to retrieve a device context handle (HDC) that includes the entire window, including the non-client areas (e.g., title bar, menus, borders, etc.)

  2. Call GetClipBox() with the HDC returned above. This will return the smallest bounding rectangle that is actually visible (i.e., on screen and not covered by other windows). Also, the return value can tell you whether the window is completely obscured (NULLREGION).

  3. Don't forget to call ReleaseDC().

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

like image 189
cbranch Avatar answered Nov 26 '22 04:11

cbranch