I have a fullscreen application that does not capture the cursor context, but should always be displayed on top of any windows, including Windows context windows, the taskbar, a pinned task manager.
Here’s what I have at the moment:
public partial class MainWindow : Window
{
private const int EXIT_HOTKEY_ID = 9355;
private const int WM_HOTKEY = 0x0312;
private const int GWL_EXSTYLE = -20;
private const int WS_EX_NOACTIVATE = 0x08000000;
private const int WS_EX_TOPMOST = 0x00000008;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOACTIVATE = 0x0010;
private const uint SWP_SHOWWINDOW = 0x0040;
private const int WH_CALLWNDPROC = 4;
private DispatcherTimer _timer;
private IntPtr _hwnd;
private IntPtr _hookID = IntPtr.Zero;
private HookProc _proc;
private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
public MainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
Unloaded += OnUnloaded;
ShowActivated = true;
Topmost = true;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
_hwnd = new WindowInteropHelper(this).Handle;
int exStyle = GetWindowLong(_hwnd, GWL_EXSTYLE);
SetWindowLong(_hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST | WS_EX_NOACTIVATE);
SetWindowPos(_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(100)
};
_timer.Tick += TimerTick;
_timer.Start();
_proc = new HookProc(HookCallback);
_hookID = SetHook(_proc);
AttachToForegroundWindow();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
UnhookWindowsHookEx(_hookID);
DetachFromForegroundWindow();
}
private void TimerTick(object sender, EventArgs e)
{
SetWindowPos(_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
}
private IntPtr SetHook(HookProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_CALLWNDPROC, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
// Checking whether a window needs to be forced to the foreground
SetWindowPos(_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private void AttachToForegroundWindow()
{
IntPtr foregroundWindow = GetForegroundWindow();
if (foregroundWindow != IntPtr.Zero)
{
uint foregroundThreadId = GetWindowThreadProcessId(foregroundWindow, out _);
uint currentThreadId = GetWindowThreadProcessId(_hwnd, out _);
if (foregroundThreadId != currentThreadId)
{
AttachThreadInput(currentThreadId, foregroundThreadId, true);
BringWindowToTop(_hwnd);
AttachThreadInput(currentThreadId, foregroundThreadId, false);
}
}
}
private void DetachFromForegroundWindow()
{
IntPtr foregroundWindow = GetForegroundWindow();
if (foregroundWindow != IntPtr.Zero)
{
uint foregroundThreadId = GetWindowThreadProcessId(foregroundWindow, out _);
uint currentThreadId = GetWindowThreadProcessId(_hwnd, out _);
if (foregroundThreadId != currentThreadId)
{
AttachThreadInput(currentThreadId, foregroundThreadId, false);
}
}
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
e.Handled = true;
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
e.Handled = true;
}
protected override void OnMouseMove(MouseEventArgs e)
{
e.Handled = true;
}
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
}
My window appears on top of any program windows except for standard Windows 11 context or pop-up windows and Task Manager, which is pinned on top of windows.
The only way I found to partially fix this is to set not only SetWindowPos but also SetWindowLong by timer, but in this case the context or pop-up window of Windows 11 closes. And it does not work with Task Manager at all.
Maybe you know a way? A solution of any complexity level will do.
Additionally. My app only has one window, which is mostly transparent. This window only shows the widgets I need, like my screen keyboard.