Helpers/ParentWindowHelper.cs
|
using System;
using System.Runtime.InteropServices; internal enum GetAncestorFlags { GetParent = 1, GetRoot = 2, /// <summary> /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. /// </summary> GetRootOwner = 3 } public static class ParentWindowHelper { [DllImport("user32.dll", ExactSpelling = true)] private static extern IntPtr GetAncestor(IntPtr hwnd, GetAncestorFlags flags); [DllImport("kernel32.dll", ExactSpelling = true)] private static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll", ExactSpelling = true)] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll", ExactSpelling = true)] private static extern bool IsWindow(IntPtr hWnd); /// <summary> /// Delegate MSAL can call to obtain a parent window handle (HWND). /// Keep it public so PowerShell can override if needed. /// </summary> public static Func<IntPtr> ConsoleWindowHandleProvider = () => { try { // 1) Classic console host (conhost.exe) IntPtr consoleHandle = GetConsoleWindow(); if (consoleHandle != IntPtr.Zero) { IntPtr rootOwner = GetAncestor(consoleHandle, GetAncestorFlags.GetRootOwner); IntPtr normalized = Normalize(rootOwner); if (normalized != IntPtr.Zero) return normalized; } // 2) VS Code / Windows Terminal / ConPTY: no real console HWND (GetConsoleWindow == 0) // Best-effort: parent to currently focused top-level window. IntPtr fg = Normalize(GetForegroundWindow()); if (fg != IntPtr.Zero) return fg; // 3) No usable window handle return IntPtr.Zero; } catch { // Never throw from a parent window provider; let caller fall back to browser/device code return IntPtr.Zero; } }; /// <summary> /// Convenience method if you prefer a direct call. /// </summary> public static IntPtr GetConsoleOrTerminalWindow() { return ConsoleWindowHandleProvider(); } private static IntPtr Normalize(IntPtr hwnd) { if (hwnd == IntPtr.Zero) return IntPtr.Zero; // Avoid returning invalid handles if (!IsWindow(hwnd)) return IntPtr.Zero; return hwnd; } } |