Reputation: 37
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.
Upvotes: -2
Views: 1145
Reputation: 137
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;
}
Upvotes: 0
Reputation: 37
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.
Upvotes: 2