using System.Runtime.InteropServices; using System.Text; using WhiteMagic.Native; namespace WhiteMagic.Windows; /// /// Wrapper around a native window handle that supports querying and mutating /// common window properties. /// public sealed class RemoteWindow { private const int MaxTextLength = 512; /// Creates a wrapper for the specified window handle. public RemoteWindow(IntPtr handle) { if (handle == IntPtr.Zero) throw new ArgumentException("Window handle cannot be zero.", nameof(handle)); Handle = handle; } /// The native window handle. public IntPtr Handle { get; } /// The window class name. public string ClassName => GetClassName(Handle); /// The current window text. public string Text => GetWindowText(Handle); /// The process identifier that owns the window. public uint ProcessId => GetWindowProcessId(Handle); /// Gets or sets the window title. public string Title { get => GetWindowText(Handle); set { if (value is null) throw new ArgumentNullException(nameof(value)); if (!NativeMethods.SetWindowTextW(Handle, value)) { int error = Marshal.GetLastPInvokeError(); throw new InvalidOperationException( $"SetWindowText failed for window {Handle} with error {error}."); } } } /// if this window is currently the foreground window. public bool IsActive => NativeMethods.GetForegroundWindow() == Handle; /// Moves and resizes the window. public bool MoveResize(int x, int y, int width, int height) { return NativeMethods.SetWindowPos( Handle, NativeMethods.HwndTop, x, y, width, height, NativeMethods.SwpShowWindow); } /// Activates the window and brings it to the foreground. public bool Activate() { IntPtr foreground = NativeMethods.GetForegroundWindow(); uint targetThread = NativeMethods.GetWindowThreadProcessId(Handle, out _); uint foregroundThread = NativeMethods.GetWindowThreadProcessId(foreground, out _); if (targetThread == 0) return false; if (targetThread == foregroundThread) return NativeMethods.SetForegroundWindow(Handle); if (!NativeMethods.AttachThreadInput(foregroundThread, targetThread, true)) return false; try { return NativeMethods.SetForegroundWindow(Handle); } finally { NativeMethods.AttachThreadInput(foregroundThread, targetThread, false); } } /// Flashes the window in the caption and taskbar button. public bool Flash() { var info = new FlashWindowInfo { cbSize = (uint)Marshal.SizeOf(), hwnd = Handle, dwFlags = NativeMethods.FlashwAll, uCount = 3, dwTimeout = 0, }; return NativeMethods.FlashWindowEx(ref info); } public override string ToString() { var sb = new StringBuilder(); sb.Append("RemoteWindow("); sb.Append(Handle.ToString("X")); sb.Append(", "); sb.Append(ClassName); sb.Append(")"); return sb.ToString(); } private static string GetClassName(IntPtr handle) { var buffer = new char[256]; int length = NativeMethods.GetClassNameW(handle, buffer, buffer.Length); if (length <= 0) return string.Empty; return new string(buffer, 0, length); } private static string GetWindowText(IntPtr handle) { var buffer = new char[MaxTextLength]; int length = NativeMethods.GetWindowTextW(handle, buffer, buffer.Length); if (length <= 0) return string.Empty; return new string(buffer, 0, length); } private static uint GetWindowProcessId(IntPtr handle) { NativeMethods.GetWindowThreadProcessId(handle, out uint processId); return processId; } }