Files
2026-10-24 13:02:19 +08:00

111 lines
4.0 KiB
C#

using anjianjingling.Native;
using System.Diagnostics;
using System.Text;
namespace anjianjingling.Window
{
public record WindowInfo(IntPtr Handle, string Title);
/// <summary>
/// 系统级窗口管理:枚举、查找、激活、启动程序、后台消息投递。
/// </summary>
public sealed class WindowManager
{
/// <summary>启动外部程序或打开文件。</summary>
public void Launch(string path, string? arguments = null)
{
var psi = new ProcessStartInfo
{
FileName = path,
UseShellExecute = true
};
if (!string.IsNullOrWhiteSpace(arguments)) psi.Arguments = arguments;
Process.Start(psi);
}
/// <summary>枚举所有可见且有标题的顶层窗口。</summary>
public List<WindowInfo> GetWindows()
{
var list = new List<WindowInfo>();
Win32.EnumWindows((h, _) =>
{
if (Win32.IsWindowVisible(h))
{
int len = Win32.GetWindowTextLength(h);
if (len > 0)
{
var sb = new StringBuilder(len + 1);
Win32.GetWindowText(h, sb, sb.Capacity);
var title = sb.ToString().Trim();
if (title.Length > 0) list.Add(new WindowInfo(h, title));
}
}
return true;
}, IntPtr.Zero);
return list;
}
/// <summary>按标题关键字(包含匹配,不区分大小写)查找窗口。</summary>
public IntPtr FindByTitle(string keyword)
{
foreach (var w in GetWindows())
{
if (w.Title.Contains(keyword, StringComparison.OrdinalIgnoreCase))
return w.Handle;
}
return IntPtr.Zero;
}
/// <summary>激活窗口并前置(最小化则恢复,使用 AttachThreadInput 解决前台锁定)。</summary>
public bool Activate(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero) return false;
if (Win32.IsIconic(hWnd)) Win32.ShowWindow(hWnd, Win32.SW_RESTORE);
uint foregroundThread = Win32.GetWindowThreadProcessId(Win32.GetForegroundWindow(), out _);
uint currentThread = Win32.GetCurrentThreadId();
if (foregroundThread != currentThread)
{
Win32.AttachThreadInput(currentThread, foregroundThread, true);
Win32.SetForegroundWindow(hWnd);
Win32.AttachThreadInput(currentThread, foregroundThread, false);
}
else
{
Win32.SetForegroundWindow(hWnd);
}
return true;
}
/// <summary>按标题激活。</summary>
public bool ActivateByTitle(string keyword)
{
var h = FindByTitle(keyword);
return h != IntPtr.Zero && Activate(h);
}
/// <summary>获取窗口屏幕矩形。</summary>
public Rectangle? GetBounds(IntPtr hWnd)
{
if (Win32.GetWindowRect(hWnd, out var r))
return new Rectangle(r.Left, r.Top, r.Width, r.Height);
return null;
}
/// <summary>
/// 后台点击:向窗口句柄 PostMessage 发送鼠标按下/弹起(客户区坐标),不抢焦点。
/// 注意:对 DirectX/自绘程序可能无效。
/// </summary>
public void PostClick(IntPtr hWnd, int clientX, int clientY, bool right = false)
{
var lp = Win32.MakeLParam(clientX, clientY);
uint down = right ? Win32.WM_RBUTTONDOWN : Win32.WM_LBUTTONDOWN;
uint up = right ? Win32.WM_RBUTTONUP : Win32.WM_LBUTTONUP;
Win32.PostMessage(hWnd, down, Win32.MK_LBUTTON, lp);
Thread.Sleep(60);
Win32.PostMessage(hWnd, up, IntPtr.Zero, lp);
}
}
}