Search code examples
c#windowshwnd

How to get the hWnd of a floating window of an application


I want to get (and alter the position of) the windows on my screen. I have managed to do so with the code below. But some applications have floating windows (e.g. I have the Solution Explorer floating outside Visual Studio) and I am not getting the handles of these windows as well.

I had hoped to find those windows with the WS_EX_TOOLWINDOW flag by returning true at the if-statement of WS_EX_TOOLWINDOW. But that wasn't the case. I also tried to use the EnumChildWindows from the user32.dll. But also no luck.

So how do I get the handles of the floating (child) windows of the application hWnd I found with IsAltTabWindow()?

private Snapshot()
{
    EnumWindows(EvalWindow, 0);
}

internal bool EvalWindow(IntPtr hwnd, int lParam)
{
    if (!IsAltTabWindow(hwnd))
    {
        return true;
    }
    // GetWindowPlacement...
}

private static bool IsAltTabWindow(IntPtr hwnd)
{
    if (!IsWindowVisible(hwnd))
    {
        return false;
    }

    IntPtr extendedStyles = GetWindowLongPtr(hwnd, (-20)); // GWL_EXSTYLE
    if ((extendedStyles.ToInt64() & 0x00040000) > 0) // WS_EX_APPWINDOW
    {
        return true;
    }

    if ((extendedStyles.ToInt64() & 0x00000080) > 0) // WS_EX_TOOLWINDOW
    {
        return false;
    }

    IntPtr hwndTry = GetAncestor(hwnd, GetAncestorFlags.SWP_GET_ROOT_OWNER);
    IntPtr hwndWalk = IntPtr.Zero;
    while (hwndTry != hwndWalk)
    {
        hwndWalk = hwndTry;
        hwndTry = GetLastActivePopup(hwndWalk);
        if (IsWindowVisible(hwndTry))
        {
            break;
        }
    }

    if (hwndWalk != hwnd)
    {
        return false;
    }

    return true;
}

[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
private static extern IntPtr GetWindowLongPtr32(IntPtr hWnd, int nIndex);

[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);

private static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
{
    if (IntPtr.Size == 8)
    {
        return GetWindowLongPtr64(hWnd, nIndex);
    }

    return GetWindowLongPtr32(hWnd, nIndex);
}

[DllImport("user32.dll")]
private static extern IntPtr GetLastActivePopup(IntPtr hWnd);

[DllImport("user32.dll", ExactSpelling = true)]
private static extern IntPtr GetAncestor(IntPtr hwnd, GetAncestorFlags gaFlags);

Solution

  • Thanks to the advice of both shingo and Hans Passant (both tools did the job) I managed to find the handle of the floating VS window. And while debugging I noticed that the while-loop about the GetAncestor() in the IsAltTabWindow() actually was returning false and prevented processing the floating windows. So I just removed those lines, and now the hWnd of the floating windows are returning true.

    private static bool IsAltTabWindow(IntPtr hwnd)
    {
        if (!IsWindowVisible(hwnd))
        {
            return false;
        }
    
        IntPtr extendedStyles = GetWindowLongPtr(hwnd, (-20)); // GWL_EXSTYLE
        if ((extendedStyles.ToInt64() & 0x00040000) > 0) // WS_EX_APPWINDOW
        {
            return true;
        }
    
        if ((extendedStyles.ToInt64() & 0x00000080) > 0) // WS_EX_TOOLWINDOW
        {
            return false;
        }
    
        return true;
    }
    

    I just don't have a clue why the original writer of the method implemented this while loop. And IsAltTabWindow() is not the best method-name, because it is not covering all windows. But hey, at least I get what I want now.