using System; using System.Text; using System.Runtime.InteropServices; using System.Windows.Automation; using System.Windows.Automation.Text; using System.Windows; using System.Collections.Generic; using System.Web.Script.Serialization; using System.Linq; using System.Threading; using System.Windows.Forms; using System.Drawing; public class AutomationManager { // UIA Control Type IDs private static class UIA_ControlTypeIds { public const int Window = 50032; } // 用于缓存找到的元素 private static Dictionary elementCache = new Dictionary(); [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [StructLayout(LayoutKind.Sequential)] private struct Point { public int X; public int Y; public override bool Equals(object obj) { if (!(obj is Point)) return false; Point other = (Point)obj; return X == other.X && Y == other.Y; } public static bool operator ==(Point a, Point b) { return a.Equals(b); } public static bool operator !=(Point a, Point b) { return !a.Equals(b); } public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); } public System.Windows.Point ToWindowsPoint() { return new System.Windows.Point(X, Y); } } [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetCursorPos(out Point lpPoint); [DllImport("user32.dll")] static extern int GetWindowLong(IntPtr hwnd, int index); [DllImport("user32.dll")] static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle); private const int GWL_EXSTYLE = -20; private const int WS_EX_TRANSPARENT = 0x20; private const int WS_EX_LAYERED = 0x80000; private const int WS_EX_TOOLWINDOW = 0x80; private const int WS_EX_NOACTIVATE = 0x08000000; private const int HWND_TOPMOST = -1; [DllImport("user32.dll")] static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("user32.dll")] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); [DllImport("user32.dll")] private static extern bool SetCursorPos(int x, int y); [DllImport("user32.dll")] private static extern void mouse_event(uint dwFlags, int dx, int dy, uint dwData, int dwExtraInfo); private const uint MOUSEEVENTF_LEFTDOWN = 0x0002; private const uint MOUSEEVENTF_LEFTUP = 0x0004; // 添加静态字段 private static System.Windows.Forms.Timer mouseTimer; private static Form overlayForm; private static Form previewForm; private static AutomationElement lastElement; private static bool completed; private static CacheRequest CreateCacheRequest() { var cacheRequest = new CacheRequest(); cacheRequest.Add(AutomationElement.NameProperty); cacheRequest.Add(AutomationElement.ClassNameProperty); cacheRequest.Add(AutomationElement.ControlTypeProperty); cacheRequest.Add(AutomationElement.BoundingRectangleProperty); cacheRequest.Add(AutomationElement.IsOffscreenProperty); cacheRequest.Add(AutomationElement.IsEnabledProperty); cacheRequest.TreeScope = TreeScope.Element | TreeScope.Children | TreeScope.Descendants; return cacheRequest; } private static AutomationElement GetTaskbarElement() { IntPtr taskbarHandle = FindWindow("Shell_TrayWnd", null); if (taskbarHandle != IntPtr.Zero) { return AutomationElement.FromHandle(taskbarHandle); } return null; } private static List GetTaskbarChildren(AutomationElement taskbarElement) { var children = new List(); if (taskbarElement == null) return children; try { var cacheRequest = CreateCacheRequest(); cacheRequest.Push(); try { var conditions = new AndCondition( new PropertyCondition(AutomationElement.IsOffscreenProperty, false), new PropertyCondition(AutomationElement.IsEnabledProperty, true), new PropertyCondition(AutomationElement.IsContentElementProperty, true) ); var taskbarChildren = taskbarElement.FindAll(TreeScope.Children | TreeScope.Descendants, conditions); foreach (AutomationElement child in taskbarChildren) { children.Add(child); } } finally { cacheRequest.Pop(); } } catch (Exception ex) { Console.Error.WriteLine(string.Format("Error: {0}", ex.Message)); } return children; } private static void InspectElementByPosition(string position) { Point cursorPos; if (string.IsNullOrEmpty(position)) { // 获取当前鼠标位置 GetCursorPos(out cursorPos); } else { // 解析传入的坐标 string[] coords = position.Split(','); cursorPos = new Point { X = int.Parse(coords[0]), Y = int.Parse(coords[1]) }; } var element = AutomationElement.FromPoint(cursorPos.ToWindowsPoint()); if (element != null) { InspectElementInfo(element, cursorPos); return; } throw new Exception("在指定坐标未找到元素"); } public static void Main(string[] args) { if (args.Length == 0 || args[0] == "-h" || args[0] == "--help") { ShowHelp(); return; } string type = GetArgumentValue(args, "-type"); if (string.IsNullOrEmpty(type)) { Console.Error.WriteLine("Error: 必须指定操作类型 (-type)"); return; } try { switch (type.ToLower()) { case "inspect": string position = GetArgumentValue(args, "-position"); if (position != null) // 参数存在(一定会有值,可能是空字符串) { InspectElementByPosition(position); } else // 参数不存在 { InspectElement(args); } break; case "click": ClickElement(GetTargetElement(args)); break; case "setvalue": string value = GetArgumentValue(args, "-value"); SetElementValue(GetTargetElement(args), value); string sendenter = GetArgumentValue(args, "-sendenter"); if (sendenter != null) { System.Windows.Forms.SendKeys.SendWait("{ENTER}"); } break; case "getvalue": GetElementValue(GetTargetElement(args)); break; case "select": string item = GetArgumentValue(args, "-item"); SelectItem(GetTargetElement(args), item); break; case "expand": string expandStr = GetArgumentValue(args, "-expand"); bool expand = false; if (!string.IsNullOrEmpty(expandStr)) { expand = expandStr.ToLower() == "true"; } ExpandElement(GetTargetElement(args), expand); break; case "scroll": string direction = GetArgumentValue(args, "-direction"); if (string.IsNullOrEmpty(direction)) { direction = "vertical"; } string amountStr = GetArgumentValue(args, "-amount"); double amount = 0; if (!string.IsNullOrEmpty(amountStr)) { amount = double.Parse(amountStr); } ScrollElement(GetTargetElement(args), direction, amount); break; case "wait": string timeoutStr = GetArgumentValue(args, "-timeout"); int timeout = 30; if (!string.IsNullOrEmpty(timeoutStr)) { timeout = int.Parse(timeoutStr); } WaitForElement(args, timeout); break; case "focus": SetFocus(GetTargetElement(args)); break; case "highlight": string durationStr = GetArgumentValue(args, "-duration"); int duration = 2; if (!string.IsNullOrEmpty(durationStr)) { duration = int.Parse(durationStr); } HighlightElement(GetTargetElement(args), duration); break; case "sendkeys": string keys = GetArgumentValue(args, "-keys"); SendKeys(GetTargetElement(args), keys); break; default: throw new Exception(string.Format("不支持的操作类型: {0}", type)); } } catch (Exception ex) { Console.Error.WriteLine(string.Format("Error: {0}", ex.Message)); Environment.Exit(1); } } private static AutomationElement GetTargetElement(string[] args) { // 获取起始窗口 AutomationElement root; string windowArg = GetArgumentValue(args, "-window"); if (!string.IsNullOrEmpty(windowArg)) { // 通过窗口句柄查找 int handle = int.Parse(windowArg); root = AutomationElement.FromHandle(new IntPtr(handle)); } else { // 使用当前活动窗口 IntPtr activeHandle = GetForegroundWindow(); root = AutomationElement.FromHandle(activeHandle); } if (root == null) { throw new Exception("无法获取指定的窗口"); } // 通过 XPath 查找(优先) string xpath = GetArgumentValue(args, "-xpath"); if (!string.IsNullOrEmpty(xpath)) { var element = FindElementByXPath(xpath, root); if (element != null) return element; throw new Exception(string.Format("找不到指定的XPath: {0}", xpath)); } // 通过 AutomationId 查找 string id = GetArgumentValue(args, "-id"); if (!string.IsNullOrEmpty(id)) { var element = root.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.AutomationIdProperty, id)); if (element != null) return element; throw new Exception(string.Format("找不到指定的AutomationId: {0}", id)); } // 通过 Name 查找 string name = GetArgumentValue(args, "-name"); if (!string.IsNullOrEmpty(name)) { var element = root.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, name)); if (element != null) return element; throw new Exception(string.Format("找不到指定的Name: {0}", name)); } // 通过组合条件查找 string condition = GetArgumentValue(args, "-condition"); if (!string.IsNullOrEmpty(condition)) { var conditions = new List(); string[] parts = condition.Split(';'); foreach (string part in parts) { string[] keyValue = part.Split('='); if (keyValue.Length == 2) { switch (keyValue[0].ToLower()) { case "name": conditions.Add(new PropertyCondition(AutomationElement.NameProperty, keyValue[1])); break; case "class": conditions.Add(new PropertyCondition(AutomationElement.ClassNameProperty, keyValue[1])); break; case "automation": conditions.Add(new PropertyCondition(AutomationElement.AutomationIdProperty, keyValue[1])); break; case "type": string controlTypeName = keyValue[1]; if (controlTypeName.StartsWith("ControlType.")) { controlTypeName = controlTypeName.Substring("ControlType.".Length); } var field = typeof(ControlType).GetField(controlTypeName); if (field != null) { ControlType controlType = (ControlType)field.GetValue(null); conditions.Add(new PropertyCondition(AutomationElement.ControlTypeProperty, controlType)); } break; } } } if (conditions.Count > 0) { Condition searchCondition; if (conditions.Count > 1) { searchCondition = new AndCondition(conditions.ToArray()); } else { searchCondition = conditions[0]; } var element = root.FindFirst(TreeScope.Subtree, searchCondition); if (element != null) return element; throw new Exception(string.Format("找不到符合条件的元素: {0}", condition)); } } throw new Exception("必须指定元素的识别方式: -xpath, -id, -name 或 -condition"); } private static void ShowHelp() { string help = @"UI自动化工具 用法: automation.exe -type <操作类型> [参数] 通用参数: -window <窗口句柄> 指定要操作的窗口,如果不指定则使用当前活动窗口 操作类型: 1. inspect - 检查元素 无需其他参数,点击要检查的元素即可 2. click - 点击元素 -xpath 或 -id 或 -name <名称> 或 -condition ""name=xx;type=Button"" 3. setvalue - 设置值 -xpath -value <新值> 4. getvalue - 获取值 -xpath 5. select - 选择项目 -xpath -item <项目名称> 6. expand - 展开/折叠 -xpath -expand 7. scroll - 滚动 -xpath -direction -amount <0-100> 8. wait - 等待元素 -xpath -timeout <秒数> 9. focus - 设置焦点 -xpath 10. highlight - 高亮显示 -xpath -duration <秒数> 11. sendkeys - 发送按键 -xpath -keys <按键> 按键格式说明: - 普通字符直接输入,如 ""abc"" - 特殊按键用 {} 包围,如 {ENTER}、{TAB} - 组合键,如 ^c 表示 Ctrl+C - 支持的特殊按键: {BACKSPACE}, {BS}, {BKSP} - 退格键 {BREAK} - Break键 {CAPSLOCK} - Caps Lock键 {DELETE}, {DEL} - Delete键 {DOWN} - 向下键 {END} - End键 {ENTER}, {RETURN} - Enter键 {ESC} - Esc键 {HELP} - Help键 {HOME} - Home键 {INSERT}, {INS} - Insert键 {LEFT} - 向左键 {NUMLOCK} - Num Lock键 {PGDN} - Page Down键 {PGUP} - Page Up键 {PRTSC} - Print Screen键 {RIGHT} - 向右键 {SCROLLLOCK} - Scroll Lock键 {TAB} - Tab键 {UP} - 向上键 {F1} - {F12} - 功能键 {ADD} - 数字键盘加号键 {SUBTRACT} - 数字键盘减号键 {MULTIPLY} - 数字键盘乘号键 {DIVIDE} - 数字键盘除号键 {NUMPAD0} - {NUMPAD9} - 数字键盘数字键 修饰键: + (加号) - SHIFT ^ (脱字号) - CTRL % (百分号) - ALT 示例: ""Hello"" - 输入 Hello ""{ENTER}"" - 按 Enter 键 ""^c"" - 按 Ctrl+C ""^{HOME}"" - 按 Ctrl+Home ""%{F4}"" - 按 Alt+F4 ""+{TAB}"" - 按 Shift+Tab"; Console.WriteLine(help); } private static string GetArgumentValue(string[] args, string key, bool checkNextArg = true) { int index = Array.IndexOf(args, key); if (index >= 0) { if (index < args.Length - 1) { // 如果需要检查下一个参数是否是参数名 if (checkNextArg && args[index + 1].StartsWith("-")) { return ""; // 参数存在但没有值,返回空字符串 } return args[index + 1]; } return ""; // 参数存在但没有值,返回空字符串 } return null; // 参数不存在 } private static void SetElementValue(AutomationElement element, string value) { if (string.IsNullOrEmpty(value)) { throw new Exception("必须指定要设置的值"); } try { var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern; if (valuePattern != null) { valuePattern.SetValue(value); Console.WriteLine("true"); return; } throw new Exception("元素不支持设置值操作"); } catch (Exception ex) { throw new Exception("设置值失败: " + ex.Message); } } private static void GetElementValue(AutomationElement element) { try { var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern; if (valuePattern != null) { Console.WriteLine(valuePattern.Current.Value); return; } throw new Exception("元素不支持获取值操作"); } catch (Exception ex) { throw new Exception("获取值失败: " + ex.Message); } } private static void SelectItem(AutomationElement element, string item) { if (string.IsNullOrEmpty(item)) { throw new Exception("必须指定要选择的项目"); } try { var selectionPattern = element.GetCurrentPattern(SelectionPattern.Pattern) as SelectionPattern; if (selectionPattern != null) { var children = element.FindAll(TreeScope.Children, Condition.TrueCondition); foreach (AutomationElement child in children) { if (child.Current.Name == item) { var selectionItemPattern = child.GetCurrentPattern(SelectionItemPattern.Pattern) as SelectionItemPattern; if (selectionItemPattern != null) { selectionItemPattern.Select(); Console.WriteLine("true"); return; } } } throw new Exception("找不到指定的项目: " + item); } throw new Exception("元素不支持选择操作"); } catch (Exception ex) { throw new Exception("选择操作失败: " + ex.Message); } } private static void ExpandElement(AutomationElement element, bool expand) { try { var expandCollapsePattern = element.GetCurrentPattern(ExpandCollapsePattern.Pattern) as ExpandCollapsePattern; if (expandCollapsePattern != null) { if (expand) expandCollapsePattern.Expand(); else expandCollapsePattern.Collapse(); Console.WriteLine("true"); return; } throw new Exception("元素不支持展开/折叠操作"); } catch (Exception ex) { throw new Exception("展开/折叠操作失败: " + ex.Message); } } private static void ScrollElement(AutomationElement element, string direction, double amount) { try { var scrollPattern = element.GetCurrentPattern(ScrollPattern.Pattern) as ScrollPattern; if (scrollPattern != null) { if (direction.ToLower() == "horizontal") scrollPattern.SetScrollPercent(amount, ScrollPattern.NoScroll); else scrollPattern.SetScrollPercent(ScrollPattern.NoScroll, amount); Console.WriteLine("true"); return; } throw new Exception("元素不支持滚动操作"); } catch (Exception ex) { throw new Exception("滚动操作失败: " + ex.Message); } } private static void WaitForElement(string[] args, int timeout) { DateTime endTime = DateTime.Now.AddSeconds(timeout); while (DateTime.Now < endTime) { try { GetTargetElement(args); Console.WriteLine("true"); return; } catch { Thread.Sleep(500); } } throw new Exception("等待超时"); } private static AutomationElement FindElementByXPath(string xpath, AutomationElement root) { string[] segments = xpath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); AutomationElement current = root; foreach (string segment in segments) { if (string.IsNullOrEmpty(segment)) continue; // 解析控件类型和索引 string controlType = segment; int index = 1; string condition = ""; // 提取索引 [n] int indexStart = segment.IndexOf('['); if (indexStart > 0) { int indexEnd = segment.IndexOf(']', indexStart); if (indexEnd > indexStart) { controlType = segment.Substring(0, indexStart); string indexStr = segment.Substring(indexStart + 1, indexEnd - indexStart - 1); // 检查是否是属性条件 if (indexStr.StartsWith("@")) { condition = indexStr; } else { int parsedIndex; if (int.TryParse(indexStr, out parsedIndex)) { index = parsedIndex; } } } } // 创建控件类型条件 List conditions = new List(); if (controlType.StartsWith("ControlType.")) { controlType = controlType.Substring("ControlType.".Length); } var field = typeof(ControlType).GetField(controlType); if (field != null) { ControlType type = (ControlType)field.GetValue(null); conditions.Add(new PropertyCondition(AutomationElement.ControlTypeProperty, type)); } // 添加属性条件 if (!string.IsNullOrEmpty(condition)) { if (condition.StartsWith("@Name='")) { string name = condition.Substring("@Name='".Length).TrimEnd('\''); conditions.Add(new PropertyCondition(AutomationElement.NameProperty, name)); } else if (condition.StartsWith("@AutomationId='")) { string automationId = condition.Substring("@AutomationId='".Length).TrimEnd('\''); conditions.Add(new PropertyCondition(AutomationElement.AutomationIdProperty, automationId)); } else if (condition.StartsWith("@ClassName='")) { string className = condition.Substring("@ClassName='".Length).TrimEnd('\''); conditions.Add(new PropertyCondition(AutomationElement.ClassNameProperty, className)); } } // 查找元素 Condition finalCondition = conditions.Count > 1 ? new AndCondition(conditions.ToArray()) : conditions[0]; var elements = current.FindAll(TreeScope.Children, finalCondition); if (elements.Count == 0) return null; // 使用索引选择元素 if (index > elements.Count) return null; current = elements[index - 1]; // 转换为0基索引 } return current; } private static void SetFocus(AutomationElement element) { try { element.SetFocus(); Console.WriteLine("true"); } catch (Exception ex) { throw new Exception("设置焦点失败: " + ex.Message); } } private static void HighlightElement(AutomationElement element, int duration) { var rect = element.Current.BoundingRectangle; var highlightForm = new Form { StartPosition = FormStartPosition.Manual, Location = new System.Drawing.Point((int)rect.Left, (int)rect.Top), Size = new System.Drawing.Size((int)rect.Width, (int)rect.Height), BackColor = Color.Yellow, Opacity = 0.3, ShowInTaskbar = false, FormBorderStyle = FormBorderStyle.None, TopMost = true }; try { highlightForm.Show(); Console.WriteLine("正在高亮显示元素"); Thread.Sleep(duration * 1000); } finally { highlightForm.Close(); highlightForm.Dispose(); } } private struct ElementHierarchyInfo { public string XPath; public IntPtr WindowHandle; } // 获取元素的层次结构信息 private static ElementHierarchyInfo GetElementHierarchyInfo(AutomationElement element) { var path = new List(); var current = element; var walker = TreeWalker.ControlViewWalker; IntPtr windowHandle = IntPtr.Zero; // 循环直到找到根元素 while (current != null && current != AutomationElement.RootElement) { var parent = walker.GetParent(current); // 是否是最后一个元素 bool isLastElement = (parent == AutomationElement.RootElement || parent == null); // 是否是句柄不为0的窗口 bool isValidWindow = (current.Current.ControlType.Id == UIA_ControlTypeIds.Window && current.Current.NativeWindowHandle != 0); // 是否是任务栏 bool isTaskbar = (current.Current.ClassName == "Shell_TrayWnd" || current.Current.ClassName == "Shell_SecondaryTrayWnd"); // 如果是窗口/任务栏,或者是最后一个元素,获取其句柄 if (isValidWindow || isTaskbar || isLastElement) { windowHandle = new IntPtr(current.Current.NativeWindowHandle); break; // 获取到句柄后就停止遍历 } else { // 获取同级元素中的索引 int index = 1; var siblings = parent.FindAll(TreeScope.Children, new PropertyCondition( AutomationElement.ControlTypeProperty, current.Current.ControlType)); foreach (AutomationElement sibling in siblings) { if (sibling == current) break; index++; } // 构建路径段 string type = current.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""); string pathSegment = type; // 如果有多个同类型元素,添加索引 if (siblings.Count > 1) { pathSegment = string.Format("{0}[{1}]", type, index); } path.Insert(0, pathSegment); } current = parent; } return new ElementHierarchyInfo { XPath = "/" + string.Join("/", path), WindowHandle = windowHandle }; } private static void ClickElement(AutomationElement element) { if (element == null) { throw new Exception("未找到目标元素"); } try { // 首先尝试使用 Invoke 模式(适用于按钮等) object invokePattern; if (element.TryGetCurrentPattern(InvokePattern.Pattern, out invokePattern)) { ((InvokePattern)invokePattern).Invoke(); Console.WriteLine("true"); return; } // 尝试使用 SelectionItem 模式(适用于列表项、单选框等) object selectionItemPattern; if (element.TryGetCurrentPattern(SelectionItemPattern.Pattern, out selectionItemPattern)) { ((SelectionItemPattern)selectionItemPattern).Select(); Console.WriteLine("true"); return; } // 尝试使用 Toggle 模式(适用于复选框等) object togglePattern; if (element.TryGetCurrentPattern(TogglePattern.Pattern, out togglePattern)) { ((TogglePattern)togglePattern).Toggle(); Console.WriteLine("true"); return; } // 如果都不支持,尝试使用鼠标点击 try { // 激活元素 element.SetFocus(); // 获取元素的中心点坐标 System.Windows.Point clickablePoint = element.GetClickablePoint(); // 转换为屏幕坐标 var rect = element.Current.BoundingRectangle; if (rect.IsEmpty) { throw new Exception("无法获取元素位置"); } // 保存当前鼠标位置 Point currentMousePosition; GetCursorPos(out currentMousePosition); // 使用 mouse_event 执行点击 int x = (int)clickablePoint.X; int y = (int)clickablePoint.Y; // 移动鼠标到目标位置 SetCursorPos(x, y); Thread.Sleep(50); // 短暂延迟确保鼠标移动到位 // 模拟鼠标点击 mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0); Thread.Sleep(50); // 短暂延迟模拟真实点击 mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0); // 恢复鼠标位置 SetCursorPos(currentMousePosition.X, currentMousePosition.Y); Console.WriteLine("true"); } catch (Exception ex) { throw new Exception(string.Format("鼠标点击失败: {0}", ex.Message)); } } catch (Exception ex) { throw new Exception(string.Format("点击操作失败: {0}", ex.Message)); } } private static void SendKeys(AutomationElement element, string keys) { if (string.IsNullOrEmpty(keys)) { throw new Exception("必须指定要发送的按键"); } try { // 确保元素可以接收输入 if (!element.Current.IsKeyboardFocusable) { throw new Exception("元素不支持键盘输入"); } element.SetFocus(); System.Windows.Forms.SendKeys.SendWait(keys); Console.WriteLine("true"); } catch (Exception ex) { throw new Exception("发送按键失败: " + ex.Message); } } // 将 HandleKeyPress 方法移到类级别 private static void HandleKeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == (char)27) // ESC键 { mouseTimer.Stop(); overlayForm.Close(); previewForm.Close(); Environment.Exit(0); } else if (e.KeyChar == 'c' || e.KeyChar == 'C') // 添加复制功能 { if (lastElement != null) { try { Clipboard.SetText(lastElement.Current.Name); } catch (Exception ex) { Console.Error.WriteLine(string.Format("Error copying name: {0}", ex.Message)); } } } } // 元素检查器 private static void InspectElement(string[] args) { // 创建一个半透明遮罩窗口 overlayForm = new Form(); overlayForm.FormBorderStyle = FormBorderStyle.None; overlayForm.StartPosition = FormStartPosition.Manual; overlayForm.TopMost = true; overlayForm.BackColor = Color.Blue; overlayForm.Opacity = 0.15; overlayForm.ShowInTaskbar = false; overlayForm.KeyPreview = true; // 设置窗口样式,确保能显示在任务栏上方 overlayForm.Load += (sender, e) => { var hwnd = overlayForm.Handle; var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE); SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_LAYERED); }; // 创建预览窗口 previewForm = new Form { FormBorderStyle = FormBorderStyle.None, StartPosition = FormStartPosition.Manual, BackColor = Color.FromArgb(40, 40, 40), ShowInTaskbar = false, TopMost = true, Opacity = 0.9, AutoSize = true, // 添加自动尺寸 AutoSizeMode = AutoSizeMode.GrowAndShrink, // 根据内容调整大小 KeyPreview = true }; Label previewLabel = new Label { AutoSize = true, Dock = DockStyle.None, TextAlign = ContentAlignment.MiddleLeft, Font = new Font("楷体", 9), ForeColor = Color.White, Padding = new Padding(5), AutoEllipsis = true, UseMnemonic = false, MaximumSize = new System.Drawing.Size(600, 0) }; previewForm.Controls.Add(previewLabel); // 设置预览窗口样式 previewForm.Load += (sender, e) => { var hwnd = previewForm.Handle; var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE); SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_LAYERED); // 确保窗口显示在最顶层 SetWindowPos( hwnd, new IntPtr(HWND_TOPMOST), previewForm.Left, previewForm.Top, previewForm.Width, previewForm.Height, 0x0040 // SWP_SHOWWINDOW ); }; // 获取根元素 var rootElement = AutomationElement.RootElement; completed = false; // 使用静态字段 Rectangle lastRect = Rectangle.Empty; lastElement = null; Point lastCursorPos = new Point(); AutomationElement taskbarElement = null; List taskbarChildren = null; // 设置/取消窗口鼠标穿透的辅助方法 Action setMousePenetrate = (penetrate) => { var hwnd = overlayForm.Handle; var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE); if (penetrate) { SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT); } else { SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle & ~WS_EX_TRANSPARENT); } }; // 更新遮罩层位置的辅助方法 Action updateOverlayPosition = (rect) => { overlayForm.Location = new System.Drawing.Point(rect.Left, rect.Top); overlayForm.Size = new System.Drawing.Size(rect.Width, rect.Height); // 确保遮罩层始终在最顶层 SetWindowPos( overlayForm.Handle, new IntPtr(HWND_TOPMOST), rect.Left, rect.Top, rect.Width, rect.Height, 0x0040 // SWP_SHOWWINDOW ); overlayForm.Refresh(); }; // 添加一个新的辅助方法来更新预览窗口位置 Action updatePreviewPosition = (x, y) => { // 确保预览窗口始终在最顶层 SetWindowPos( previewForm.Handle, new IntPtr(HWND_TOPMOST), x, y, previewForm.Width, previewForm.Height, 0x0040 // SWP_SHOWWINDOW ); previewForm.Refresh(); }; // 创建一个定时器来处理鼠标位置检测 mouseTimer = new System.Windows.Forms.Timer(); mouseTimer.Interval = 100; mouseTimer.Tick += (sender, e) => { try { Point cursorPos; GetCursorPos(out cursorPos); if (cursorPos == lastCursorPos) { return; } lastCursorPos = cursorPos; setMousePenetrate(true); // 获取鼠标位置的元素 var element = AutomationElement.FromPoint(cursorPos.ToWindowsPoint()); setMousePenetrate(false); if (element != null && element != rootElement) { // AutomationElement.FromPoint只能获得到任务栏 // 无法获得到任务栏的子元素,所以需要遍历 element = HandleTaskbarElement( element, cursorPos, ref taskbarElement, ref taskbarChildren ); var elementBounds = element.Current.BoundingRectangle; Rectangle elementRect = new Rectangle( (int)elementBounds.Left, (int)elementBounds.Top, (int)elementBounds.Width, (int)elementBounds.Height ); bool elementChanged = lastElement == null || !element.Equals(lastElement) || elementRect != lastRect; if (elementChanged) { lastElement = element; lastRect = elementRect; // 获取元素后,显示遮罩层 if (!overlayForm.Visible) { overlayForm.Show(); } updateOverlayPosition(elementRect); // 计算预览窗口位置 int previewX = elementRect.Right + 10; int previewY = elementRect.Top; // 确保预览窗口不会超出屏幕 Screen screen = Screen.FromPoint(new System.Drawing.Point(previewX, previewY)); if (previewX + previewForm.Width > screen.Bounds.Right) { previewX = elementRect.Left - previewForm.Width - 10; } if (previewY + previewForm.Height > screen.Bounds.Bottom) { previewY = screen.Bounds.Bottom - previewForm.Height; } updatePreviewPosition(previewX, previewY); } // 处理名称长度 string elementName = element.Current.Name; if (elementName.Length > 50) { elementName = elementName.Substring(0, 47) + "..."; } // 更新预览窗口内容 previewLabel.Text = string.Format( "坐标: {0}, {1}\r\n" + "名称: {2}\r\n" + "大小: {3}x{4}\r\n" + "类型: {5}\r\n" + "C:复制名称,ESC:退出", cursorPos.X, cursorPos.Y, elementName, elementRect.Width, elementRect.Height, element.Current.ControlType.ProgrammaticName.Replace("ControlType.", "")); previewForm.Show(); } } catch (Exception ex) { Console.Error.WriteLine(string.Format("Error: {0}", ex.Message)); } }; overlayForm.MouseClick += (sender, e) => { if (e.Button == MouseButtons.Left && lastElement != null) { try { mouseTimer.Stop(); overlayForm.Hide(); previewForm.Hide(); InspectElementInfo(lastElement, lastCursorPos); completed = true; } catch (Exception ex) { Console.Error.WriteLine(string.Format("Error: {0}", ex.Message)); } finally { overlayForm.Close(); previewForm.Close(); } } }; // 添加按键事件处理 overlayForm.KeyPress += (sender, e) => { HandleKeyPress(sender, e); }; previewForm.KeyPress += (sender, e) => { HandleKeyPress(sender, e); }; // 在新线程中显示窗口 Thread thread = new Thread(() => { mouseTimer.Start(); while (!completed) { Application.DoEvents(); Thread.Sleep(100); } mouseTimer.Stop(); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); thread.Join(); } private static Dictionary GetWindowInfoFromHandle(IntPtr hwnd) { var windowInfo = new Dictionary(); if (hwnd != IntPtr.Zero) { 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, ref rect); windowInfo.Add("title", title.ToString()); windowInfo.Add("class", className.ToString()); windowInfo.Add("handle", hwnd.ToInt32()); windowInfo.Add("x", rect.Left); windowInfo.Add("y", rect.Top); windowInfo.Add("width", rect.Right - rect.Left); windowInfo.Add("height", rect.Bottom - rect.Top); // 获取进程信息 uint processId; GetWindowThreadProcessId(hwnd, out processId); try { var process = System.Diagnostics.Process.GetProcessById((int)processId); windowInfo.Add("processName", process.ProcessName); windowInfo.Add("processPath", process.MainModule.FileName); } catch { } } return windowInfo; } // 打印元素的完整信息 private static void InspectElementInfo(AutomationElement element, Point position) { var hierarchyInfo = GetElementHierarchyInfo(element); Dictionary result = new Dictionary(); // 元素信息 Dictionary elementInfo = new Dictionary(); elementInfo.Add("name", element.Current.Name); elementInfo.Add("class", element.Current.ClassName); elementInfo.Add("type", element.Current.ControlType.ProgrammaticName.Replace("ControlType.", "")); elementInfo.Add("automationId", element.Current.AutomationId); elementInfo.Add("xpath", hierarchyInfo.XPath); elementInfo.Add("handle", element.Current.NativeWindowHandle); // 添加元素位置和大小信息 var bounds = element.Current.BoundingRectangle; elementInfo.Add("x", (int)bounds.Left); elementInfo.Add("y", (int)bounds.Top); elementInfo.Add("width", (int)bounds.Width); elementInfo.Add("height", (int)bounds.Height); // 添加控件文本值 try { var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern; if (valuePattern != null) { elementInfo.Add("value", valuePattern.Current.Value); } } catch { } // 根据传入的坐标获取窗口信息 if (hierarchyInfo.WindowHandle != IntPtr.Zero) { var windowInfo = GetWindowInfoFromHandle(hierarchyInfo.WindowHandle); foreach (var kvp in windowInfo) { result.Add(kvp.Key, kvp.Value); } } // 将元素信息添加到结果中 result.Add("element", elementInfo); // 添加坐标信息 result.Add("position", new Dictionary { { "x", position.X }, { "y", position.Y } }); var serializer = new JavaScriptSerializer(); Console.Write(serializer.Serialize(result)); } private static AutomationElement HandleTaskbarElement(AutomationElement element, Point cursorPos, ref AutomationElement taskbarElement, ref List taskbarChildren) { // 检查是否是任务栏元素 bool isTaskbarElement = element.Current.ClassName == "Shell_TrayWnd" || element.Current.ClassName == "Shell_SecondaryTrayWnd"; // 如果是新的任务栏元素,获取其所有子元素 if (isTaskbarElement && element != taskbarElement) { taskbarElement = GetTaskbarElement(); if (taskbarElement != null) { taskbarChildren = GetTaskbarChildren(taskbarElement); } } // 如果是任务栏区域,在缓存的子元素中查找 if (isTaskbarElement && taskbarChildren != null && taskbarChildren.Count > 0) { var point = cursorPos.ToWindowsPoint(); AutomationElement bestMatch = null; double minArea = double.MaxValue; foreach (var child in taskbarChildren) { try { var childRect = child.Cached.BoundingRectangle; if (childRect.Contains(point)) { double area = childRect.Width * childRect.Height; if (area < minArea && area > 0) { minArea = area; bestMatch = child; } } } catch { } } if (bestMatch != null) { return bestMatch; } } return element; } }