From 0ee3647261dc1deaf57a60e687bd6a038078f309 Mon Sep 17 00:00:00 2001 From: fofolee Date: Sat, 18 Jan 2025 11:18:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E9=80=81=E6=B6=88=E6=81=AF=E3=80=81?= =?UTF-8?q?=E7=AA=97=E5=8F=A3=E7=AE=A1=E7=90=86=E7=BB=9F=E4=B8=80=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E5=8F=A5=E6=9F=84=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/lib/csharp/sendmessage.cs | 1580 ++++++++--------- plugin/lib/csharp/window.cs | 613 +++---- .../lib/quickcomposer/windows/automation.js | 5 +- .../lib/quickcomposer/windows/sendmessage.js | 41 +- plugin/lib/quickcomposer/windows/window.js | 68 +- src/js/composer/commands/windowsCommands.js | 16 +- 6 files changed, 1140 insertions(+), 1183 deletions(-) diff --git a/plugin/lib/csharp/sendmessage.cs b/plugin/lib/csharp/sendmessage.cs index 66789cc..484c110 100644 --- a/plugin/lib/csharp/sendmessage.cs +++ b/plugin/lib/csharp/sendmessage.cs @@ -10,867 +10,827 @@ using System.Web.Script.Serialization; public class AutomationTool { - #region Win32 API - [DllImport("user32.dll")] - private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + #region Win32 API + [DllImport("user32.dll")] + private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); - [DllImport("user32.dll")] - private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); + [DllImport("user32.dll")] + private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); - [DllImport("user32.dll")] - private static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); + [DllImport("user32.dll")] + private static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); - [DllImport("user32.dll")] - private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); + [DllImport("user32.dll")] + private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); - [DllImport("user32.dll")] - private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam); + [DllImport("user32.dll")] + private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam); - [DllImport("user32.dll")] - private static extern bool SetForegroundWindow(IntPtr hWnd); + [DllImport("user32.dll")] + private static extern bool SetForegroundWindow(IntPtr hWnd); - [DllImport("user32.dll")] - private static extern IntPtr GetFocus(); + [DllImport("user32.dll")] + private static extern IntPtr GetFocus(); - [DllImport("user32.dll")] - private static extern int EnumChildWindows(IntPtr hWnd, EnumWindowProc lpEnumFunc, IntPtr lParam); + [DllImport("user32.dll")] + private static extern int EnumChildWindows(IntPtr hWnd, EnumWindowProc lpEnumFunc, IntPtr lParam); - [DllImport("user32.dll")] - private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + [DllImport("user32.dll")] + private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); - [DllImport("user32.dll")] - private static extern IntPtr GetForegroundWindow(); + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); - [DllImport("user32.dll")] - private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + [DllImport("user32.dll")] + private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); - [DllImport("user32.dll")] - private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); + [DllImport("user32.dll")] + private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); - [DllImport("user32.dll")] - private static extern int GetWindowTextLength(IntPtr hWnd); + [DllImport("user32.dll")] + private static extern int GetWindowTextLength(IntPtr hWnd); - [DllImport("user32.dll")] - private static extern bool IsWindowVisible(IntPtr hWnd); + [DllImport("user32.dll")] + private static extern bool IsWindowVisible(IntPtr hWnd); - [DllImport("user32.dll")] - private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + [DllImport("user32.dll")] + private static extern bool IsWindow(IntPtr hWnd); - [DllImport("user32.dll")] - private static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint); + [DllImport("user32.dll")] + private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); - [DllImport("user32.dll")] - private static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); + [DllImport("user32.dll")] + private static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint); - private const int WM_KEYDOWN = 0x0100; - private const int WM_KEYUP = 0x0101; - private const int WM_CHAR = 0x0102; - private const int WM_SETTEXT = 0x000C; - private const int WM_LBUTTONDOWN = 0x0201; - private const int WM_LBUTTONUP = 0x0202; - private const int WM_RBUTTONDOWN = 0x0204; - private const int WM_RBUTTONUP = 0x0205; - private const int WM_LBUTTONDBLCLK = 0x0203; + [DllImport("user32.dll")] + private static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); - // 添加按键状态标志 - private const int KEYEVENTF_KEYDOWN = 0x0; - private const int KEYEVENTF_EXTENDEDKEY = 0x1; - private const int KEYEVENTF_KEYUP = 0x2; + private const int WM_KEYDOWN = 0x0100; + private const int WM_KEYUP = 0x0101; + private const int WM_CHAR = 0x0102; + private const int WM_SETTEXT = 0x000C; + private const int WM_LBUTTONDOWN = 0x0201; + private const int WM_LBUTTONUP = 0x0202; + private const int WM_RBUTTONDOWN = 0x0204; + private const int WM_RBUTTONUP = 0x0205; + private const int WM_LBUTTONDBLCLK = 0x0203; - private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam); - private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam); + private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); - [StructLayout(LayoutKind.Sequential)] - private struct RECT + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + [StructLayout(LayoutKind.Sequential)] + private struct POINT + { + public int X; + public int Y; + } + + [DllImport("user32.dll")] + private static extern IntPtr GetParent(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern IntPtr GetAncestor(IntPtr hwnd, int flags); + + private const int GA_ROOT = 2; + + // 添加工具栏相关的常量和结构体 + private const uint TB_BUTTONCOUNT = 0x0418; + private const uint TB_GETBUTTON = 0x0417; + private const uint TB_GETBUTTONTEXT = 0x042D; + private const uint TB_GETITEMRECT = 0x041D; + + [StructLayout(LayoutKind.Sequential)] + private struct TBBUTTON + { + public int iBitmap; + public int idCommand; + public byte fsState; + public byte fsStyle; + public byte bReserved1; + public byte bReserved2; + public IntPtr dwData; + public IntPtr iString; + } + + // 添加 SendMessage 重载 + [DllImport("user32.dll")] + private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, ref TBBUTTON lParam); + + [DllImport("user32.dll")] + private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, ref RECT lParam); + + [DllImport("user32.dll")] + private static extern uint MapVirtualKey(uint uCode, uint uMapType); + + [DllImport("user32.dll")] + private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); + + private const uint MAPVK_VK_TO_VSC = 0x00; + private const uint MAPVK_VSC_TO_VK = 0x01; + private const uint MAPVK_VK_TO_CHAR = 0x02; + private const uint MAPVK_VSC_TO_VK_EX = 0x03; + #endregion + + public static void Main(string[] args) + { + if (args.Length == 0 || args[0] == "-h" || args[0] == "--help") { - public int Left; - public int Top; - public int Right; - public int Bottom; + ShowHelp(); + return; } - [StructLayout(LayoutKind.Sequential)] - private struct POINT + try { - public int X; - public int Y; + List targetWindows = FindTargetWindows(args); + if (targetWindows.Count == 0) + { + throw new Exception("未找到目标窗口"); + } + + string type = GetArgumentValue(args, "-type"); + IntPtr targetHandle = targetWindows[0]; // 总是使用第一个窗口 + Dictionary operatedWindow = null; + + switch (type.ToLower()) + { + case "list": + // list 操作只获取第一个匹配窗口的控件树 + string filter = GetArgumentValue(args, "-filter"); + bool background = bool.Parse(GetArgumentValue(args, "-background") ?? "false"); + + // 只在非后台操作时激活窗口 + if (!background) + { + SetForegroundWindow(targetHandle); + Thread.Sleep(50); + } + + string treeJson = GetControlsTree(targetHandle, filter); + if (!string.IsNullOrEmpty(treeJson)) + { + Console.WriteLine("[" + treeJson + "]"); + } + return; // 直接返回,不输出窗口信息 + + case "keyboard": + HandleKeyboardOperation(targetHandle, args); + Console.WriteLine("true"); + break; + + case "mouse": + HandleMouseOperation(targetHandle, args); + Console.WriteLine("true"); + break; + + default: + throw new Exception("不支持的操作类型"); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(string.Format("Error: {0}", ex.Message)); + } + } + + private static List FindTargetWindows(string[] args) + { + List targetWindows = new List(); + string method = GetArgumentValue(args, "-method") ?? "title"; + string value = GetArgumentValue(args, "-window") ?? ""; + + // 如果是active方法,直接返回当前活动窗口 + if (method.ToLower() == "active") + { + targetWindows.Add(GetForegroundWindow()); + return targetWindows; } - [DllImport("user32.dll")] - private static extern IntPtr GetParent(IntPtr hWnd); - - [DllImport("user32.dll")] - private static extern IntPtr GetAncestor(IntPtr hwnd, int flags); - - private const int GA_ROOT = 2; - - // 添加工具栏相关的常量和结构体 - private const uint TB_BUTTONCOUNT = 0x0418; - private const uint TB_GETBUTTON = 0x0417; - private const uint TB_GETBUTTONTEXT = 0x042D; - private const uint TB_GETITEMRECT = 0x041D; - - [StructLayout(LayoutKind.Sequential)] - private struct TBBUTTON + // 如果是handle方法,直接返回指定句柄 + if (method.ToLower() == "handle") { - public int iBitmap; - public int idCommand; - public byte fsState; - public byte fsStyle; - public byte bReserved1; - public byte bReserved2; - public IntPtr dwData; - public IntPtr iString; + IntPtr handle = new IntPtr(long.Parse(value)); + if (!IsWindow(handle)) + { + throw new Exception("指定的句柄不是一个有效的窗口句柄"); + } + targetWindows.Add(handle); + return targetWindows; } - // 添加 SendMessage 重载 - [DllImport("user32.dll")] - private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, ref TBBUTTON lParam); - - [DllImport("user32.dll")] - private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, ref RECT lParam); - - [DllImport("user32.dll")] - private static extern uint MapVirtualKey(uint uCode, uint uMapType); - - [DllImport("user32.dll")] - private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); - - private const uint MAPVK_VK_TO_VSC = 0x00; - private const uint MAPVK_VSC_TO_VK = 0x01; - private const uint MAPVK_VK_TO_CHAR = 0x02; - private const uint MAPVK_VSC_TO_VK_EX = 0x03; - #endregion - - public static void Main(string[] args) + // 如果没有指定窗口值,返回空列表 + if (string.IsNullOrEmpty(value)) { - if (args.Length == 0 || args[0] == "-h" || args[0] == "--help") - { - ShowHelp(); - return; - } - - try - { - List targetWindows = FindTargetWindows(args); - if (targetWindows.Count == 0) - { - throw new Exception("未找到目标窗口"); - } - - string type = GetArgumentValue(args, "-type"); - IntPtr targetHandle = targetWindows[0]; // 总是使用第一个窗口 - Dictionary operatedWindow = null; - - switch (type.ToLower()) - { - case "inspect": - // inspect 操作只获取第一个匹配窗口的控件树 - string filter = GetArgumentValue(args, "-filter"); - bool background = bool.Parse(GetArgumentValue(args, "-background") ?? "false"); - - // 只在非后台操作时激活窗口 - if (!background) - { - SetForegroundWindow(targetHandle); - Thread.Sleep(50); - } - - string treeJson = GetControlsTree(targetHandle, filter); - if (!string.IsNullOrEmpty(treeJson)) - { - Console.WriteLine("[" + treeJson + "]"); - } - return; // 直接返回,不输出窗口信息 - - case "keyboard": - operatedWindow = HandleKeyboardOperation(targetHandle, args); - break; - - case "mouse": - operatedWindow = HandleMouseOperation(targetHandle, args); - break; - - default: - throw new Exception("不支持的操作类型"); - } - - // 输出操作的窗口信息 - if (operatedWindow != null) - { - var serializer = new JavaScriptSerializer(); - Console.WriteLine(serializer.Serialize(operatedWindow)); - } - } - catch (Exception ex) - { - Console.Error.WriteLine(string.Format("Error: {0}", ex.Message)); - } + return targetWindows; } - private static List FindTargetWindows(string[] args) + switch (method.ToLower()) { - List targetWindows = new List(); - string method = GetArgumentValue(args, "-method") ?? "title"; - string value = GetArgumentValue(args, "-window") ?? ""; - - // 如果是active方法,直接返回当前活动窗口 - if (method.ToLower() == "active") + case "process": + // 通过进程名查找 + var processes = System.Diagnostics.Process.GetProcessesByName(value); + foreach (var process in processes) { - targetWindows.Add(GetForegroundWindow()); - return targetWindows; + if (process.MainWindowHandle != IntPtr.Zero) + { + targetWindows.Add(process.MainWindowHandle); + } } + break; - // 如果是handle方法,直接返回指定句柄 - if (method.ToLower() == "handle") + case "class": + // 通过窗口类名查找 + EnumWindows((hwnd, param) => { - targetWindows.Add(new IntPtr(long.Parse(value))); - return targetWindows; - } + if (!IsWindowVisible(hwnd)) + { + return true; + } - // 如果没有指定窗口值,返回空列表 + StringBuilder className = new StringBuilder(256); + GetClassName(hwnd, className, className.Capacity); + string windowClassName = className.ToString(); + + if (windowClassName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) + { + targetWindows.Add(hwnd); + } + return true; + }, IntPtr.Zero); + break; + + case "title": + default: + // 通过窗口标题查找(支持模糊匹配) + EnumWindows((hwnd, param) => + { + StringBuilder title = new StringBuilder(256); + GetWindowText(hwnd, title, title.Capacity); + string windowTitle = title.ToString(); + + if (!string.IsNullOrEmpty(windowTitle) && + windowTitle.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) + { + targetWindows.Add(hwnd); + } + return true; + }, IntPtr.Zero); + break; + } + + if (targetWindows.Count == 0) + { + Console.WriteLine(string.Format("Error: 未找到匹配的窗口 (method={0}, value={1})", method, value)); + } + + return targetWindows; + } + + private static void HandleKeyboardOperation(IntPtr targetHandle, string[] args) + { + string control = GetArgumentValue(args, "-control"); + string action = GetArgumentValue(args, "-action"); + string value = GetArgumentValue(args, "-value"); + bool background = bool.Parse(GetArgumentValue(args, "-background") ?? "false"); + + // 如果指定了控件,递归查找控件句柄 + IntPtr controlHandle = IntPtr.Zero; + if (!string.IsNullOrEmpty(control)) + { + StringBuilder windowTitle = new StringBuilder(256); + GetWindowText(targetHandle, windowTitle, windowTitle.Capacity); + + controlHandle = FindControl(targetHandle, control); + if (controlHandle == IntPtr.Zero) + { + throw new Exception(string.Format("在窗口中未找到指定控件 (窗口句柄={0}, 标题=\"{1}\", 控件类名=\"{2}\")", + targetHandle.ToInt64(), windowTitle.ToString(), control)); + } + targetHandle = controlHandle; + } + + // 只在非后台操作时激活窗口 + if (!background) + { + SetForegroundWindow(targetHandle); + Thread.Sleep(50); + } + + switch (action.ToLower()) + { + case "keys": if (string.IsNullOrEmpty(value)) { - return targetWindows; + throw new Exception("发送按键需要指定 -value 参数"); } + SendKeys(targetHandle, value); + break; - switch (method.ToLower()) + case "text": + if (string.IsNullOrEmpty(value)) { - case "process": - // 通过进程名查找 - var processes = System.Diagnostics.Process.GetProcessesByName(value); - foreach (var process in processes) - { - if (process.MainWindowHandle != IntPtr.Zero) - { - targetWindows.Add(process.MainWindowHandle); - } - } - break; - - case "class": - // 通过窗口类名查找 - EnumWindows((hwnd, param) => - { - if (!IsWindowVisible(hwnd)) - { - return true; - } - - StringBuilder className = new StringBuilder(256); - GetClassName(hwnd, className, className.Capacity); - string windowClassName = className.ToString(); - - if (windowClassName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) - { - targetWindows.Add(hwnd); - } - return true; - }, IntPtr.Zero); - break; - - case "title": - default: - // 通过窗口标题查找(支持模糊匹配) - EnumWindows((hwnd, param) => - { - StringBuilder title = new StringBuilder(256); - GetWindowText(hwnd, title, title.Capacity); - string windowTitle = title.ToString(); - - if (!string.IsNullOrEmpty(windowTitle) && - windowTitle.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) - { - targetWindows.Add(hwnd); - } - return true; - }, IntPtr.Zero); - break; + throw new Exception("发送文本需要指定 -value 参数"); } + SendText(targetHandle, value); + break; - if (targetWindows.Count == 0) - { - Console.WriteLine(string.Format("Error: 未找到匹配的窗口 (method={0}, value={1})", method, value)); - } - - return targetWindows; + default: + throw new Exception("不支持的keyboard操作类型"); } - private static Dictionary HandleKeyboardOperation(IntPtr targetHandle, string[] args) + // 返回操作结果 + return; + } + + private static void HandleMouseOperation(IntPtr targetHandle, string[] args) + { + string control = GetArgumentValue(args, "-control"); + string controlText = GetArgumentValue(args, "-text"); + string action = GetArgumentValue(args, "-action"); + string position = GetArgumentValue(args, "-pos"); + bool background = bool.Parse(GetArgumentValue(args, "-background") ?? "false"); + + if (string.IsNullOrEmpty(action)) { - string control = GetArgumentValue(args, "-control"); - string action = GetArgumentValue(args, "-action"); - string value = GetArgumentValue(args, "-value"); - bool background = bool.Parse(GetArgumentValue(args, "-background") ?? "false"); - - // 如果指定了控件,递归查找控件句柄 - IntPtr controlHandle = IntPtr.Zero; - if (!string.IsNullOrEmpty(control)) - { - StringBuilder windowTitle = new StringBuilder(256); - GetWindowText(targetHandle, windowTitle, windowTitle.Capacity); - - controlHandle = FindControl(targetHandle, control); - if (controlHandle == IntPtr.Zero) - { - throw new Exception(string.Format("在窗口中未找到指定控件 (窗口句柄={0}, 标题=\"{1}\", 控件类名=\"{2}\")", - targetHandle.ToInt64(), windowTitle.ToString(), control)); - } - targetHandle = controlHandle; - } - - // 只在非后台操作时激活窗口 - if (!background) - { - SetForegroundWindow(targetHandle); - Thread.Sleep(50); - } - - switch (action.ToLower()) - { - case "keys": - if (string.IsNullOrEmpty(value)) - { - throw new Exception("发送按键需要指定 -value 参数"); - } - SendKeys(targetHandle, value); - break; - - case "text": - if (string.IsNullOrEmpty(value)) - { - throw new Exception("发送文本需要指定 -value 参数"); - } - SendText(targetHandle, value); - break; - - default: - throw new Exception("不支持的keyboard操作类型"); - } - - // 返回操作结果 - return GetBasicWindowInfo(targetHandle, controlHandle); + throw new Exception("mouse操作需要指定 -action 参数"); } - private static Dictionary HandleMouseOperation(IntPtr targetHandle, string[] args) + // 如果指定了控件类名和文本,查找匹配的控件 + IntPtr controlHandle = IntPtr.Zero; + if (!string.IsNullOrEmpty(control) || !string.IsNullOrEmpty(controlText)) { - string control = GetArgumentValue(args, "-control"); - string controlText = GetArgumentValue(args, "-text"); - string action = GetArgumentValue(args, "-action"); - string position = GetArgumentValue(args, "-pos"); - bool background = bool.Parse(GetArgumentValue(args, "-background") ?? "false"); + StringBuilder windowTitle = new StringBuilder(256); + GetWindowText(targetHandle, windowTitle, windowTitle.Capacity); - if (string.IsNullOrEmpty(action)) + controlHandle = FindControlByTextAndClass(targetHandle, controlText, control); + if (controlHandle == IntPtr.Zero) + { + throw new Exception(string.Format("在窗口中未找到指定控件 (窗口句柄={0}, 标题=\"{1}\", 控件类名=\"{2}\", 控件文本=\"{3}\")", + targetHandle.ToInt64(), windowTitle.ToString(), control ?? "", controlText ?? "")); + } + targetHandle = controlHandle; + } + + // 只在非后台操作时激活窗口 + if (!background) + { + SetForegroundWindow(targetHandle); + Thread.Sleep(50); + } + + // 获取点击坐标 + int x = 0, y = 0; + if (!string.IsNullOrEmpty(position)) + { + // 使用指定坐标 + string[] pos = position.Split(','); + if (pos.Length == 2) + { + x = int.Parse(pos[0]); + y = int.Parse(pos[1]); + } + } + else + { + // 如果没有指定坐标,点击控件中心 + RECT rect; + if (GetWindowRect(targetHandle, out rect)) + { + x = (rect.Right - rect.Left) / 2; + y = (rect.Bottom - rect.Top) / 2; + } + } + + int lParam = (y << 16) | (x & 0xFFFF); + + switch (action.ToLower()) + { + case "click": + SendMessage(targetHandle, WM_LBUTTONDOWN, 0, lParam); + SendMessage(targetHandle, WM_LBUTTONUP, 0, lParam); + break; + + case "rightclick": + SendMessage(targetHandle, WM_RBUTTONDOWN, 0, lParam); + SendMessage(targetHandle, WM_RBUTTONUP, 0, lParam); + break; + + case "doubleclick": + SendMessage(targetHandle, WM_LBUTTONDOWN, 0, lParam); + SendMessage(targetHandle, WM_LBUTTONUP, 0, lParam); + SendMessage(targetHandle, WM_LBUTTONDBLCLK, 0, lParam); + SendMessage(targetHandle, WM_LBUTTONUP, 0, lParam); + break; + + default: + Console.WriteLine("Error: 不支持的mouse操作类型"); + break; + } + + // 返回操作结果 + return; + } + + private static void SendKeys(IntPtr hWnd, string keys) + { + // 解析按键组合 + string[] keyArray = keys.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (string keyCombo in keyArray) + { + string[] modifiers = keyCombo.Trim().Split('+'); + List modifierKeys = new List(); + byte mainKey = 0; + + // 处理每个按键 + for (int i = 0; i < modifiers.Length; i++) + { + byte vKey = GetVirtualKeyCode(modifiers[i].Trim()); + if (i < modifiers.Length - 1) { - throw new Exception("mouse操作需要指定 -action 参数"); - } - - // 如果指定了控件类名和文本,查找匹配的控件 - IntPtr controlHandle = IntPtr.Zero; - if (!string.IsNullOrEmpty(control) || !string.IsNullOrEmpty(controlText)) - { - StringBuilder windowTitle = new StringBuilder(256); - GetWindowText(targetHandle, windowTitle, windowTitle.Capacity); - - controlHandle = FindControlByTextAndClass(targetHandle, controlText, control); - if (controlHandle == IntPtr.Zero) - { - throw new Exception(string.Format("在窗口中未找到指定控件 (窗口句柄={0}, 标题=\"{1}\", 控件类名=\"{2}\", 控件文本=\"{3}\")", - targetHandle.ToInt64(), windowTitle.ToString(), control ?? "", controlText ?? "")); - } - targetHandle = controlHandle; - } - - // 只在非后台操作时激活窗口 - if (!background) - { - SetForegroundWindow(targetHandle); - Thread.Sleep(50); - } - - // 获取点击坐标 - int x = 0, y = 0; - if (!string.IsNullOrEmpty(position)) - { - // 使用指定坐标 - string[] pos = position.Split(','); - if (pos.Length == 2) - { - x = int.Parse(pos[0]); - y = int.Parse(pos[1]); - } + modifierKeys.Add(vKey); } else { - // 如果没有指定坐标,点击控件中心 - RECT rect; - if (GetWindowRect(targetHandle, out rect)) - { - x = (rect.Right - rect.Left) / 2; - y = (rect.Bottom - rect.Top) / 2; - } + mainKey = vKey; } + } - int lParam = (y << 16) | (x & 0xFFFF); + // 按下修饰键 + foreach (byte modifier in modifierKeys) + { + // 获取扫描码 + uint scanCode = MapVirtualKey((uint)modifier, MAPVK_VK_TO_VSC); - switch (action.ToLower()) + // 构造 lParam + int lParamDown = 0x00000001 | // repeat count = 1 + ((int)scanCode << 16) | // scan code + (0x1 << 24); // extended key for modifiers + + PostMessage(hWnd, WM_KEYDOWN, modifier, lParamDown); + Thread.Sleep(10); // 短暂延迟确保修饰键被正确识别 + } + + // 发送主键 + if (mainKey > 0) + { + // 获取主键的扫描码 + uint scanCode = MapVirtualKey((uint)mainKey, MAPVK_VK_TO_VSC); + + // 构造主键的 lParam + int lParamDown = 0x00000001 | // repeat count = 1 + ((int)scanCode << 16); // scan code + + int lParamUp = 0x00000001 | // repeat count = 1 + ((int)scanCode << 16) | // scan code + (0xC0 << 24); // key up + previous key state + + // 发送按键按下 + PostMessage(hWnd, WM_KEYDOWN, mainKey, lParamDown); + Thread.Sleep(10); // 短暂延迟 + + // 发送按键释放 + PostMessage(hWnd, WM_KEYUP, mainKey, lParamUp); + Thread.Sleep(10); // 短暂延迟 + } + + // 释放修饰键(反序释放) + for (int i = modifierKeys.Count - 1; i >= 0; i--) + { + byte modifier = modifierKeys[i]; + uint scanCode = MapVirtualKey((uint)modifier, MAPVK_VK_TO_VSC); + + // 构造释放修饰键的 lParam + int lParamUp = 0x00000001 | // repeat count = 1 + ((int)scanCode << 16) | // scan code + (0xC1 << 24); // extended key + key up + previous key state + + PostMessage(hWnd, WM_KEYUP, modifier, lParamUp); + Thread.Sleep(10); // 短暂延迟 + } + + // 如果有多个按键组合,等待一下 + if (keyArray.Length > 1) + { + Thread.Sleep(50); + } + } + } + + private static void SendText(IntPtr hWnd, string text) + { + StringBuilder sb = new StringBuilder(text); + SendMessage(hWnd, WM_SETTEXT, 0, sb); + } + + private static string GetArgumentValue(string[] args, string key) + { + for (int i = 0; i < args.Length - 1; i++) + { + if (args[i].Equals(key, StringComparison.OrdinalIgnoreCase)) + { + return args[i + 1]; + } + } + return null; + } + + private static byte GetVirtualKeyCode(string key) + { + switch (key.ToUpper()) + { + // 修饰键 + case "CTRL": + case "^": return 0x11; // VK_CONTROL + case "ALT": + case "%": return 0x12; // VK_MENU + case "SHIFT": return 0x10; // VK_SHIFT + + // 特殊按键 + case "{BACKSPACE}": + case "{BS}": + case "{BKSP}": return 0x08; // VK_BACK + case "{BREAK}": return 0x03; // VK_CANCEL + case "{CAPSLOCK}": return 0x14; // VK_CAPITAL + case "{DELETE}": + case "{DEL}": return 0x2E; // VK_DELETE + case "{DOWN}": return 0x28; // VK_DOWN + case "{END}": return 0x23; // VK_END + case "{ENTER}": + case "{RETURN}": return 0x0D; // VK_RETURN + case "{ESC}": return 0x1B; // VK_ESCAPE + case "{HELP}": return 0x2F; // VK_HELP + case "{HOME}": return 0x24; // VK_HOME + case "{INSERT}": + case "{INS}": return 0x2D; // VK_INSERT + case "{LEFT}": return 0x25; // VK_LEFT + case "{NUMLOCK}": return 0x90; // VK_NUMLOCK + case "{PGDN}": return 0x22; // VK_NEXT + case "{PGUP}": return 0x21; // VK_PRIOR + case "{PRTSC}": return 0x2C; // VK_SNAPSHOT + case "{RIGHT}": return 0x27; // VK_RIGHT + case "{SCROLLLOCK}": return 0x91; // VK_SCROLL + case "{TAB}": return 0x09; // VK_TAB + case "{UP}": return 0x26; // VK_UP + + // 功能键 F1-F16 + case "{F1}": return 0x70; + case "{F2}": return 0x71; + case "{F3}": return 0x72; + case "{F4}": return 0x73; + case "{F5}": return 0x74; + case "{F6}": return 0x75; + case "{F7}": return 0x76; + case "{F8}": return 0x77; + case "{F9}": return 0x78; + case "{F10}": return 0x79; + case "{F11}": return 0x7A; + case "{F12}": return 0x7B; + + // 数字键盘 + case "{ADD}": return 0x6B; // VK_ADD + case "{SUBTRACT}": return 0x6D; // VK_SUBTRACT + case "{MULTIPLY}": return 0x6A; // VK_MULTIPLY + case "{DIVIDE}": return 0x6F; // VK_DIVIDE + case "{NUMPAD0}": return 0x60; // VK_NUMPAD0 + case "{NUMPAD1}": return 0x61; + case "{NUMPAD2}": return 0x62; + case "{NUMPAD3}": return 0x63; + case "{NUMPAD4}": return 0x64; + case "{NUMPAD5}": return 0x65; + case "{NUMPAD6}": return 0x66; + case "{NUMPAD7}": return 0x67; + case "{NUMPAD8}": return 0x68; + case "{NUMPAD9}": return 0x69; + + default: + if (key.Length == 1) { - case "click": - SendMessage(targetHandle, WM_LBUTTONDOWN, 0, lParam); - SendMessage(targetHandle, WM_LBUTTONUP, 0, lParam); - break; - - case "rightclick": - SendMessage(targetHandle, WM_RBUTTONDOWN, 0, lParam); - SendMessage(targetHandle, WM_RBUTTONUP, 0, lParam); - break; - - case "doubleclick": - SendMessage(targetHandle, WM_LBUTTONDOWN, 0, lParam); - SendMessage(targetHandle, WM_LBUTTONUP, 0, lParam); - SendMessage(targetHandle, WM_LBUTTONDBLCLK, 0, lParam); - SendMessage(targetHandle, WM_LBUTTONUP, 0, lParam); - break; - - default: - Console.WriteLine("Error: 不支持的mouse操作类型"); - break; + return (byte)key.ToUpper()[0]; } + throw new ArgumentException(string.Format("不支持的按键: {0}", key)); + } + } - // 返回操作结果 - return GetBasicWindowInfo(targetHandle, controlHandle); + private static IntPtr FindControlRecursive(IntPtr parentHandle, string targetClassName) + { + if (string.IsNullOrEmpty(targetClassName)) + return IntPtr.Zero; + + IntPtr foundHandle = IntPtr.Zero; + List childHandles = new List(); + + // 枚举所有子窗口 + EnumWindowProc childProc = new EnumWindowProc((handle, param) => + { + // 获取类名 + StringBuilder classNameBuffer = new StringBuilder(256); + GetClassName(handle, classNameBuffer, classNameBuffer.Capacity); + + // 检查是否匹配 + if (classNameBuffer.ToString().Equals(targetClassName, StringComparison.OrdinalIgnoreCase)) + { + foundHandle = handle; + return false; // 找到后停止枚举 + } + + childHandles.Add(handle); + return true; + }); + + EnumChildWindows(parentHandle, childProc, IntPtr.Zero); + + // 如果在当前层级没找到,递归查找子窗口 + if (foundHandle == IntPtr.Zero) + { + foreach (IntPtr childHandle in childHandles) + { + foundHandle = FindControlRecursive(childHandle, targetClassName); + if (foundHandle != IntPtr.Zero) + break; + } } - private static void SendKeys(IntPtr hWnd, string keys) + return foundHandle; + } + + // 修改 HandleKeyboardOperation 和 HandleMouseOperation 中查找控件的部分 + private static IntPtr FindControl(IntPtr parentHandle, string controlClass) + { + // 先尝试直接查找 + IntPtr hControl = FindWindowEx(parentHandle, IntPtr.Zero, controlClass, null); + if (hControl != IntPtr.Zero) + return hControl; + + // 如果直接查找失败,进行递归查找 + return FindControlRecursive(parentHandle, controlClass); + } + + private static IntPtr FindControlByTextAndClass(IntPtr parentHandle, string controlText, string className) + { + if (string.IsNullOrEmpty(controlText) && string.IsNullOrEmpty(className)) + return IntPtr.Zero; + + List matchedControls = new List(); + Queue searchQueue = new Queue(); + searchQueue.Enqueue(parentHandle); + + while (searchQueue.Count > 0) { - // 解析按键组合 - string[] keyArray = keys.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + IntPtr currentHandle = searchQueue.Dequeue(); + List children = new List(); - foreach (string keyCombo in keyArray) + // 枚举当前层级的子窗口 + EnumWindowProc childProc = new EnumWindowProc((handle, param) => + { + bool match = true; + + // 检查类名(如果指定) + if (!string.IsNullOrEmpty(className)) { - string[] modifiers = keyCombo.Trim().Split('+'); - List modifierKeys = new List(); - byte mainKey = 0; - - // 处理每个按键 - for (int i = 0; i < modifiers.Length; i++) - { - byte vKey = GetVirtualKeyCode(modifiers[i].Trim()); - if (i < modifiers.Length - 1) - { - modifierKeys.Add(vKey); - } - else - { - mainKey = vKey; - } - } - - // 按下修饰键 - foreach (byte modifier in modifierKeys) - { - // 获取扫描码 - uint scanCode = MapVirtualKey((uint)modifier, MAPVK_VK_TO_VSC); - - // 构造 lParam - int lParamDown = 0x00000001 | // repeat count = 1 - ((int)scanCode << 16) | // scan code - (0x1 << 24); // extended key for modifiers - - PostMessage(hWnd, WM_KEYDOWN, modifier, lParamDown); - Thread.Sleep(10); // 短暂延迟确保修饰键被正确识别 - } - - // 发送主键 - if (mainKey > 0) - { - // 获取主键的扫描码 - uint scanCode = MapVirtualKey((uint)mainKey, MAPVK_VK_TO_VSC); - - // 构造主键的 lParam - int lParamDown = 0x00000001 | // repeat count = 1 - ((int)scanCode << 16); // scan code - - int lParamUp = 0x00000001 | // repeat count = 1 - ((int)scanCode << 16) | // scan code - (0xC0 << 24); // key up + previous key state - - // 发送按键按下 - PostMessage(hWnd, WM_KEYDOWN, mainKey, lParamDown); - Thread.Sleep(10); // 短暂延迟 - - // 发送按键释放 - PostMessage(hWnd, WM_KEYUP, mainKey, lParamUp); - Thread.Sleep(10); // 短暂延迟 - } - - // 释放修饰键(反序释放) - for (int i = modifierKeys.Count - 1; i >= 0; i--) - { - byte modifier = modifierKeys[i]; - uint scanCode = MapVirtualKey((uint)modifier, MAPVK_VK_TO_VSC); - - // 构造释放修饰键的 lParam - int lParamUp = 0x00000001 | // repeat count = 1 - ((int)scanCode << 16) | // scan code - (0xC1 << 24); // extended key + key up + previous key state - - PostMessage(hWnd, WM_KEYUP, modifier, lParamUp); - Thread.Sleep(10); // 短暂延迟 - } - - // 如果有多个按键组合,等待一下 - if (keyArray.Length > 1) - { - Thread.Sleep(50); - } + StringBuilder classNameBuffer = new StringBuilder(256); + GetClassName(handle, classNameBuffer, classNameBuffer.Capacity); + if (!className.Equals(classNameBuffer.ToString(), StringComparison.OrdinalIgnoreCase)) + { + match = false; + } } + + // 检查控件文本(如果指定) + if (match && !string.IsNullOrEmpty(controlText)) + { + StringBuilder textBuffer = new StringBuilder(256); + GetWindowText(handle, textBuffer, textBuffer.Capacity); + string windowText = textBuffer.ToString(); + if (!windowText.Contains(controlText)) + { + match = false; + } + } + + // 检查控件是否可见 + if (match && !IsWindowVisible(handle)) + { + match = false; + } + + if (match) + { + matchedControls.Add(handle); + } + + // 将子窗口加入搜索队列 + children.Add(handle); + return true; + }); + + EnumChildWindows(currentHandle, childProc, IntPtr.Zero); + + // 将所有子窗口加入搜索队列 + foreach (IntPtr child in children) + { + searchQueue.Enqueue(child); + } } - private static void SendText(IntPtr hWnd, string text) + if (matchedControls.Count == 0) { - StringBuilder sb = new StringBuilder(text); - SendMessage(hWnd, WM_SETTEXT, 0, sb); + return IntPtr.Zero; + } + else if (matchedControls.Count > 1) + { + foreach (IntPtr handle in matchedControls) + { + StringBuilder text = new StringBuilder(256); + GetWindowText(handle, text, text.Capacity); + StringBuilder classBuffer = new StringBuilder(256); + GetClassName(handle, classBuffer, classBuffer.Capacity); + Console.WriteLine(string.Format("0x{0:X} - Class: {1}, Text: {2}", + handle.ToInt64(), classBuffer, text)); + } } - private static string GetArgumentValue(string[] args, string key) + return matchedControls[0]; + } + + private static string GetControlsTree(IntPtr parentHandle, string filter, int depth = 0) + { + if (parentHandle == IntPtr.Zero) + return "{}"; + + StringBuilder json = new StringBuilder(); + json.Append("{"); + + // 获取窗口信息 + StringBuilder title = new StringBuilder(256); + StringBuilder className = new StringBuilder(256); + GetWindowText(parentHandle, title, title.Capacity); + GetClassName(parentHandle, className, className.Capacity); + + // 获取窗口位置和大小 + RECT windowRect; + GetWindowRect(parentHandle, out windowRect); + + bool isVisible = IsWindowVisible(parentHandle); + + // 检查当前节点是否匹配过滤条件 + bool matchFilter = true; + if (!string.IsNullOrEmpty(filter)) { - for (int i = 0; i < args.Length - 1; i++) - { - if (args[i].Equals(key, StringComparison.OrdinalIgnoreCase)) - { - return args[i + 1]; - } - } - return null; + matchFilter = + title.ToString().IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0 || + className.ToString().IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0; } - private static byte GetVirtualKeyCode(string key) + // 添加节点信息 - 将句柄改为十进制格式 + json.AppendFormat( + "\"handle\":\"{0}\",\"class\":\"{1}\",\"text\":\"{2}\",\"visible\":{3},\"location\":{{\"x\":{4},\"y\":{5},\"width\":{6},\"height\":{7}}},\"matched\":{8},\"children\":[", + parentHandle.ToInt64(), // 直接使用十进制格式 + className.ToString().Replace("\"", "\\\""), + title.ToString().Replace("\"", "\\\""), + isVisible.ToString().ToLower(), + windowRect.Left, + windowRect.Top, + windowRect.Right - windowRect.Left, + windowRect.Bottom - windowRect.Top, + matchFilter.ToString().ToLower() + ); + + // 递归获取子控件树 + List childJsons = new List(); + + EnumChildWindows(parentHandle, (hwnd, param) => { - switch (key.ToUpper()) - { - // 修饰键 - case "CTRL": - case "^": return 0x11; // VK_CONTROL - case "ALT": - case "%": return 0x12; // VK_MENU - case "SHIFT": return 0x10; // VK_SHIFT + string childJson = GetControlsTree(hwnd, filter, depth + 1); + if (!string.IsNullOrEmpty(childJson) && childJson != "{}") + { + childJsons.Add(childJson); + } + return true; + }, IntPtr.Zero); - // 特殊按键 - case "{BACKSPACE}": - case "{BS}": - case "{BKSP}": return 0x08; // VK_BACK - case "{BREAK}": return 0x03; // VK_CANCEL - case "{CAPSLOCK}": return 0x14; // VK_CAPITAL - case "{DELETE}": - case "{DEL}": return 0x2E; // VK_DELETE - case "{DOWN}": return 0x28; // VK_DOWN - case "{END}": return 0x23; // VK_END - case "{ENTER}": - case "{RETURN}": return 0x0D; // VK_RETURN - case "{ESC}": return 0x1B; // VK_ESCAPE - case "{HELP}": return 0x2F; // VK_HELP - case "{HOME}": return 0x24; // VK_HOME - case "{INSERT}": - case "{INS}": return 0x2D; // VK_INSERT - case "{LEFT}": return 0x25; // VK_LEFT - case "{NUMLOCK}": return 0x90; // VK_NUMLOCK - case "{PGDN}": return 0x22; // VK_NEXT - case "{PGUP}": return 0x21; // VK_PRIOR - case "{PRTSC}": return 0x2C; // VK_SNAPSHOT - case "{RIGHT}": return 0x27; // VK_RIGHT - case "{SCROLLLOCK}": return 0x91; // VK_SCROLL - case "{TAB}": return 0x09; // VK_TAB - case "{UP}": return 0x26; // VK_UP - - // 功能键 F1-F16 - case "{F1}": return 0x70; - case "{F2}": return 0x71; - case "{F3}": return 0x72; - case "{F4}": return 0x73; - case "{F5}": return 0x74; - case "{F6}": return 0x75; - case "{F7}": return 0x76; - case "{F8}": return 0x77; - case "{F9}": return 0x78; - case "{F10}": return 0x79; - case "{F11}": return 0x7A; - case "{F12}": return 0x7B; - - // 数字键盘 - case "{ADD}": return 0x6B; // VK_ADD - case "{SUBTRACT}": return 0x6D; // VK_SUBTRACT - case "{MULTIPLY}": return 0x6A; // VK_MULTIPLY - case "{DIVIDE}": return 0x6F; // VK_DIVIDE - case "{NUMPAD0}": return 0x60; // VK_NUMPAD0 - case "{NUMPAD1}": return 0x61; - case "{NUMPAD2}": return 0x62; - case "{NUMPAD3}": return 0x63; - case "{NUMPAD4}": return 0x64; - case "{NUMPAD5}": return 0x65; - case "{NUMPAD6}": return 0x66; - case "{NUMPAD7}": return 0x67; - case "{NUMPAD8}": return 0x68; - case "{NUMPAD9}": return 0x69; - - default: - if (key.Length == 1) - { - return (byte)key.ToUpper()[0]; - } - throw new ArgumentException(string.Format("不支持的按键: {0}", key)); - } + // 添加子节点JSON + if (childJsons.Count > 0) + { + json.Append(string.Join(",", childJsons)); } - private static IntPtr FindControlRecursive(IntPtr parentHandle, string targetClassName) - { - if (string.IsNullOrEmpty(targetClassName)) - return IntPtr.Zero; + json.Append("]}"); - IntPtr foundHandle = IntPtr.Zero; - List childHandles = new List(); + return json.ToString(); + } - // 枚举所有子窗口 - EnumWindowProc childProc = new EnumWindowProc((handle, param) => - { - // 获取类名 - StringBuilder classNameBuffer = new StringBuilder(256); - GetClassName(handle, classNameBuffer, classNameBuffer.Capacity); - - // 检查是否匹配 - if (classNameBuffer.ToString().Equals(targetClassName, StringComparison.OrdinalIgnoreCase)) - { - foundHandle = handle; - return false; // 找到后停止枚举 - } - - childHandles.Add(handle); - return true; - }); - - EnumChildWindows(parentHandle, childProc, IntPtr.Zero); - - // 如果在当前层级没找到,递归查找子窗口 - if (foundHandle == IntPtr.Zero) - { - foreach (IntPtr childHandle in childHandles) - { - foundHandle = FindControlRecursive(childHandle, targetClassName); - if (foundHandle != IntPtr.Zero) - break; - } - } - - return foundHandle; - } - - // 修改 HandleKeyboardOperation 和 HandleMouseOperation 中查找控件的部分 - private static IntPtr FindControl(IntPtr parentHandle, string controlClass) - { - // 先尝试直接查找 - IntPtr hControl = FindWindowEx(parentHandle, IntPtr.Zero, controlClass, null); - if (hControl != IntPtr.Zero) - return hControl; - - // 如果直接查找失败,进行递归查找 - return FindControlRecursive(parentHandle, controlClass); - } - - private static IntPtr FindControlByTextAndClass(IntPtr parentHandle, string controlText, string className) - { - if (string.IsNullOrEmpty(controlText) && string.IsNullOrEmpty(className)) - return IntPtr.Zero; - - List matchedControls = new List(); - Queue searchQueue = new Queue(); - searchQueue.Enqueue(parentHandle); - - while (searchQueue.Count > 0) - { - IntPtr currentHandle = searchQueue.Dequeue(); - List children = new List(); - - // 枚举当前层级的子窗口 - EnumWindowProc childProc = new EnumWindowProc((handle, param) => - { - bool match = true; - - // 检查类名(如果指定) - if (!string.IsNullOrEmpty(className)) - { - StringBuilder classNameBuffer = new StringBuilder(256); - GetClassName(handle, classNameBuffer, classNameBuffer.Capacity); - if (!className.Equals(classNameBuffer.ToString(), StringComparison.OrdinalIgnoreCase)) - { - match = false; - } - } - - // 检查控件文本(如果指定) - if (match && !string.IsNullOrEmpty(controlText)) - { - StringBuilder textBuffer = new StringBuilder(256); - GetWindowText(handle, textBuffer, textBuffer.Capacity); - string windowText = textBuffer.ToString(); - if (!windowText.Contains(controlText)) - { - match = false; - } - } - - // 检查控件是否可见 - if (match && !IsWindowVisible(handle)) - { - match = false; - } - - if (match) - { - matchedControls.Add(handle); - } - - // 将子窗口加入搜索队列 - children.Add(handle); - return true; - }); - - EnumChildWindows(currentHandle, childProc, IntPtr.Zero); - - // 将所有子窗口加入搜索队列 - foreach (IntPtr child in children) - { - searchQueue.Enqueue(child); - } - } - - if (matchedControls.Count == 0) - { - return IntPtr.Zero; - } - else if (matchedControls.Count > 1) - { - Console.WriteLine("Warning: 找到多个匹配控件:"); - foreach (IntPtr handle in matchedControls) - { - StringBuilder text = new StringBuilder(256); - GetWindowText(handle, text, text.Capacity); - StringBuilder classBuffer = new StringBuilder(256); - GetClassName(handle, classBuffer, classBuffer.Capacity); - Console.WriteLine(string.Format("0x{0:X} - Class: {1}, Text: {2}", - handle.ToInt64(), classBuffer, text)); - } - } - - return matchedControls[0]; - } - - private static void HandleInspectOperation(string[] args) - { - string filter = GetArgumentValue(args, "-filter"); - bool background = bool.Parse(GetArgumentValue(args, "-background") ?? "false"); - - var targetWindows = FindTargetWindows(args); - if (targetWindows.Count == 0) - { - return; - } - - // 创建一个列表存储所有窗口的控件树数组 - List allWindowTrees = new List(); - - foreach (IntPtr hWnd in targetWindows) - { - // 只在非后台操作时激活窗口 - if (!background) - { - SetForegroundWindow(hWnd); - Thread.Sleep(50); - } - - // 获取当前窗口的控件树并添加到列表 - // 注意:每个控件树已经是一个数组 - string treeJson = GetControlsTree(hWnd, filter); - if (!string.IsNullOrEmpty(treeJson) && treeJson != "{}") - { - // 将每个控件树作为一个数组元素添加 - allWindowTrees.Add("[" + treeJson + "]"); - } - } - - // 将所有窗口的控件树数组合并成一个大数组 - Console.WriteLine("[" + string.Join(",", allWindowTrees) + "]"); - } - - private static string GetControlsTree(IntPtr parentHandle, string filter, int depth = 0) - { - if (parentHandle == IntPtr.Zero) - return "{}"; - - StringBuilder json = new StringBuilder(); - json.Append("{"); - - // 获取窗口信息 - StringBuilder title = new StringBuilder(256); - StringBuilder className = new StringBuilder(256); - GetWindowText(parentHandle, title, title.Capacity); - GetClassName(parentHandle, className, className.Capacity); - - // 获取窗口位置和大小 - RECT windowRect; - GetWindowRect(parentHandle, out windowRect); - - bool isVisible = IsWindowVisible(parentHandle); - - // 检查当前节点是否匹配过滤条件 - bool matchFilter = true; - if (!string.IsNullOrEmpty(filter)) - { - matchFilter = - title.ToString().IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0 || - className.ToString().IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0; - } - - // 添加节点信息 - 将句柄改为十进制格式 - json.AppendFormat( - "\"handle\":\"{0}\",\"class\":\"{1}\",\"text\":\"{2}\",\"visible\":{3},\"location\":{{\"x\":{4},\"y\":{5},\"width\":{6},\"height\":{7}}},\"matched\":{8},\"children\":[", - parentHandle.ToInt64(), // 直接使用十进制格式 - className.ToString().Replace("\"", "\\\""), - title.ToString().Replace("\"", "\\\""), - isVisible.ToString().ToLower(), - windowRect.Left, - windowRect.Top, - windowRect.Right - windowRect.Left, - windowRect.Bottom - windowRect.Top, - matchFilter.ToString().ToLower() - ); - - // 递归获取子控件树 - List childJsons = new List(); - - EnumChildWindows(parentHandle, (hwnd, param) => - { - string childJson = GetControlsTree(hwnd, filter, depth + 1); - if (!string.IsNullOrEmpty(childJson) && childJson != "{}") - { - childJsons.Add(childJson); - } - return true; - }, IntPtr.Zero); - - // 添加子节点JSON - if (childJsons.Count > 0) - { - json.Append(string.Join(",", childJsons)); - } - - json.Append("]}"); - - return json.ToString(); - } - - private static void ShowHelp() - { - string help = @" + private static void ShowHelp() + { + string help = @" Windows 界面自动化工具使用说明 ========================== @@ -881,7 +841,7 @@ sendmessage.exe -type <操作类型> [参数...] -------- 1. keyboard - 键盘操作 2. mouse - 鼠标操作 -3. inspect - 获取控件树 +3. list - 获取控件树 通用参数: -------- @@ -927,7 +887,7 @@ sendmessage.exe -type <操作类型> [参数...] sendmessage.exe -type keyboard -action text -window ""记事本"" -value ""Hello"" -background 5. 获取窗口控件树: - sendmessage.exe -type inspect -window ""记事本"" -filter ""button"" + sendmessage.exe -type list -window ""记事本"" -filter ""button"" 6. 使用句柄查找窗口: sendmessage.exe -type keyboard -method handle -window ""0x12345"" -value ""Hello"" @@ -945,7 +905,7 @@ sendmessage.exe -type <操作类型> [参数...] 返回值: ------ 1. 均为JSON格式 -2. inspect操作返回控件树信息 +2. list操作返回控件树信息 3. 其他操作返回操作的控件信息及其所在窗口信息 4. 失败均抛出异常 @@ -954,58 +914,6 @@ sendmessage.exe -type <操作类型> [参数...] 1. 窗口标题、类名支持模糊匹配,active方式可不提供window参数 2. 所有操作都只会处理第一个匹配的窗口 "; - Console.WriteLine(help); - } - - private static Dictionary GetBasicWindowInfo(IntPtr hwnd, IntPtr controlHandle = default(IntPtr)) - { - var info = new Dictionary(); - - // 获取窗口信息 - StringBuilder title = new StringBuilder(256); - StringBuilder className = new StringBuilder(256); - GetWindowText(hwnd, title, title.Capacity); - GetClassName(hwnd, className, className.Capacity); - - int processId = 0; - GetWindowThreadProcessId(hwnd, out processId); - string processName = ""; - try - { - var process = System.Diagnostics.Process.GetProcessById(processId); - processName = process.ProcessName; - } - catch { } - - // 窗口信息放在 window 键下 - info["window"] = new Dictionary - { - { "handle", hwnd.ToInt64() }, - { "title", title.ToString() }, - { "class", className.ToString() }, - { "process", processName } - }; - - // 如果有控件信息,添加到顶层 - if (controlHandle != IntPtr.Zero) - { - StringBuilder controlTitle = new StringBuilder(256); - StringBuilder controlClassName = new StringBuilder(256); - GetWindowText(controlHandle, controlTitle, controlTitle.Capacity); - GetClassName(controlHandle, controlClassName, controlClassName.Capacity); - - info["handle"] = controlHandle.ToInt64(); - info["title"] = controlTitle.ToString(); - info["class"] = controlClassName.ToString(); - } - else - { - // 如果没有控件,设置为 null - info["handle"] = null; - info["title"] = null; - info["class"] = null; - } - - return info; - } + Console.WriteLine(help); + } } diff --git a/plugin/lib/csharp/window.cs b/plugin/lib/csharp/window.cs index 2bdf3df..18eeeb8 100644 --- a/plugin/lib/csharp/window.cs +++ b/plugin/lib/csharp/window.cs @@ -7,333 +7,340 @@ using System.Collections.Generic; public class WindowManager { - #region Win32 API - [DllImport("user32.dll")] - private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + #region Win32 API + [DllImport("user32.dll")] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); - [DllImport("user32.dll")] - private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); + [DllImport("user32.dll")] + private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); - [DllImport("user32.dll")] - private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + [DllImport("user32.dll")] + private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); - [DllImport("user32.dll")] - private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + [DllImport("user32.dll")] + private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); - [DllImport("user32.dll")] - private static extern bool SetForegroundWindow(IntPtr hWnd); + [DllImport("user32.dll")] + private static extern bool SetForegroundWindow(IntPtr hWnd); - [DllImport("user32.dll")] - private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); + [DllImport("user32.dll")] + private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); - [DllImport("user32.dll")] - private static extern int GetWindowLong(IntPtr hWnd, int nIndex); + [DllImport("user32.dll")] + private static extern int GetWindowLong(IntPtr hWnd, int nIndex); - [DllImport("user32.dll")] - private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + [DllImport("user32.dll")] + private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); - [DllImport("user32.dll")] - private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + [DllImport("user32.dll")] + private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); - [DllImport("user32.dll")] - private static extern IntPtr GetForegroundWindow(); + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); - [DllImport("user32.dll")] - private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + [DllImport("user32.dll")] + private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); - [DllImport("user32.dll")] - private static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); + [DllImport("user32.dll")] + private static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); - [DllImport("user32.dll")] - private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); + [DllImport("user32.dll")] + private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); - [DllImport("user32.dll")] - private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + [DllImport("user32.dll")] + private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); - [DllImport("user32.dll")] - private static extern bool IsWindowVisible(IntPtr hWnd); + [DllImport("user32.dll")] + private static extern bool IsWindowVisible(IntPtr hWnd); - [DllImport("user32.dll")] - private static extern bool IsIconic(IntPtr hWnd); + [DllImport("user32.dll")] + private static extern bool IsIconic(IntPtr hWnd); - [DllImport("user32.dll")] - private static extern bool IsZoomed(IntPtr hWnd); + [DllImport("user32.dll")] + private static extern bool IsZoomed(IntPtr hWnd); - [DllImport("user32.dll")] - private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + [DllImport("user32.dll")] + private static extern bool IsWindow(IntPtr hWnd); + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); - [DllImport("user32.dll")] - private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); + [DllImport("user32.dll")] + private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); - [StructLayout(LayoutKind.Sequential)] - private struct RECT + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); + private const uint SWP_NOMOVE = 0x0002; + private const uint SWP_NOSIZE = 0x0001; + private const uint WM_CLOSE = 0x0010; + private const int GWL_STYLE = -16; + private const int GWL_EXSTYLE = -20; + private const int WS_BORDER = 0x00800000; + private const int WS_CAPTION = 0x00C00000; + private const int WS_CHILD = 0x40000000; + private const int WS_POPUP = unchecked((int)0x80000000); + private const int WS_SYSMENU = 0x00080000; + private const int WS_MINIMIZEBOX = 0x00020000; + private const int WS_MAXIMIZEBOX = 0x00010000; + private const int WS_EX_TOPMOST = 0x00000008; + private const int WS_EX_TRANSPARENT = 0x00000020; + private const int WS_EX_TOOLWINDOW = 0x00000080; + private const int WS_EX_LAYERED = 0x00080000; + private const uint LWA_ALPHA = 0x2; + private const int SW_HIDE = 0; + private const int SW_SHOW = 5; + private const int SW_NORMAL = 1; + private const int SW_MAXIMIZE = 3; + private const int SW_MINIMIZE = 6; + private const int SW_RESTORE = 9; + + private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + #endregion + + public static void Main(string[] args) + { + if (args.Length == 0 || args[0] == "-h" || args[0] == "--help") { - public int Left; - public int Top; - public int Right; - public int Bottom; + ShowHelp(); + return; } - private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); - private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); - private const uint SWP_NOMOVE = 0x0002; - private const uint SWP_NOSIZE = 0x0001; - private const uint WM_CLOSE = 0x0010; - private const int GWL_STYLE = -16; - private const int GWL_EXSTYLE = -20; - private const int WS_BORDER = 0x00800000; - private const int WS_CAPTION = 0x00C00000; - private const int WS_CHILD = 0x40000000; - private const int WS_POPUP = unchecked((int)0x80000000); - private const int WS_SYSMENU = 0x00080000; - private const int WS_MINIMIZEBOX = 0x00020000; - private const int WS_MAXIMIZEBOX = 0x00010000; - private const int WS_EX_TOPMOST = 0x00000008; - private const int WS_EX_TRANSPARENT = 0x00000020; - private const int WS_EX_TOOLWINDOW = 0x00000080; - private const int WS_EX_LAYERED = 0x00080000; - private const uint LWA_ALPHA = 0x2; - private const int SW_HIDE = 0; - private const int SW_SHOW = 5; - private const int SW_NORMAL = 1; - private const int SW_MAXIMIZE = 3; - private const int SW_MINIMIZE = 6; - private const int SW_RESTORE = 9; - - private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); - #endregion - - public static void Main(string[] args) + try { - if (args.Length == 0 || args[0] == "-h" || args[0] == "--help") + List targetWindows = GetTargetWindows(args); + if (targetWindows.Count == 0) + { + throw new Exception("未找到目标窗口"); + } + + string type = GetArgumentValue(args, "-type"); + if (type.ToLower() == "info") + { + var allWindowInfo = new List>(); + foreach (IntPtr windowHandle in targetWindows) { - ShowHelp(); - return; + var windowInfo = GetBasicWindowInfo(windowHandle); + if (windowInfo != null) + { + allWindowInfo.Add(windowInfo); + } } + var serializer = new JavaScriptSerializer(); + Console.WriteLine(serializer.Serialize(allWindowInfo)); + return; + } - try + IntPtr targetHandle = targetWindows[0]; + Dictionary operatedWindow = null; + + switch (type.ToLower()) + { + case "topmost": + bool isTopMost = bool.Parse(GetArgumentValue(args, "-value") ?? "true"); + SetWindowPos(targetHandle, isTopMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + case "opacity": + int opacity = int.Parse(GetArgumentValue(args, "-value") ?? "100"); + SetWindowLong(targetHandle, GWL_EXSTYLE, GetWindowLong(targetHandle, GWL_EXSTYLE) | WS_EX_LAYERED); + SetLayeredWindowAttributes(targetHandle, 0, (byte)(opacity * 2.55), LWA_ALPHA); + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + case "rect": + string[] rectValues = (GetArgumentValue(args, "-value") ?? "").Split(','); + if (rectValues.Length == 4) + { + int x = int.Parse(rectValues[0]); + int y = int.Parse(rectValues[1]); + int width = int.Parse(rectValues[2]); + int height = int.Parse(rectValues[3]); + MoveWindow(targetHandle, x, y, width, height, true); + } + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + case "state": + string state = GetArgumentValue(args, "-value") ?? "normal"; + int cmdShow = state == "maximize" ? SW_MAXIMIZE : + state == "minimize" ? SW_MINIMIZE : SW_NORMAL; + ShowWindow(targetHandle, cmdShow); + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + case "visible": + bool visible = bool.Parse(GetArgumentValue(args, "-value") ?? "true"); + ShowWindow(targetHandle, visible ? SW_SHOW : SW_HIDE); + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + case "close": + PostMessage(targetHandle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + case "focus": + if (IsIconic(targetHandle)) + { + ShowWindow(targetHandle, SW_RESTORE); + } + SetForegroundWindow(targetHandle); + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + case "border": + bool hasBorder = bool.Parse(GetArgumentValue(args, "-value") ?? "true"); + int style = GetWindowLong(targetHandle, GWL_STYLE); + style = hasBorder ? style | WS_CAPTION : style & ~WS_CAPTION; + SetWindowLong(targetHandle, GWL_STYLE, style); + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + case "clickthrough": + bool isTransparent = bool.Parse(GetArgumentValue(args, "-value") ?? "true"); + int exStyle = GetWindowLong(targetHandle, GWL_EXSTYLE); + exStyle |= WS_EX_LAYERED; + exStyle = isTransparent ? exStyle | WS_EX_TRANSPARENT : exStyle & ~WS_EX_TRANSPARENT; + SetWindowLong(targetHandle, GWL_EXSTYLE, exStyle); + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + case "info": + operatedWindow = GetBasicWindowInfo(targetHandle); + break; + + default: + Console.Error.WriteLine("Error: 不支持的操作类型"); + return; + } + + if (operatedWindow != null) + { + var serializer = new JavaScriptSerializer(); + Console.WriteLine(serializer.Serialize(operatedWindow)); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(string.Format("Error: {0}", ex.Message)); + } + } + + private static List GetTargetWindows(string[] args) + { + List targetWindows = new List(); + string method = GetArgumentValue(args, "-method") ?? "title"; + string value = GetArgumentValue(args, "-window") ?? ""; + + switch (method.ToLower()) + { + case "handle": + IntPtr handle = new IntPtr(long.Parse(value)); + if (!IsWindow(handle)) { - List targetWindows = GetTargetWindows(args); - if (targetWindows.Count == 0) - { - throw new Exception("未找到目标窗口"); - } - - string type = GetArgumentValue(args, "-type"); - if (type.ToLower() == "info") - { - var allWindowInfo = new List>(); - foreach (IntPtr windowHandle in targetWindows) - { - var windowInfo = GetBasicWindowInfo(windowHandle); - if (windowInfo != null) - { - allWindowInfo.Add(windowInfo); - } - } - var serializer = new JavaScriptSerializer(); - Console.WriteLine(serializer.Serialize(allWindowInfo)); - return; - } - - IntPtr targetHandle = targetWindows[0]; - Dictionary operatedWindow = null; - - switch (type.ToLower()) - { - case "topmost": - bool isTopMost = bool.Parse(GetArgumentValue(args, "-value") ?? "true"); - SetWindowPos(targetHandle, isTopMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - case "opacity": - int opacity = int.Parse(GetArgumentValue(args, "-value") ?? "100"); - SetWindowLong(targetHandle, GWL_EXSTYLE, GetWindowLong(targetHandle, GWL_EXSTYLE) | WS_EX_LAYERED); - SetLayeredWindowAttributes(targetHandle, 0, (byte)(opacity * 2.55), LWA_ALPHA); - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - case "rect": - string[] rectValues = (GetArgumentValue(args, "-value") ?? "").Split(','); - if (rectValues.Length == 4) - { - int x = int.Parse(rectValues[0]); - int y = int.Parse(rectValues[1]); - int width = int.Parse(rectValues[2]); - int height = int.Parse(rectValues[3]); - MoveWindow(targetHandle, x, y, width, height, true); - } - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - case "state": - string state = GetArgumentValue(args, "-value") ?? "normal"; - int cmdShow = state == "maximize" ? SW_MAXIMIZE : - state == "minimize" ? SW_MINIMIZE : SW_NORMAL; - ShowWindow(targetHandle, cmdShow); - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - case "visible": - bool visible = bool.Parse(GetArgumentValue(args, "-value") ?? "true"); - ShowWindow(targetHandle, visible ? SW_SHOW : SW_HIDE); - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - case "close": - PostMessage(targetHandle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - case "focus": - if (IsIconic(targetHandle)) - { - ShowWindow(targetHandle, SW_RESTORE); - } - SetForegroundWindow(targetHandle); - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - case "border": - bool hasBorder = bool.Parse(GetArgumentValue(args, "-value") ?? "true"); - int style = GetWindowLong(targetHandle, GWL_STYLE); - style = hasBorder ? style | WS_CAPTION : style & ~WS_CAPTION; - SetWindowLong(targetHandle, GWL_STYLE, style); - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - case "clickthrough": - bool isTransparent = bool.Parse(GetArgumentValue(args, "-value") ?? "true"); - int exStyle = GetWindowLong(targetHandle, GWL_EXSTYLE); - exStyle |= WS_EX_LAYERED; - exStyle = isTransparent ? exStyle | WS_EX_TRANSPARENT : exStyle & ~WS_EX_TRANSPARENT; - SetWindowLong(targetHandle, GWL_EXSTYLE, exStyle); - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - case "info": - operatedWindow = GetBasicWindowInfo(targetHandle); - break; - - default: - Console.Error.WriteLine("Error: 不支持的操作类型"); - return; - } - - if (operatedWindow != null) - { - var serializer = new JavaScriptSerializer(); - Console.WriteLine(serializer.Serialize(operatedWindow)); - } + throw new Exception("指定的句柄不是一个有效的窗口句柄"); } - catch (Exception ex) + targetWindows.Add(handle); + break; + + case "active": + targetWindows.Add(GetForegroundWindow()); + break; + + case "process": + var processes = Process.GetProcessesByName(value); + foreach (var process in processes) { - Console.Error.WriteLine(string.Format("Error: {0}", ex.Message)); + if (process.MainWindowHandle != IntPtr.Zero) + { + targetWindows.Add(process.MainWindowHandle); + } } + break; + + case "class": + EnumWindows((hwnd, param) => + { + if (!IsWindowVisible(hwnd)) + { + return true; + } + + StringBuilder className = new StringBuilder(256); + GetClassName(hwnd, className, className.Capacity); + string windowClassName = className.ToString(); + + if (windowClassName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) + { + targetWindows.Add(hwnd); + } + return true; + }, IntPtr.Zero); + break; + + case "title": + default: + if (!string.IsNullOrEmpty(value)) + { + EnumWindows((hwnd, param) => + { + StringBuilder title = new StringBuilder(256); + GetWindowText(hwnd, title, title.Capacity); + string windowTitle = title.ToString(); + + if (!string.IsNullOrEmpty(windowTitle) && + windowTitle.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) + { + targetWindows.Add(hwnd); + } + return true; + }, IntPtr.Zero); + } + break; } - private static List GetTargetWindows(string[] args) + if (targetWindows.Count == 0) { - List targetWindows = new List(); - string method = GetArgumentValue(args, "-method") ?? "title"; - string value = GetArgumentValue(args, "-window") ?? ""; - - switch (method.ToLower()) - { - case "handle": - targetWindows.Add(new IntPtr(long.Parse(value))); - break; - - case "active": - targetWindows.Add(GetForegroundWindow()); - break; - - case "process": - var processes = Process.GetProcessesByName(value); - foreach (var process in processes) - { - if (process.MainWindowHandle != IntPtr.Zero) - { - targetWindows.Add(process.MainWindowHandle); - } - } - break; - - case "class": - EnumWindows((hwnd, param) => - { - if (!IsWindowVisible(hwnd)) - { - return true; - } - - StringBuilder className = new StringBuilder(256); - GetClassName(hwnd, className, className.Capacity); - string windowClassName = className.ToString(); - - if (windowClassName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) - { - targetWindows.Add(hwnd); - } - return true; - }, IntPtr.Zero); - break; - - case "title": - default: - if (!string.IsNullOrEmpty(value)) - { - EnumWindows((hwnd, param) => - { - StringBuilder title = new StringBuilder(256); - GetWindowText(hwnd, title, title.Capacity); - string windowTitle = title.ToString(); - - if (!string.IsNullOrEmpty(windowTitle) && - windowTitle.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) - { - targetWindows.Add(hwnd); - } - return true; - }, IntPtr.Zero); - } - break; - } - - if (targetWindows.Count == 0) - { - throw new Exception("未找到匹配的窗口"); - } - - return targetWindows; + throw new Exception("未找到匹配的窗口"); } - private static Dictionary GetBasicWindowInfo(IntPtr hwnd) + return targetWindows; + } + + private static Dictionary GetBasicWindowInfo(IntPtr hwnd) + { + StringBuilder title = new StringBuilder(256); + StringBuilder className = new StringBuilder(256); + GetWindowText(hwnd, title, title.Capacity); + GetClassName(hwnd, className, className.Capacity); + + // 获取窗口位置和大小 + RECT rect = new RECT(); + GetWindowRect(hwnd, out rect); + + // 获取进程信息 + uint processId = 0; + GetWindowThreadProcessId(hwnd, out processId); + string processName = ""; + string processPath = ""; + try { - StringBuilder title = new StringBuilder(256); - StringBuilder className = new StringBuilder(256); - GetWindowText(hwnd, title, title.Capacity); - GetClassName(hwnd, className, className.Capacity); + var process = Process.GetProcessById((int)processId); + processName = process.ProcessName; + processPath = process.MainModule.FileName; + } + catch { } - // 获取窗口位置和大小 - RECT rect = new RECT(); - GetWindowRect(hwnd, out rect); - - // 获取进程信息 - uint processId = 0; - GetWindowThreadProcessId(hwnd, out processId); - string processName = ""; - string processPath = ""; - try - { - var process = Process.GetProcessById((int)processId); - processName = process.ProcessName; - processPath = process.MainModule.FileName; - } - catch { } - - return new Dictionary + return new Dictionary { { "handle", hwnd.ToInt64() }, { "title", title.ToString() }, @@ -345,21 +352,21 @@ public class WindowManager { "processName", processName }, { "processPath", processPath } }; - } + } - private static string GetArgumentValue(string[] args, string key) + private static string GetArgumentValue(string[] args, string key) + { + int index = Array.IndexOf(args, key); + if (index >= 0 && index < args.Length - 1) { - int index = Array.IndexOf(args, key); - if (index >= 0 && index < args.Length - 1) - { - return args[index + 1]; - } - return null; + return args[index + 1]; } + return null; + } - private static void ShowHelp() - { - string help = @" + private static void ShowHelp() + { + string help = @" Windows 窗口管理工具使用说明 ========================== @@ -455,6 +462,6 @@ window.exe -type <操作类型> [参数...] 1. 窗口标题、类名支持模糊匹配,active方式可不提供window参数 2. 只有info操作会返回所有匹配窗口的信息,其他操作只会操作第一个匹配的窗口 "; - Console.WriteLine(help); - } + Console.WriteLine(help); + } } diff --git a/plugin/lib/quickcomposer/windows/automation.js b/plugin/lib/quickcomposer/windows/automation.js index a834d37..7bc59a3 100644 --- a/plugin/lib/quickcomposer/windows/automation.js +++ b/plugin/lib/quickcomposer/windows/automation.js @@ -123,7 +123,10 @@ async function runAutomation( } } } catch (err) { - error = err.toString().trim(); + error = err + .toString() + .replace(/^Error: /, "") + .trim(); } if (type === "inspect") return { error }; diff --git a/plugin/lib/quickcomposer/windows/sendmessage.js b/plugin/lib/quickcomposer/windows/sendmessage.js index a8288e4..392cced 100644 --- a/plugin/lib/quickcomposer/windows/sendmessage.js +++ b/plugin/lib/quickcomposer/windows/sendmessage.js @@ -2,7 +2,7 @@ const { runCsharpFeature } = require("../../csharp"); /** * 执行消息发送操作 - * @param {string} type - 操作类型, 可选值: "keyboard"|"mouse"|"inspect" + * @param {string} type - 操作类型, 可选值: "keyboard"|"mouse"|"list" * @param {Object} params - 参数对象 * @param {string} params.method - 查找方式:"title"|"handle"|"process"|"class"|"active" * @param {string} params.window - 窗口标题、句柄、进程名、类名 @@ -47,7 +47,7 @@ async function runSendMessage(type, params = {}) { } break; - case "inspect": + case "list": if (filter) { args.push("-filter", filter); } @@ -61,54 +61,53 @@ async function runSendMessage(type, params = {}) { try { const result = await runCsharpFeature("sendmessage", args); if (result) { - const jsonResult = JSON.parse(result); - if (type === "inspect") { - return jsonResult; - } - return { success: true, control: jsonResult }; + const resultStr = result.toString().trim(); + if (type === "list") return JSON.parse(resultStr); + if (resultStr === "true") return { success: true }; } } catch (err) { error = err .toString() .replace(/^Error: /, "") .trim(); + console.log(error); } - if (type === "inspect") return []; + if (type === "list") return []; return { success: false, error }; } module.exports = { - sendKeys: (method, window, keys, options = {}) => + sendKeys: (windowHandle, keys, options = {}) => runSendMessage("keyboard", { - method, - window, + method: windowHandle ? "handle" : "active", + window: windowHandle, action: "keys", value: keys, options, }), - sendText: (method, window, text, options = {}) => + sendText: (windowHandle, text, options = {}) => runSendMessage("keyboard", { - method, - window, + method: windowHandle ? "handle" : "active", + window: windowHandle, action: "text", value: text, options, }), - click: (method, window, action = "click", options = {}) => + click: (windowHandle, action = "click", options = {}) => runSendMessage("mouse", { - method, - window, + method: windowHandle ? "handle" : "active", + window: windowHandle, action, options, }), - inspectWindow: (method, window, options = {}) => - runSendMessage("inspect", { - method, - window, + listControls: (windowHandle, options = {}) => + runSendMessage("list", { + method: windowHandle ? "handle" : "active", + window: windowHandle, options, }), }; diff --git a/plugin/lib/quickcomposer/windows/window.js b/plugin/lib/quickcomposer/windows/window.js index b2eca9a..6ac6a82 100644 --- a/plugin/lib/quickcomposer/windows/window.js +++ b/plugin/lib/quickcomposer/windows/window.js @@ -57,7 +57,7 @@ async function runWindow(type, params = {}) { if (type === "info") { return jsonResult; } - return { success: true, window: jsonResult }; + return { success: true }; } } catch (err) { error = err @@ -72,27 +72,63 @@ async function runWindow(type, params = {}) { } module.exports = { - setTopMost: (method, window, isTopMost) => - runWindow("topmost", { method, window, value: isTopMost }), + setTopMost: (windowHandle, isTopMost) => + runWindow("topmost", { + method: windowHandle ? "handle" : "active", + window: windowHandle, + value: isTopMost, + }), - setOpacity: (method, window, opacity) => - runWindow("opacity", { method, window, value: opacity }), + setOpacity: (windowHandle, opacity) => + runWindow("opacity", { + method: windowHandle ? "handle" : "active", + window: windowHandle, + value: opacity, + }), - setWindowRect: (method, window, x, y, width, height) => - runWindow("rect", { method, window, value: { x, y, width, height } }), + setWindowRect: (windowHandle, x, y, width, height) => + runWindow("rect", { + method: windowHandle ? "handle" : "active", + window: windowHandle, + value: { x, y, width, height }, + }), - setWindowState: (method, window, state) => - runWindow("state", { method, window, value: state }), + setWindowState: (windowHandle, state) => + runWindow("state", { + method: windowHandle ? "handle" : "active", + window: windowHandle, + value: state, + }), - closeWindow: (method, window) => runWindow("close", { method, window }), + closeWindow: (windowHandle) => + runWindow("close", { + method: windowHandle ? "handle" : "active", + window: windowHandle, + }), - setFocus: (method, window) => runWindow("focus", { method, window }), + setFocus: (windowHandle) => + runWindow("focus", { + method: windowHandle ? "handle" : "active", + window: windowHandle, + }), - setBorder: (method, window, hasBorder) => - runWindow("border", { method, window, value: hasBorder }), + setBorder: (windowHandle, hasBorder) => + runWindow("border", { + method: windowHandle ? "handle" : "active", + window: windowHandle, + value: hasBorder, + }), - setClickThrough: (method, window, isTransparent) => - runWindow("clickthrough", { method, window, value: isTransparent }), + setClickThrough: (windowHandle, isTransparent) => + runWindow("clickthrough", { + method: windowHandle ? "handle" : "active", + window: windowHandle, + value: isTransparent, + }), - getWindowInfo: (method, window) => runWindow("info", { method, window }), + getWindowInfo: (method, window) => + runWindow("info", { + method, + window, + }), }; diff --git a/src/js/composer/commands/windowsCommands.js b/src/js/composer/commands/windowsCommands.js index b81d825..904dfd0 100644 --- a/src/js/composer/commands/windowsCommands.js +++ b/src/js/composer/commands/windowsCommands.js @@ -177,15 +177,19 @@ const searchWindowConfig = [ }, ]; -const searchElementConfig = [ +const windowHandleConfig = [ { label: "窗口句柄", component: "VariableInput", icon: "window", width: 12, - placeholder: "留空则使用当前活动窗口", + placeholder: "可从搜索/选择窗口获取,留空则使用当前活动窗口", defaultValue: newVarInputVal("str", ""), }, +]; + +const searchElementConfig = [ + windowHandleConfig, { label: "元素查找方式", component: "q-select", @@ -267,7 +271,7 @@ export const windowsCommands = { value: "quickcomposer.windows.window.setTopMost", label: "窗口控制", icon: "window", - config: searchWindowConfig, + config: windowHandleConfig, subCommands: [ { value: "quickcomposer.windows.window.setTopMost", @@ -630,14 +634,14 @@ export const windowsCommands = { }, // sendmessage { - value: "quickcomposer.windows.sendmessage.inspectWindow", + value: "quickcomposer.windows.sendmessage.listControls", label: "发送控制消息", icon: "smart_button", isAsync: true, - config: searchWindowConfig, + config: windowHandleConfig, subCommands: [ { - value: "quickcomposer.windows.sendmessage.inspectWindow", + value: "quickcomposer.windows.sendmessage.listControls", label: "获取控件树", icon: "account_tree", outputVariable: "controlsTree",