111 lines
4.0 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|