Implemented: - Core: UTF-16 ReadString boundary/alignment fix, target bitness and process id on MemoryBase - function interception: PatchManager, DetourManager, InstructionAnalyzer, MainThreadDispatcher - Execution: BackgroundTaskExecutor, InProcessInvoker - High-level: Magic facade, RemotePointer, async wrappers - Discovery/external code loading/Window groundwork (PEB/TEB, pattern scanning, raw allocations, DLL external code loading, window/input) Tests: 180 passing, 4 integration/interactive tests skipped.
75 lines
2.6 KiB
C#
75 lines
2.6 KiB
C#
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.CompilerServices;
|
|
using WhiteMagic.Native;
|
|
|
|
namespace WhiteMagic.Windows;
|
|
|
|
/// <summary>
|
|
/// Factory for enumerating and locating <see cref="RemoteWindow"/> instances.
|
|
/// </summary>
|
|
public static class WindowFactory
|
|
{
|
|
/// <summary>Enumerates all top-level windows.</summary>
|
|
public static unsafe IEnumerable<RemoteWindow> GetWindows()
|
|
{
|
|
var handles = new List<IntPtr>();
|
|
GCHandle gch = GCHandle.Alloc(handles);
|
|
try
|
|
{
|
|
delegate* unmanaged[Stdcall]<IntPtr, IntPtr, int> callback = &EnumWindowsCallback;
|
|
NativeMethods.EnumWindows((nint)callback, GCHandle.ToIntPtr(gch));
|
|
}
|
|
finally
|
|
{
|
|
gch.Free();
|
|
}
|
|
|
|
return handles.Select(static h => new RemoteWindow(h));
|
|
}
|
|
|
|
/// <summary>Returns all top-level windows with the specified class name.</summary>
|
|
public static IEnumerable<RemoteWindow> GetWindowsByClassName(string className)
|
|
{
|
|
if (className is null)
|
|
throw new ArgumentNullException(nameof(className));
|
|
|
|
return GetWindows().Where(w => w.ClassName.Equals(className, StringComparison.Ordinal));
|
|
}
|
|
|
|
/// <summary>Returns all top-level windows owned by the specified process.</summary>
|
|
public static IEnumerable<RemoteWindow> GetWindowsByProcessId(int processId)
|
|
{
|
|
return GetWindows().Where(w => w.ProcessId == (uint)processId);
|
|
}
|
|
|
|
/// <summary>Returns the first top-level window with the specified class name.</summary>
|
|
public static RemoteWindow? GetWindowByClassName(string className)
|
|
{
|
|
return GetWindowsByClassName(className).FirstOrDefault();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the main window of a process. When <see cref="Process.MainWindowHandle"/>
|
|
/// is unavailable, falls back to the first enumerated window owned by the process.
|
|
/// </summary>
|
|
public static RemoteWindow? GetMainWindow(System.Diagnostics.Process process)
|
|
{
|
|
if (process is null)
|
|
throw new ArgumentNullException(nameof(process));
|
|
|
|
if (process.MainWindowHandle != IntPtr.Zero)
|
|
return new RemoteWindow(process.MainWindowHandle);
|
|
|
|
return GetWindowsByProcessId(process.Id).FirstOrDefault();
|
|
}
|
|
|
|
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
|
|
private static int EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
|
|
{
|
|
var handles = (List<IntPtr>)GCHandle.FromIntPtr(lParam).Target!;
|
|
handles.Add(hWnd);
|
|
return 1; // Continue enumeration.
|
|
}
|
|
}
|