Files
whitemagic/WhiteMagic/Windows/RemoteWindow.cs
T
kbe 040a51bf03 fix: Build documentation at compilation
Some documentation was broken. I refactored it to be declarative now the
project build correctly and produce XML documentation.
2026-07-22 19:58:53 +02:00

154 lines
4.6 KiB
C#

using System.Runtime.InteropServices;
using System.Text;
using WhiteMagic.Native;
namespace WhiteMagic.Windows;
/// <summary>
/// Wrapper around a native window handle that supports querying and mutating
/// common window properties.
/// </summary>
public sealed class RemoteWindow
{
private const int MaxTextLength = 512;
/// <summary>Creates a wrapper for the specified window handle.</summary>
public RemoteWindow(IntPtr handle)
{
if (handle == IntPtr.Zero)
throw new ArgumentException("Window handle cannot be zero.", nameof(handle));
Handle = handle;
}
/// <summary>The native window handle.</summary>
public IntPtr Handle { get; }
/// <summary>The window class name.</summary>
public string ClassName => GetClassName(Handle);
/// <summary>The current window text.</summary>
public string Text => GetWindowText(Handle);
/// <summary>The process identifier that owns the window.</summary>
public uint ProcessId => GetWindowProcessId(Handle);
/// <summary>Gets or sets the window title.</summary>
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}.");
}
}
}
/// <summary><see langword="true"/> if this window is currently the foreground window.</summary>
public bool IsActive => NativeMethods.GetForegroundWindow() == Handle;
/// <summary>Moves and resizes the window.</summary>
public bool MoveResize(int x, int y, int width, int height)
{
return NativeMethods.SetWindowPos(
Handle,
NativeMethods.HwndTop,
x,
y,
width,
height,
NativeMethods.SwpShowWindow);
}
/// <summary>Activates the window and brings it to the foreground.</summary>
public bool Activate()
{
IntPtr foreground = NativeMethods.GetForegroundWindow();
uint targetThread = NativeMethods.GetWindowThreadProcessId(Handle, out _);
if (targetThread == 0)
return false;
// If there's no foreground window, or we're already in the foreground thread, just set it
if (foreground == IntPtr.Zero)
return NativeMethods.SetForegroundWindow(Handle);
uint foregroundThread = NativeMethods.GetWindowThreadProcessId(foreground, out _);
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);
}
}
/// <summary>Flashes the window in the caption and taskbar button.</summary>
public bool Flash()
{
var info = new FlashWindowInfo
{
cbSize = (uint)Marshal.SizeOf<FlashWindowInfo>(),
hwnd = Handle,
dwFlags = NativeMethods.FlashwAll,
uCount = 3,
dwTimeout = 0,
};
return NativeMethods.FlashWindowEx(ref info);
}
/// <summary>Returns a string representation of this window, including its handle, class name, and title.</summary>
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;
}
}