Show a list of all "Alt+Tab windows" (even full screen UWP windows) and retrieve the handle of the one picked by the user
Asked Answered
A

2

-2

I need to retrieve the handle of a window selected by the user and then retrieve its handle. This window must be one of those shown when ALT+TAB is pressed.

I tried enumerating the windows using EnumWindows, but it does not enumerate the full screen UWP windows. For example, if you open a picture with the Photos app and put it in full screen, EnumWindows will not enumerate it.

Then I tried EnumChildWindows because I thought it could enumerate everything, even fullscreen UWP windows, but probably not.

The GraphicsCapturePicker.PickSingleItemAsync method shows a list of windows and the user can pick one, but it returns a GraphicsCaptureItem and I guess you can't get the window handle from it.

Is it possible to reuse the ALT+TAB window to do this (or any other way that shows a list of windows) and retrieve the handle of the window selected by the user?

Note: I need all the windows shown when ALT+TAB is pressed, even the full screen UWP windows, and no others.

Angelicaangelico answered 30/4, 2022 at 15:24 Comment(12)
Unclear, what you're asking for. You seem to be using EnumChildWindows but then move on to asking how Alt+Tab filters its list of windows. Clearly, the Alt+Tab list doesn't include child windows. So... don't call EnumChildWindows.Photozincography
EnumWindows should do itCortege
You've given us an XY problem. GetForgroundWindow should get exactly what you are after.Nash
Please read How to Ask.Nash
@Nash The question isn't "What is the window the user is currently using?" It's "I want to display some UI with a list of windows and ask the user to pick one of them."Rienzi
Like what ScreenCaptureforHWND does? IsAltTabWindowKenyatta
@Kenyatta That function is not very good because it filters out all windows without a title (it is not mandatory for a window to have a title) and instead includes other windows that do not appear when Alt+Tab is pressed.Angelicaangelico
@Nash I am sorry to have caused confusion. I hope the question is now more clear.Angelicaangelico
@Cortege EnumWindows does not enumerate the full screen UWP windows. I have added more details to my post. I hope it is now more clear.Angelicaangelico
@Photozincography Sorry for the confusion. I added why I tried EnumChildWindows.Angelicaangelico
@Angelicaangelico ScreenCaptureforHWND can list the full screen UWP windows. And you can comment out the title filter.Kenyatta
@Kenyatta If you open a picture with Photos, run ScreenCaptureHWND and then put the picture in full screen, Photos is listed. This is because its handle was retrieved when it was not full screen. But if you open the picture, put it in full screen, and after that run ScreenCaptureHWND, Photos is not listed. This is an EnumWindows problem, as described here. However, investigating with Spy++, I may have found a solution. I will provide further updates soon.Angelicaangelico
A
1

I have investigated with Spy++ and neither EnumWindows nor EnumChildWindows retrieve the handles of the root owners of full screen UWP windows. However EnumChildWindows retrieves their child windows, and each UWP window has a child window whose class name is ApplicationFrameInputSinkWindow (and other child windows). Then, you can retrive the root owner window with GetAncestor.

So, to retrieve "standard" windows, you could call EnumWindows.

But to retrieve full screen UWP windows:

This sample shows how to use both EnumWindows and EnumChildWindows to enumerate all "ALT+TAB windows", even full-screen UWP windows. These are listed in a Form with a two-column DataGridView and the window handle corresponding to the row the user clicks on is retrieved.

const int GWL_EXSTYLE = -20;
const uint DWMWA_CLOAKED = 14;
const uint DWM_CLOAKED_SHELL = 0x00000002;
const uint GA_ROOTOWNER = 3;
const uint WS_EX_TOOLWINDOW = 0x00000080;
const uint WS_EX_TOPMOST = 0x00000008;
const uint WS_EX_NOACTIVATE = 0x08000000;

private void Form1_Load(object sender, EventArgs e)
{
    dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
    dataGridView1.MultiSelect = false;
    dataGridView1.ReadOnly = true;
    dataGridView1.Click += dataGridView1_Click;
    
    EnumWindows(GetAltTabWindows, IntPtr.Zero);
    EnumChildWindows(GetDesktopWindow(), GetFullScreenUWPWindows, IntPtr.Zero);
}

private bool GetAltTabWindows(IntPtr hWnd, IntPtr lparam)
{
    if (IsAltTabWindow(hWnd))
        AddWindowToGrid(hWnd);

    return true;
}

private bool GetFullScreenUWPWindows(IntPtr hWnd, IntPtr lparam)
{
    // Check only the windows whose class name is ApplicationFrameInputSinkWindow
    StringBuilder className = new StringBuilder(1024);
    GetClassName(hWnd, className, className.Capacity);
    if (className.ToString() != "ApplicationFrameInputSinkWindow")
        return true;

    // Get the root owner of the window
    IntPtr rootOwner = GetAncestor(hWnd, GA_ROOTOWNER);

    if (IsFullScreenUWPWindows(rootOwner))
        AddWindowToGrid(rootOwner);

    return true;
}

private bool IsAltTabWindow(IntPtr hWnd)
{
    // The window must be visible
    if (!IsWindowVisible(hWnd))
        return false;

    // The window must be a root owner
    if (GetAncestor(hWnd, GA_ROOTOWNER) != hWnd)
        return false;

    // The window must not be cloaked by the shell
    DwmGetWindowAttribute(hWnd, DWMWA_CLOAKED, out uint cloaked, sizeof(uint));
    if (cloaked == DWM_CLOAKED_SHELL)
        return false;

    // The window must not have the extended style WS_EX_TOOLWINDOW
    uint style = GetWindowLong(hWnd, GWL_EXSTYLE);
    if ((style & WS_EX_TOOLWINDOW) != 0)
        return false;
    
    return true;
}

private bool IsFullScreenUWPWindows(IntPtr hWnd)
{
    // Get the extended style of the window
    uint style = GetWindowLong(hWnd, GWL_EXSTYLE);

    // The window must have the extended style WS_EX_TOPMOST
    if ((style & WS_EX_TOPMOST) == 0)
        return false;

    // The window must not have the extended style WS_EX_NOACTIVATE
    if ((style & WS_EX_NOACTIVATE) != 0)
        return false;

    // The window must not have the extended style WS_EX_TOOLWINDOW
    if ((style & WS_EX_TOOLWINDOW) != 0)
        return false;

    return true;
}

private void AddWindowToGrid(IntPtr hWnd)
{
    StringBuilder windowText = new StringBuilder(1024);
    GetWindowText(hWnd, windowText, windowText.Capacity);
    var strTitle = windowText.ToString();
    var strHandle = hWnd.ToString("X8");
    dataGridView1.Rows.Add(new string[] { strHandle, strTitle });
}

private void dataGridView1_Click(object sender, EventArgs e)
{
    var dgv = (DataGridView)sender;

    if (dgv.SelectedRows.Count == 0)
        return;

    // Get the value of the first cell of the selected row
    var value = dgv.SelectedRows[0].Cells[0].Value;
    if (value == null)
        return;

    // Convert the value to IntPtr
    var strValue = value.ToString();
    var intValue = int.Parse(strValue, System.Globalization.NumberStyles.HexNumber);
    var windowHandle = new IntPtr(intValue);

    // Do what you want with the window handle
}

Of course, you can also just use EnumChildWindows to get all "ALT+TAB windows", as long as the callback function has all the necessary filters to filter the different windows.

Angelicaangelico answered 5/5, 2022 at 14:21 Comment(0)
H
0

This is how Microsoft did it in their Screen Capture example.

You'll find it in the Win32WindowEnumeration.h.


#pragma once
#include <dwmapi.h>

struct Window
{
public:
    Window(nullptr_t) {}
    Window(HWND hwnd, std::wstring const& title, std::wstring& className)
    {
        m_hwnd = hwnd;
        m_title = title;
        m_className = className;
    }

    HWND Hwnd() const noexcept { return m_hwnd; }
    std::wstring Title() const noexcept { return m_title; }
    std::wstring ClassName() const noexcept { return m_className; }

private:
    HWND m_hwnd;
    std::wstring m_title;
    std::wstring m_className;
};

std::wstring GetClassName(HWND hwnd)
{
    std::array<WCHAR, 1024> className;

    ::GetClassName(hwnd, className.data(), (int)className.size());

    std::wstring title(className.data());
    return title;
}

std::wstring GetWindowText(HWND hwnd)
{
    std::array<WCHAR, 1024> windowText;

    ::GetWindowText(hwnd, windowText.data(), (int)windowText.size());

    std::wstring title(windowText.data());
    return title;
}

bool IsAltTabWindow(Window const& window)
{
    HWND hwnd = window.Hwnd();
    HWND shellWindow = GetShellWindow();

    auto title = window.Title();
    auto className = window.ClassName();

    if (hwnd == shellWindow)
    {
        return false;
    }

    if (title.length() == 0)
    {
        return false;
    }

    if (!IsWindowVisible(hwnd))
    {
        return false;
    }

    if (GetAncestor(hwnd, GA_ROOT) != hwnd)
    {
        return false;
    }

    LONG style = GetWindowLong(hwnd, GWL_STYLE);
    if (!((style & WS_DISABLED) != WS_DISABLED))
    {
        return false;
    }

    DWORD cloaked = FALSE;
    HRESULT hrTemp = DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked));
    if (SUCCEEDED(hrTemp) &&
        cloaked == DWM_CLOAKED_SHELL)
    {
        return false;
    }

    return true;
}

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    auto class_name = GetClassName(hwnd);
    auto title = GetWindowText(hwnd);

    auto window = Window(hwnd, title, class_name);

    if (!IsAltTabWindow(window))
    {
        return TRUE;
    }

    std::vector<Window>& windows = *reinterpret_cast<std::vector<Window>*>(lParam);
    windows.push_back(window);

    return TRUE;
}

const std::vector<Window> EnumerateWindows()
{
    std::vector<Window> windows;
    EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&windows));

    return windows;
}

Hollander answered 22/10 at 22:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.