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

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.



Solution 1:[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 have to call EnumWindows.

But to retrieve full screen UWP windows:

Here is a sample to list all the "Alt+Tab windows", even the full screen UWP windows, in a form with a two-column DataGridView and retrieve the handle of the window picked by the user:

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
}

Note: Standard windows cannot be cloaked by the shell; full screen UWP windows can. When you place another window over a full screen UWP window it disappears because it is cloaked by the shell, not minimized.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1