This repository has been archived on 2022-08-05. You can view files and clone it, but cannot push or open issues or pull requests.
CC-Functions/W32/Wnd32.cs
2020-05-25 19:32:53 +02:00

389 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using CC_Functions.W32.Native;
namespace CC_Functions.W32
{
/// <summary>
/// Object representing a window handle in the Windows API. Provides a simplified interface for basic interactions
/// </summary>
public sealed class Wnd32 : IEquatable<Wnd32>
{
#region Exposed
#region CreateInstance
private Wnd32(IntPtr handle) => HWnd = handle;
/// <summary>
/// Base method. Generates a window object from the specified handle
/// </summary>
/// <param name="handle">The handle</param>
/// <returns>The window</returns>
public static Wnd32 FromHandle(IntPtr handle) => new Wnd32(handle);
/// <summary>
/// Gets the main window of the process
/// </summary>
/// <param name="process">The process</param>
/// <returns>The window. Might be IntPtr.Zero</returns>
public static Wnd32 GetProcessMain(Process process) => FromHandle(process.MainWindowHandle);
/// <summary>
/// Generates a window from metadata. Parameters should be null if they are not used
/// </summary>
/// <param name="lpClassName">
/// The class name of the window. Use the name you found before using the ClassName-parameter of
/// a window
/// </param>
/// <param name="lpWindowName">The windows name (title)</param>
/// <returns>The window. Might be IntPtr.Zero</returns>
public static Wnd32 FromMetadata(string? lpClassName = null, string? lpWindowName = null) =>
FromHandle(user32.FindWindow(lpClassName, lpWindowName));
/// <summary>
/// Gets the window that is visible at the specified point
/// </summary>
/// <param name="point">The point to scan</param>
/// <returns>The window. Might be IntPtr.Zero</returns>
public static Wnd32 FromPoint(Point point) => FromHandle(user32.WindowFromPoint(point.X, point.Y));
/// <summary>
/// Gets all windows at the specific point
/// </summary>
/// <param name="point">The point to scan</param>
/// <param name="visible">Whether windows need to be visible</param>
/// <returns>The windows</returns>
public static Wnd32[] AllFromPoint(Point point, bool visible = false) =>
All.Where(s => s.Position.Contains(point) && s.Shown || !visible).ToArray();
/// <summary>
/// Gets the window associated with the forms handle
/// </summary>
/// <param name="form">Form to get window from</param>
/// <returns>The window. Might be IntPtr.Zero</returns>
public static Wnd32 FromForm(Form form) => FromHandle(form.Handle);
/// <summary>
/// Gets ALL windows. In most cases you will want to use Wnd32.Visible
/// </summary>
/// <exception cref="Win32Exception"></exception>
public static Wnd32[] All
{
get
{
_windowHandles = new List<IntPtr>();
if (!user32.EnumDesktopWindows(IntPtr.Zero, FilterCallback, IntPtr.Zero))
throw new Win32Exception("There was a native error. This should never happen!");
return _windowHandles.Select(FromHandle).ToArray();
}
}
/// <summary>
/// Gets all visible windows
/// </summary>
public static Wnd32[] Visible =>
All.Where(s => s.Shown).ToArray();
/// <summary>
/// Gets the foreground window
/// </summary>
public static Wnd32 Foreground => FromHandle(user32.GetForegroundWindow());
/// <summary>
/// The current programs console window. Do NOT use this if you are not targeting a console app or allocating a console
/// </summary>
public static Wnd32 ConsoleWindow => FromHandle(kernel32.GetConsoleWindow());
#endregion CreateInstance
#region InstanceActions
public Wnd32[] Children
{
get
{
List<IntPtr> childHandles = new List<IntPtr>();
GCHandle gcChildHandlesList = GCHandle.Alloc(childHandles);
IntPtr pointerChildHandlesList = GCHandle.ToIntPtr(gcChildHandlesList);
try
{
user32.EnumChildWindows(HWnd, EnumWindow, pointerChildHandlesList);
}
finally
{
gcChildHandlesList.Free();
}
return childHandles.Select(FromHandle).ToArray();
}
}
public Wnd32 Parent
{
get
{
IntPtr result = user32.GetParent(HWnd);
if (result == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
return FromHandle(result);
}
}
/// <summary>
/// The windows title
/// </summary>
public string Title
{
get
{
int length = user32.GetWindowTextLength(HWnd);
StringBuilder sb = new StringBuilder(length + 1);
user32.GetWindowText(HWnd, sb, sb.Capacity);
return sb.ToString();
}
set => user32.SetWindowTextW(HWnd, value);
}
/// <summary>
/// The windows position in screen-space
/// </summary>
public Rectangle Position
{
get
{
RECT rect = new RECT();
user32.GetWindowRect(HWnd, ref rect);
return new Rectangle(new Point(rect.Left, rect.Top),
new Size(rect.Width, rect.Height));
}
set
{
RECT rect = new RECT();
user32.GetWindowRect(HWnd, ref rect);
user32.MoveWindow(HWnd, value.X, value.Y, value.Width, value.Height, true);
}
}
/// <summary>
/// Gets whether the window is the foreground window or brings it to the front. "False" must not be assigned!
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when the value is "False"</exception>
public bool IsForeground
{
get => user32.GetForegroundWindow() == HWnd;
set
{
if (value)
user32.SetForegroundWindow(HWnd);
else
throw new InvalidOperationException(
"You can't set a Window not to be in the foreground. Move another one over it!");
}
}
/// <summary>
/// Whether the window is enabled. Functionally similar to WinForms' "Control.Enabled"
/// </summary>
public bool Enabled
{
get => user32.IsWindowEnabled(HWnd);
set => user32.EnableWindow(HWnd, value);
}
/// <summary>
/// Gets the windows icon
/// </summary>
public Icon Icon
{
get
{
IntPtr hIcon = user32.SendMessage(HWnd, 0x7F, 1, 0);
if (hIcon == IntPtr.Zero)
hIcon = user32.SendMessage(HWnd, 0x7F, 0, 0);
if (hIcon == IntPtr.Zero)
hIcon = user32.SendMessage(HWnd, 0x7F, 2, 0);
return Icon.FromHandle(hIcon);
}
}
/// <summary>
/// Whether the window is visible
/// </summary>
public bool Shown
{
get => user32.IsWindowVisible(HWnd);
set => user32.ShowWindow(HWnd, value ? 9 : 0);
}
/// <summary>
/// Gets the windows class name, This is basically only useful for finding window class-names of specified programs
/// </summary>
public string ClassName
{
get
{
StringBuilder className = new StringBuilder(256);
user32.GetClassName(HWnd, className, className.Capacity);
return className.ToString();
}
}
/// <summary>
/// Sets the window state
/// </summary>
public FormWindowState State
{
get
{
int style = user32.GetWindowLong(HWnd, -16);
if ((style & 0x01000000) == 0x01000000)
return FormWindowState.Maximized;
if ((style & 0x20000000) == 0x20000000)
return FormWindowState.Minimized;
return FormWindowState.Normal;
}
set
{
switch (value)
{
case FormWindowState.Minimized:
user32.ShowWindow(HWnd, 11);
break;
case FormWindowState.Normal:
user32.ShowWindow(HWnd, 1);
break;
case FormWindowState.Maximized:
user32.ShowWindow(HWnd, 3);
break;
default:
throw new ArgumentException("The provided WindowState was invalid", "value");
}
}
}
/// <summary>
/// Overlays the window over others
/// </summary>
public bool Overlay
{
set
{
Rectangle tmp = Position;
user32.SetWindowPos(HWnd, value ? new IntPtr(-1) : new IntPtr(-2), tmp.X, tmp.Y, tmp.Width, tmp.Height,
value ? (uint) 3 : 0);
}
}
/// <summary>
/// Forces the window to close
/// </summary>
/// <exception cref="Exception">Thrown if the window could not be closed</exception>
public void Destroy()
{
if (!user32.DestroyWindow(HWnd))
throw new Exception("Failed.");
}
/// <summary>
/// Whether the IntPtr is a window and still exists
/// </summary>
public bool StillExists => user32.IsWindow(HWnd);
/// <summary>
/// Creates a user-readable string from the windows hWnd, title and position
/// </summary>
/// <returns>The created string</returns>
public override string ToString() => $"{HWnd}; {Title}; {Position}";
/// <summary>
/// Equality operator, uses the hWnd field
/// </summary>
/// <param name="obj">Object (Window) to compare</param>
/// <returns>Equality result</returns>
public override bool Equals(object obj) => Equals(obj as Wnd32);
/// <summary>
/// Equality operator, uses the hWnd field
/// </summary>
/// <param name="other">Window to compare</param>
/// <returns>Equality result</returns>
public bool Equals(Wnd32 other) => !IsNull(other) && other != null && HWnd.Equals(other.HWnd);
/// <summary>
/// Equality operator, uses the hWnd field
/// </summary>
/// <returns>Equality result</returns>
public override int GetHashCode() => HWnd.GetHashCode();
/// <summary>
/// Equality operator, uses the hWnd field
/// </summary>
/// <param name="left">Window to compare</param>
/// <param name="right">Window to compare</param>
/// <returns>Equality result</returns>
public static bool operator ==(Wnd32 left, Wnd32 right) => !AreNull(left, right) && left.HWnd == right.HWnd;
/// <summary>
/// Equality operator, uses the hWnd field
/// </summary>
/// <param name="left">Window to compare</param>
/// <param name="right">Window to compare</param>
/// <returns>Equality result</returns>
public static bool operator !=(Wnd32 left, Wnd32 right) => AreNull(left, right) || left.HWnd != right.HWnd;
private static bool AreNull(params Wnd32[] windows) => windows.Any(IsNull);
private static bool IsNull(Wnd32 window)
{
try
{
window.ToString();
return false;
}
catch (NullReferenceException)
{
return true;
}
}
#endregion InstanceActions
#endregion Exposed
#region Internal
/// <summary>
/// The windows' handle
/// </summary>
public readonly IntPtr HWnd;
private static List<IntPtr> _windowHandles;
private static bool FilterCallback(IntPtr hWnd, int lParam)
{
StringBuilder sbTitle = new StringBuilder(1024);
user32.GetWindowText(hWnd, sbTitle, 1024);
_windowHandles.Add(hWnd);
return true;
}
private bool EnumWindow(IntPtr hWnd, IntPtr lParam)
{
GCHandle gcChildhandlesList = GCHandle.FromIntPtr(lParam);
if (gcChildhandlesList.Target == null) return false;
List<IntPtr> childHandles = gcChildhandlesList.Target as List<IntPtr>;
childHandles?.Add(hWnd);
return true;
}
#endregion Internal
}
}