automation实现inspect元素

This commit is contained in:
fofolee 2025-01-15 01:33:28 +08:00
parent 021cce5947
commit a3dc6479f2
11 changed files with 933 additions and 263 deletions

9
automation.cs Normal file
View File

@ -0,0 +1,9 @@
// 如果指定了过滤条件,创建一个组合条件
if (!string.IsNullOrEmpty(filter))
{
searchCondition = new OrCondition(
new PropertyCondition(AutomationElement.NameProperty, filter, PropertyConditionFlags.IgnoreCase),
new PropertyCondition(AutomationElement.ClassNameProperty, filter, PropertyConditionFlags.IgnoreCase),
new PropertyCondition(AutomationElement.AutomationIdProperty, filter, PropertyConditionFlags.IgnoreCase)
);
}

View File

@ -9,9 +9,16 @@ using System.Web.Script.Serialization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using System.Drawing;
public class AutomationManager public class AutomationManager
{ {
// UIA Control Type IDs
private static class UIA_ControlTypeIds
{
public const int Window = 50032;
}
// 用于缓存找到的元素 // 用于缓存找到的元素
private static Dictionary<string, AutomationElement> elementCache = new Dictionary<string, AutomationElement>(); private static Dictionary<string, AutomationElement> elementCache = new Dictionary<string, AutomationElement>();
@ -59,6 +66,15 @@ public class AutomationManager
[DllImport("user32.dll")] [DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey); private static extern short GetAsyncKeyState(int vKey);
[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 BlockInput(bool fBlockIt);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public static void Main(string[] args) public static void Main(string[] args)
{ {
if (args.Length == 0 || args[0] == "-h" || args[0] == "--help") if (args.Length == 0 || args[0] == "-h" || args[0] == "--help")
@ -79,17 +95,10 @@ public class AutomationManager
switch (type.ToLower()) switch (type.ToLower())
{ {
case "list": case "list":
// 列出所有元素
string filter = GetArgumentValue(args, "-filter"); // 可选的过滤条件
ListElements(args, filter);
break;
case "find": case "find":
// 查找元素 // 列出所有元素或查找元素
string by = GetArgumentValue(args, "-by") ?? "name"; string filter = GetArgumentValue(args, "-value") ?? GetArgumentValue(args, "-filter"); // 兼容两种参数名
string value = GetArgumentValue(args, "-value"); ListElements(args, filter);
string scope = GetArgumentValue(args, "-scope") ?? "children";
FindElement(args, by, value, scope);
break; break;
case "getinfo": case "getinfo":
@ -135,8 +144,8 @@ public class AutomationManager
case "wait": case "wait":
// 等待元素 // 等待元素
by = GetArgumentValue(args, "-by") ?? "name"; string by = GetArgumentValue(args, "-by") ?? "name";
value = GetArgumentValue(args, "-value"); string value = GetArgumentValue(args, "-value");
int timeout = int.Parse(GetArgumentValue(args, "-timeout") ?? "30"); int timeout = int.Parse(GetArgumentValue(args, "-timeout") ?? "30");
WaitForElement(args, by, value, timeout); WaitForElement(args, by, value, timeout);
break; break;
@ -171,8 +180,7 @@ public class AutomationManager
case "inspect": case "inspect":
// 生成元素识别器 // 生成元素识别器
int inspectTimeout = int.Parse(GetArgumentValue(args, "-timeout") ?? "30"); InspectElement(args);
InspectElement(args, inspectTimeout);
break; break;
default: default:
@ -203,6 +211,7 @@ public class AutomationManager
- process - process
- active () - active ()
-background ()
: :
-value <> -value <>
@ -215,10 +224,8 @@ public class AutomationManager
: :
1. inspect - 1. inspect -
:
-timeout <> (,30)
: :
automation.exe -type inspect -timeout 60 automation.exe -type inspect
: :
@ -465,7 +472,9 @@ public class AutomationManager
var info = new Dictionary<string, object>(); var info = new Dictionary<string, object>();
info.Add("name", element.Current.Name); info.Add("name", element.Current.Name);
info.Add("class", element.Current.ClassName); info.Add("class", element.Current.ClassName);
info.Add("type", element.Current.ControlType.ProgrammaticName); info.Add("controlType", element.Current.ControlType.ProgrammaticName);
info.Add("controlClass", element.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
info.Add("localizedType", element.Current.LocalizedControlType);
info.Add("automationId", element.Current.AutomationId); info.Add("automationId", element.Current.AutomationId);
info.Add("processId", element.Current.ProcessId); info.Add("processId", element.Current.ProcessId);
info.Add("boundingRectangle", new info.Add("boundingRectangle", new
@ -486,7 +495,18 @@ public class AutomationManager
string identifier = GetArgumentValue(args, "-value"); string identifier = GetArgumentValue(args, "-value");
string by = GetArgumentValue(args, "-by"); string by = GetArgumentValue(args, "-by");
string pattern = GetArgumentValue(args, "-pattern") ?? "invoke"; string pattern = GetArgumentValue(args, "-pattern") ?? "invoke";
bool background = HasArgument(args, "-background");
var element = GetElementByIdentifier(args, identifier, by); var element = GetElementByIdentifier(args, identifier, by);
// 只在非后台操作时激活窗口
if (!background)
{
var hwnd = new IntPtr(element.Current.NativeWindowHandle);
SetForegroundWindow(hwnd);
Thread.Sleep(50); // 等待窗口激活
}
switch (pattern.ToLower()) switch (pattern.ToLower())
{ {
case "invoke": case "invoke":
@ -512,6 +532,11 @@ public class AutomationManager
throw new Exception("元素不支持指定的操作模式"); throw new Exception("元素不支持指定的操作模式");
} }
private static bool HasArgument(string[] args, string key)
{
return args.Contains(key, StringComparer.OrdinalIgnoreCase);
}
private static string[] GetSupportedPatterns(AutomationElement element) private static string[] GetSupportedPatterns(AutomationElement element)
{ {
var patterns = new List<string>(); var patterns = new List<string>();
@ -527,8 +552,18 @@ public class AutomationManager
{ {
string identifier = GetArgumentValue(args, "-value"); string identifier = GetArgumentValue(args, "-value");
string by = GetArgumentValue(args, "-by"); string by = GetArgumentValue(args, "-by");
bool background = HasArgument(args, "-background");
var element = GetElementByIdentifier(args, identifier, by); var element = GetElementByIdentifier(args, identifier, by);
// 只在非后台操作时激活窗口
if (!background)
{
var hwnd = new IntPtr(element.Current.NativeWindowHandle);
SetForegroundWindow(hwnd);
Thread.Sleep(50); // 等待窗口激活
}
// 尝试ValuePattern // 尝试ValuePattern
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern; var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (valuePattern != null) if (valuePattern != null)
@ -698,7 +733,7 @@ public class AutomationManager
{ {
try try
{ {
FindElement(args, by, value, "subtree"); ListElements(args, value);
Console.WriteLine("成功找到元素"); Console.WriteLine("成功找到元素");
return; return;
} }
@ -734,15 +769,25 @@ public class AutomationManager
private static void SendKeys(string[] args, string keys) private static void SendKeys(string[] args, string keys)
{ {
string identifier = GetArgumentValue(args, "-value");
string by = GetArgumentValue(args, "-by");
bool background = HasArgument(args, "-background");
var element = GetElementByIdentifier(args, identifier, by);
// 只在非后台操作时激活窗口
if (!background)
{
var hwnd = new IntPtr(element.Current.NativeWindowHandle);
SetForegroundWindow(hwnd);
Thread.Sleep(50); // 等待窗口激活
}
if (string.IsNullOrEmpty(keys)) if (string.IsNullOrEmpty(keys))
{ {
throw new Exception("必须指定要发送的按键"); throw new Exception("必须指定要发送的按键");
} }
string identifier = GetArgumentValue(args, "-value");
string by = GetArgumentValue(args, "-by");
var element = GetElementByIdentifier(args, identifier, by);
// 确保元素可以接收输入 // 确保元素可以接收输入
if (!element.Current.IsKeyboardFocusable) if (!element.Current.IsKeyboardFocusable)
{ {
@ -824,7 +869,9 @@ public class AutomationManager
childInfo.Add("id", CacheElement(child)); childInfo.Add("id", CacheElement(child));
childInfo.Add("name", child.Current.Name); childInfo.Add("name", child.Current.Name);
childInfo.Add("class", child.Current.ClassName); childInfo.Add("class", child.Current.ClassName);
childInfo.Add("type", child.Current.ControlType.ProgrammaticName); childInfo.Add("controlType", child.Current.ControlType.ProgrammaticName);
childInfo.Add("type", child.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
childInfo.Add("localizedType", child.Current.LocalizedControlType);
childInfo.Add("automationId", child.Current.AutomationId); childInfo.Add("automationId", child.Current.AutomationId);
result.Add(childInfo); result.Add(childInfo);
} }
@ -846,7 +893,9 @@ public class AutomationManager
parentInfo.Add("id", CacheElement(parent)); parentInfo.Add("id", CacheElement(parent));
parentInfo.Add("name", parent.Current.Name); parentInfo.Add("name", parent.Current.Name);
parentInfo.Add("class", parent.Current.ClassName); parentInfo.Add("class", parent.Current.ClassName);
parentInfo.Add("type", parent.Current.ControlType.ProgrammaticName); parentInfo.Add("controlType", parent.Current.ControlType.ProgrammaticName);
parentInfo.Add("type", parent.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
parentInfo.Add("localizedType", parent.Current.LocalizedControlType);
parentInfo.Add("automationId", parent.Current.AutomationId); parentInfo.Add("automationId", parent.Current.AutomationId);
var serializer = new JavaScriptSerializer(); var serializer = new JavaScriptSerializer();
@ -891,33 +940,132 @@ public class AutomationManager
{ {
var root = GetRootElement(args); var root = GetRootElement(args);
var result = new List<Dictionary<string, string>>(); var result = new List<Dictionary<string, string>>();
string window = GetArgumentValue(args, "-window");
string method = GetArgumentValue(args, "-method") ?? "active";
string by = GetArgumentValue(args, "-by") ?? "name";
string scope = GetArgumentValue(args, "-scope") ?? "children";
// 获取所有元素 // 确定搜索范围
var elements = root.FindAll( TreeScope searchScope = TreeScope.Children;
TreeScope.Subtree, switch (scope.ToLower())
Condition.TrueCondition
);
foreach (AutomationElement element in elements)
{ {
// 如果指定了过滤条件,检查元素名称是否包含过滤文本 case "descendants":
if (!string.IsNullOrEmpty(filter) && searchScope = TreeScope.Descendants;
!element.Current.Name.Contains(filter) && break;
!element.Current.ClassName.Contains(filter) && case "subtree":
!element.Current.AutomationId.Contains(filter)) searchScope = TreeScope.Subtree;
{ break;
continue;
} }
Condition searchCondition = Condition.TrueCondition;
// 如果指定了过滤条件,创建一个组合条件
if (!string.IsNullOrEmpty(filter))
{
// 尝试直接使用ControlType
if (filter.StartsWith("ControlType."))
{
string controlTypeName = filter.Substring("ControlType.".Length);
ControlType controlType = (ControlType)typeof(ControlType).GetField(controlTypeName).GetValue(null);
searchCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, controlType);
}
else
{
switch (by.ToLower())
{
case "name":
searchCondition = new PropertyCondition(AutomationElement.NameProperty, filter, PropertyConditionFlags.IgnoreCase);
break;
case "class":
searchCondition = new PropertyCondition(AutomationElement.ClassNameProperty, filter, PropertyConditionFlags.IgnoreCase);
break;
case "automation":
searchCondition = new PropertyCondition(AutomationElement.AutomationIdProperty, filter, PropertyConditionFlags.IgnoreCase);
break;
case "xpath":
// XPath暂不支持
throw new Exception("XPath查找暂未实现");
default:
// 如果没有指定查找方式,则使用组合条件
searchCondition = new OrCondition(
new PropertyCondition(AutomationElement.NameProperty, filter, PropertyConditionFlags.IgnoreCase),
new PropertyCondition(AutomationElement.ClassNameProperty, filter, PropertyConditionFlags.IgnoreCase),
new PropertyCondition(AutomationElement.AutomationIdProperty, filter, PropertyConditionFlags.IgnoreCase)
);
break;
}
}
}
// 如果是按标题搜索,先找到所有匹配的窗口
if (method.ToLower() == "title" && !string.IsNullOrEmpty(window))
{
searchScope = TreeScope.Children;
var windows = root.FindAll(searchScope, Condition.TrueCondition);
foreach (AutomationElement win in windows)
{
if (win.Current.Name.Contains(window))
{
// 对每个匹配的窗口进行处理
var windowElements = win.FindAll(TreeScope.Subtree, searchCondition);
foreach (AutomationElement element in windowElements)
{
var elementInfo = new Dictionary<string, string>(); var elementInfo = new Dictionary<string, string>();
elementInfo.Add("id", CacheElement(element)); elementInfo.Add("id", CacheElement(element));
elementInfo.Add("name", element.Current.Name); elementInfo.Add("name", element.Current.Name);
elementInfo.Add("class", element.Current.ClassName); elementInfo.Add("class", element.Current.ClassName);
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName); elementInfo.Add("controlType", element.Current.ControlType.ProgrammaticName);
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
elementInfo.Add("localizedType", element.Current.LocalizedControlType);
elementInfo.Add("automationId", element.Current.AutomationId); elementInfo.Add("automationId", element.Current.AutomationId);
elementInfo.Add("path", GetElementPath(element)); elementInfo.Add("path", GetElementPath(element));
// 添加控件文本值
try
{
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (valuePattern != null)
{
elementInfo.Add("value", valuePattern.Current.Value);
}
}
catch { }
result.Add(elementInfo); result.Add(elementInfo);
} }
}
}
}
else
{
// 其他查找方式保持原有逻辑
var elements = root.FindAll(searchScope, searchCondition);
foreach (AutomationElement element in elements)
{
var elementInfo = new Dictionary<string, string>();
elementInfo.Add("id", CacheElement(element));
elementInfo.Add("name", element.Current.Name);
elementInfo.Add("class", element.Current.ClassName);
elementInfo.Add("controlType", element.Current.ControlType.ProgrammaticName);
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
elementInfo.Add("localizedType", element.Current.LocalizedControlType);
elementInfo.Add("automationId", element.Current.AutomationId);
elementInfo.Add("path", GetElementPath(element));
// 添加控件文本值
try
{
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (valuePattern != null)
{
elementInfo.Add("value", valuePattern.Current.Value);
}
}
catch { }
result.Add(elementInfo);
}
}
var serializer = new JavaScriptSerializer(); var serializer = new JavaScriptSerializer();
Console.Write(serializer.Serialize(result)); Console.Write(serializer.Serialize(result));
@ -963,7 +1111,15 @@ public class AutomationManager
string window = GetArgumentValue(args, "-window"); string window = GetArgumentValue(args, "-window");
string method = GetArgumentValue(args, "-method") ?? "active"; string method = GetArgumentValue(args, "-method") ?? "active";
if (string.IsNullOrEmpty(window) && method == "active") // 如果是active方法直接返回当前活动窗口
if (method.ToLower() == "active")
{
IntPtr activeHandle = GetForegroundWindow();
return AutomationElement.FromHandle(activeHandle);
}
// 其他方法需要检查window参数
if (string.IsNullOrEmpty(window))
{ {
return AutomationElement.RootElement; return AutomationElement.RootElement;
} }
@ -977,10 +1133,15 @@ public class AutomationManager
case "class": case "class":
// 通过窗口类名查找 // 通过窗口类名查找
return AutomationElement.RootElement.FindFirst( var classElements = AutomationElement.RootElement.FindAll(
TreeScope.Children, TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, window) new PropertyCondition(AutomationElement.ClassNameProperty, window)
); );
if (classElements.Count > 0)
{
return classElements[0];
}
break;
case "process": case "process":
// 通过进程名查找 // 通过进程名查找
@ -991,151 +1152,199 @@ public class AutomationManager
} }
break; break;
case "active":
// 获取当前活动窗口
IntPtr activeHandle = GetForegroundWindow();
return AutomationElement.FromHandle(activeHandle);
case "title": case "title":
default: default:
// 通过窗口标题查找(支持模糊匹配) // 返回根元素,让调用方自己处理查找逻辑
var windows = AutomationElement.RootElement.FindAll( return AutomationElement.RootElement;
TreeScope.Children,
Condition.TrueCondition
);
foreach (AutomationElement win in windows)
{
if (win.Current.Name.Contains(window))
{
return win;
}
}
break;
} }
throw new Exception("找不到指定的窗口"); throw new Exception("找不到指定的窗口");
} }
private static void FindElement(string[] args, string by, string value, string scope) private static void InspectElement(string[] args)
{ {
if (string.IsNullOrEmpty(value)) // 创建一个半透明的全屏窗口来捕获鼠标事件
{ Form overlayForm = new Form();
throw new Exception("必须指定查找值 (-value)"); overlayForm.FormBorderStyle = FormBorderStyle.None;
} overlayForm.WindowState = FormWindowState.Maximized;
overlayForm.TopMost = true;
overlayForm.BackColor = Color.Black;
overlayForm.Opacity = 0.1; // 设置为淡淡的蒙版效果
overlayForm.Cursor = Cursors.Cross;
overlayForm.ShowInTaskbar = false;
AutomationElement root = GetRootElement(args); bool completed = false;
TreeScope treeScope = TreeScope.Children;
switch (scope.ToLower())
{
case "descendants":
treeScope = TreeScope.Descendants;
break;
case "subtree":
treeScope = TreeScope.Subtree;
break;
}
Condition condition; overlayForm.MouseClick += (sender, e) => {
switch (by.ToLower()) if (e.Button == MouseButtons.Left) {
{ try {
case "name": Point clickPosition;
condition = new PropertyCondition(AutomationElement.NameProperty, value); GetCursorPos(out clickPosition);
break;
case "class":
condition = new PropertyCondition(AutomationElement.ClassNameProperty, value);
break;
case "automation":
condition = new PropertyCondition(AutomationElement.AutomationIdProperty, value);
break;
case "xpath":
FindElementByXPath(value);
return;
default:
throw new Exception("不支持的查找方式: " + by);
}
var elements = root.FindAll(treeScope, condition); // 先关闭覆盖窗口
var result = new List<Dictionary<string, string>>(); overlayForm.Hide();
foreach (AutomationElement element in elements) // 等待一小段时间确保窗口完全消失
{ Thread.Sleep(100);
var elementInfo = new Dictionary<string, string>();
elementInfo.Add("id", CacheElement(element));
elementInfo.Add("name", element.Current.Name);
elementInfo.Add("class", element.Current.ClassName);
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName);
elementInfo.Add("automationId", element.Current.AutomationId);
result.Add(elementInfo);
}
var serializer = new JavaScriptSerializer(); // 获取点击位置的元素
Console.Write(serializer.Serialize(result)); var element = AutomationElement.FromPoint(clickPosition.ToWindowsPoint());
}
private static void InspectElement(string[] args, int timeout) if (element != null && element != AutomationElement.RootElement) {
{ InspectElementInfo(element);
Console.WriteLine("请在" + timeout + "秒内点击要识别的元素..."); completed = true;
// 记录当前鼠标位置
DateTime endTime = DateTime.Now.AddSeconds(timeout);
AutomationElement clickedElement = null;
bool wasPressed = false;
while (DateTime.Now < endTime)
{
// 检测鼠标左键点击
bool isPressed = (GetAsyncKeyState(0x01) & 0x8000) != 0;
if (isPressed && !wasPressed) // 鼠标按下瞬间
{
// 获取当前鼠标位置
Point currentPosition;
GetCursorPos(out currentPosition);
try
{
// 从鼠标位置获取元素
clickedElement = AutomationElement.FromPoint(currentPosition.ToWindowsPoint());
if (clickedElement != null && clickedElement != AutomationElement.RootElement)
{
// 构建元素信息
var elementInfo = new Dictionary<string, object>();
elementInfo.Add("id", CacheElement(clickedElement));
elementInfo.Add("name", clickedElement.Current.Name);
elementInfo.Add("class", clickedElement.Current.ClassName);
elementInfo.Add("type", clickedElement.Current.ControlType.ProgrammaticName);
elementInfo.Add("automationId", clickedElement.Current.AutomationId);
elementInfo.Add("path", GetElementPath(clickedElement));
elementInfo.Add("location", new {
x = clickedElement.Current.BoundingRectangle.X,
y = clickedElement.Current.BoundingRectangle.Y,
width = clickedElement.Current.BoundingRectangle.Width,
height = clickedElement.Current.BoundingRectangle.Height
});
elementInfo.Add("patterns", GetSupportedPatterns(clickedElement));
// 生成示例命令
var commands = new List<string>();
if (!string.IsNullOrEmpty(clickedElement.Current.Name))
commands.Add(string.Format("automation.exe -type find -by name -value \"{0}\"", clickedElement.Current.Name));
if (!string.IsNullOrEmpty(clickedElement.Current.AutomationId))
commands.Add(string.Format("automation.exe -type find -by automationid -value \"{0}\"", clickedElement.Current.AutomationId));
elementInfo.Add("commands", commands);
var serializer = new JavaScriptSerializer();
Console.Write(serializer.Serialize(elementInfo));
return;
} }
} }
catch catch (Exception ex) {
{ Console.Error.WriteLine(string.Format("Error: {0}", ex.Message));
// 忽略获取元素时的错误 }
finally {
overlayForm.Close();
} }
} }
};
wasPressed = isPressed; overlayForm.KeyPress += (sender, e) => {
if (e.KeyChar == (char)27) { // ESC键
overlayForm.Close();
Environment.Exit(0); // 直接退出程序
}
};
// 在新线程中显示窗口
Thread thread = new Thread(() => {
overlayForm.Show();
while (!completed) {
Application.DoEvents();
Thread.Sleep(100); Thread.Sleep(100);
} }
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
throw new Exception("操作超时"); private static void InspectElementInfo(AutomationElement element)
{
Dictionary<string, object> elementInfo = new Dictionary<string, object>();
// 基本信息
elementInfo.Add("id", element.Current.AutomationId);
elementInfo.Add("name", element.Current.Name);
elementInfo.Add("class", element.Current.ClassName);
elementInfo.Add("controlType", element.Current.ControlType.ProgrammaticName);
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
elementInfo.Add("localizedType", element.Current.LocalizedControlType);
elementInfo.Add("automationId", element.Current.AutomationId);
elementInfo.Add("path", GetElementPath(element));
elementInfo.Add("handle", element.Current.NativeWindowHandle);
// 添加控件文本值
try
{
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (valuePattern != null)
{
elementInfo.Add("value", valuePattern.Current.Value);
}
}
catch { }
// 获取窗口信息
var walker = TreeWalker.ControlViewWalker;
var parent = element;
while (parent != null && parent != AutomationElement.RootElement && parent.Current.ControlType.Id != UIA_ControlTypeIds.Window)
{
parent = walker.GetParent(parent);
}
// 添加窗口详细信息
if (parent != null && parent != AutomationElement.RootElement)
{
var processId = parent.Current.ProcessId;
var process = System.Diagnostics.Process.GetProcessById(processId);
elementInfo.Add("window", new
{
title = parent.Current.Name,
@class = parent.Current.ClassName,
handle = parent.Current.NativeWindowHandle,
processName = process.ProcessName,
processId = processId,
processPath = process.MainModule.FileName
});
}
// 添加父元素信息
var immediateParent = walker.GetParent(element);
if (immediateParent != null && immediateParent != AutomationElement.RootElement)
{
elementInfo.Add("parent", new
{
handle = immediateParent.Current.NativeWindowHandle,
path = GetElementPath(immediateParent),
@class = immediateParent.Current.ClassName,
name = immediateParent.Current.Name,
controlType = immediateParent.Current.ControlType.ProgrammaticName,
type = immediateParent.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""),
localizedType = immediateParent.Current.LocalizedControlType,
automationId = immediateParent.Current.AutomationId
});
}
// 位置信息
elementInfo.Add("location", new {
x = element.Current.BoundingRectangle.X,
y = element.Current.BoundingRectangle.Y,
width = element.Current.BoundingRectangle.Width,
height = element.Current.BoundingRectangle.Height
});
// 支持的模式
List<string> patterns = new List<string>();
foreach (AutomationPattern pattern in element.GetSupportedPatterns())
{
patterns.Add(pattern.ProgrammaticName);
}
elementInfo.Add("patterns", patterns);
// 输出 JSON
var serializer = new JavaScriptSerializer();
Console.Write(serializer.Serialize(elementInfo));
}
// 序列化为 JSON 字符串
private static string JsonSerialize(object obj)
{
if (obj == null) return "null";
if (obj is string)
{
return string.Format("\"{0}\"", ((string)obj).Replace("\"", "\\\""));
}
if (obj is bool)
{
return obj.ToString().ToLower();
}
if (obj is int || obj is long || obj is float || obj is double)
{
return obj.ToString();
}
if (obj is Dictionary<string, object>)
{
var dict = obj as Dictionary<string, object>;
var pairs = dict.Select(kvp => string.Format("\"{0}\": {1}", kvp.Key, JsonSerialize(kvp.Value)));
return string.Format("{{{0}}}", string.Join(", ", pairs));
}
if (obj is System.Collections.IEnumerable)
{
var items = ((System.Collections.IEnumerable)obj).Cast<object>().Select(item => JsonSerialize(item));
return string.Format("[{0}]", string.Join(", ", items));
}
return "null";
} }
} }

View File

@ -4,6 +4,8 @@ const iconv = require("iconv-lite");
const child_process = require("child_process"); const child_process = require("child_process");
const { getQuickcommandFolderFile } = require("../getQuickcommandFile"); const { getQuickcommandFolderFile } = require("../getQuickcommandFile");
let currentChild = null;
const getAssemblyPath = (assembly) => { const getAssemblyPath = (assembly) => {
const { version } = getCscPath(); const { version } = getCscPath();
const is64bit = process.arch === "x64"; const is64bit = process.arch === "x64";
@ -37,15 +39,9 @@ const getAssemblyPath = (assembly) => {
// v3.0/v3.5 路径 // v3.0/v3.5 路径
path.join( path.join(
process.env["ProgramFiles(x86)"] || process.env.ProgramFiles, process.arch === "x64"
"Reference Assemblies", ? process.env.ProgramFiles
"Microsoft", : process.env["ProgramFiles(x86)"],
"Framework",
"v3.0",
assembly + ".dll"
),
path.join(
process.env.ProgramFiles,
"Reference Assemblies", "Reference Assemblies",
"Microsoft", "Microsoft",
"Framework", "Framework",
@ -81,13 +77,16 @@ const getFeatureReferences = (feature) => {
const formsDll = getAssemblyPath("System.Windows.Forms"); const formsDll = getAssemblyPath("System.Windows.Forms");
const typesDll = getAssemblyPath("UIAutomationTypes"); const typesDll = getAssemblyPath("UIAutomationTypes");
const baseDll = getAssemblyPath("WindowsBase"); const baseDll = getAssemblyPath("WindowsBase");
const drawingDll = getAssemblyPath("System.Drawing");
if (!automationDll) throw new Error("找不到UIAutomationClient.dll"); if (!automationDll) throw new Error("找不到UIAutomationClient.dll");
if (!formsDll) throw new Error("找不到System.Windows.Forms.dll"); if (!formsDll) throw new Error("找不到System.Windows.Forms.dll");
if (!typesDll) throw new Error("找不到UIAutomationTypes.dll"); if (!typesDll) throw new Error("找不到UIAutomationTypes.dll");
if (!baseDll) throw new Error("找不到WindowsBase.dll"); if (!baseDll) throw new Error("找不到WindowsBase.dll");
if (!drawingDll) throw new Error("找不到System.Drawing.dll");
references = references =
`/reference:"${automationDll}" /reference:"${formsDll}" ` + `/reference:"${automationDll}" /reference:"${formsDll}" ` +
`/reference:"${typesDll}" /reference:"${baseDll}" `; `/reference:"${typesDll}" /reference:"${baseDll}" ` +
`/reference:"${drawingDll}" `;
} }
return references; return references;
}; };
@ -128,10 +127,11 @@ const buildCsharpFeature = async (feature) => {
}; };
const getCscPath = () => { const getCscPath = () => {
const is64bit = process.arch === "x64";
let cscPath = path.join( let cscPath = path.join(
process.env.WINDIR, process.env.WINDIR,
"Microsoft.NET", "Microsoft.NET",
"Framework", is64bit ? "Framework64" : "Framework",
"v4.0.30319", "v4.0.30319",
"csc.exe" "csc.exe"
); );
@ -141,7 +141,7 @@ const getCscPath = () => {
cscPath = path.join( cscPath = path.join(
process.env.WINDIR, process.env.WINDIR,
"Microsoft.NET", "Microsoft.NET",
"Framework", is64bit ? "Framework64" : "Framework",
"v3.5", "v3.5",
"csc.exe" "csc.exe"
); );
@ -163,13 +163,17 @@ const getCscPath = () => {
*/ */
const runCsharpFeature = async (feature, args = [], options = {}) => { const runCsharpFeature = async (feature, args = [], options = {}) => {
return new Promise(async (reslove, reject) => { return new Promise(async (reslove, reject) => {
const { alwaysBuildNewExe = false } = options; const { alwaysBuildNewExe = window.utools.isDev(), killPrevious = true } =
options;
try { try {
const featureExePath = await getCsharpFeatureExe( const featureExePath = await getCsharpFeatureExe(
feature, feature,
alwaysBuildNewExe alwaysBuildNewExe
); );
child_process.execFile( if (killPrevious && currentChild) {
quickcommand.kill(currentChild.pid, "SIGKILL");
}
currentChild = child_process.execFile(
featureExePath, featureExePath,
args, args,
{ {

View File

@ -0,0 +1,165 @@
const { runCsharpFeature } = require("../../csharp");
/**
* 列出所有元素
* @param {string} method 窗口类型title/handle/active
* @param {string} window 窗口标题/句柄
* @param {object} options 选项
* @param {string} options.filter 过滤条件
* @param {boolean} options.background 是否后台操作
* @returns {object[]} 元素列表
*/
const listElements = async function (method, window, options = {}) {
const { filter, scope } = options;
const args = ["-type", "list"];
args.push("-method", method);
if (method !== "active" && window) args.push("-window", window);
if (filter) args.push("-filter", filter);
if (scope) args.push("-scope", scope);
const result = await runCsharpFeature("automation", args);
if (result && result.startsWith("Error:")) {
throw new Error(result.substring(7));
}
try {
return JSON.parse(result);
} catch (error) {
console.error("解析元素列表失败:", error);
return null;
}
};
/**
* 点击元素
* @param {string} method 窗口类型title/handle/active
* @param {string} window 窗口标题/句柄
* @param {string} by 查找方式name/class/type/automationid
* @param {string} value 查找值
* @param {object} options 选项
* @param {string} options.pattern 点击模式invoke/toggle
* @param {boolean} options.background 是否后台操作
* @returns {boolean} 是否成功
*/
const clickElement = async function (method, window, by, value, options = {}) {
const { pattern = "invoke", background = false } = options;
const args = ["-type", "click", "-by", by, "-value", value];
args.push("-method", method);
if (method !== "active" && window) args.push("-window", window);
if (pattern) args.push("-pattern", pattern);
if (background) args.push("-background");
const result = await runCsharpFeature("automation", args);
if (result && result.startsWith("Error:")) {
throw new Error(result.substring(7));
}
return true;
};
/**
* 设置元素值
* @param {string} method 窗口类型title/handle/active
* @param {string} window 窗口标题/句柄
* @param {string} by 查找方式name/class/type/automationid
* @param {string} value 查找值
* @param {string} newValue 要设置的值
* @param {object} options 选项
* @param {boolean} options.background 是否后台操作
* @returns {boolean} 是否成功
*/
const setElementValue = async function (
method,
window,
by,
value,
newValue,
options = {}
) {
const { background = false } = options;
const args = [
"-type",
"setvalue",
"-by",
by,
"-value",
value,
"-newvalue",
newValue,
];
args.push("-method", method);
if (method !== "active" && window) args.push("-window", window);
if (background) args.push("-background");
const result = await runCsharpFeature("automation", args);
if (result && result.startsWith("Error:")) {
throw new Error(result.substring(7));
}
return true;
};
/**
* 获取元素值
* @param {string} method 窗口类型title/handle/active
* @param {string} window 窗口标题/句柄
* @param {string} by 查找方式name/class/type/automationid
* @param {string} value 查找值
* @param {object} options 选项
* @param {boolean} options.background 是否后台操作
* @returns {object} 元素值
*/
const getElementValue = async function (
method,
window,
by,
value,
options = {}
) {
const { background = false } = options;
const args = ["-type", "getvalue", "-by", by, "-value", value];
args.push("-method", method);
if (method !== "active" && window) args.push("-window", window);
if (background) args.push("-background");
const result = await runCsharpFeature("automation", args);
if (result && result.startsWith("Error:")) {
throw new Error(result.substring(7));
}
try {
return JSON.parse(result);
} catch (error) {
console.error("解析元素值失败:", error);
return null;
}
};
/**
* 检查元素
* @param {object} options 选项
* @param {number} options.timeout 超时时间()
* @param {boolean} options.background 是否后台操作
* @returns {object} 元素信息
*/
const inspectElement = async function () {
const args = ["-type", "inspect"];
const result = await runCsharpFeature("automation", args);
if (result && result.startsWith("Error:")) {
throw new Error(result.substring(7));
}
try {
return JSON.parse(result);
} catch (error) {
console.error("解析元素信息失败:", error);
return null;
}
};
module.exports = {
listElements,
clickElement,
setElementValue,
getElementValue,
inspectElement,
};

View File

@ -6,6 +6,7 @@ const registry = require("./registry");
const service = require("./service"); const service = require("./service");
const software = require("./software"); const software = require("./software");
const utils = require("./utils"); const utils = require("./utils");
const automation = require("./automation");
module.exports = { module.exports = {
window, window,
@ -16,4 +17,5 @@ module.exports = {
service, service,
software, software,
utils, utils,
automation,
}; };

View File

@ -48,7 +48,7 @@ const getQuickcommandIconPath = () => {
}; };
// 修改对话框函数,使用新的 dialog.cs // 修改对话框函数,使用新的 dialog.cs
const showSystemMessageBox = async function (content, title = "") { const showSystemMessageBox = async function (content, title = "提示") {
try { try {
const iconPath = getQuickcommandIconPath(); const iconPath = getQuickcommandIconPath();
if (window.utools.isWindows()) { if (window.utools.isWindows()) {
@ -97,7 +97,7 @@ const showSystemMessageBox = async function (content, title = "") {
} }
}; };
const showSystemInputBox = async function (placeholders, title = "") { const showSystemInputBox = async function (placeholders, title = "请输入") {
if (!Array.isArray(placeholders)) { if (!Array.isArray(placeholders)) {
placeholders = [placeholders]; placeholders = [placeholders];
} }
@ -163,7 +163,7 @@ const showSystemInputBox = async function (placeholders, title = "") {
} }
}; };
const showSystemConfirmBox = async function (content, title = "") { const showSystemConfirmBox = async function (content, title = "请确认") {
const iconPath = getQuickcommandIconPath(); const iconPath = getQuickcommandIconPath();
if (window.utools.isMacOs()) { if (window.utools.isMacOs()) {
let iconParam = "note"; let iconParam = "note";
@ -207,7 +207,7 @@ const showSystemConfirmBox = async function (content, title = "") {
} }
}; };
const showSystemButtonBox = async function (buttons, title = "") { const showSystemButtonBox = async function (buttons, title = "请选择") {
const iconPath = getQuickcommandIconPath(); const iconPath = getQuickcommandIconPath();
if (window.utools.isMacOs()) { if (window.utools.isMacOs()) {
const itemList = buttons.map((item) => `"${item}"`).join(", "); const itemList = buttons.map((item) => `"${item}"`).join(", ");
@ -250,7 +250,7 @@ const showSystemButtonBox = async function (buttons, title = "") {
const itemsList = buttons const itemsList = buttons
.map((btn, index) => `"${index}" "${btn}"`) .map((btn, index) => `"${index}" "${btn}"`)
.join(" "); .join(" ");
const script2 = `zenity --list --title="${title}" --text="请选择:" --column="序号" --column="选项" ${itemsList} --width=400 --height=300`; const script2 = `zenity --list --title="${title}" --column="序号" --column="选项" ${itemsList} --width=400 --height=300`;
const result = await execCommand(script2); const result = await execCommand(script2);
if (!result) return {}; if (!result) return {};
const text = result.trim(); const text = result.trim();
@ -263,7 +263,7 @@ const showSystemButtonBox = async function (buttons, title = "") {
} }
}; };
const showSystemTextArea = async function (defaultText = "", title = "") { const showSystemTextArea = async function (defaultText = "", title = "请输入") {
const iconPath = getQuickcommandIconPath(); const iconPath = getQuickcommandIconPath();
if (window.utools.isWindows()) { if (window.utools.isWindows()) {
const args = [ const args = [

View File

@ -85,7 +85,9 @@ export default {
}, },
// --- // ---
isTable() { isTable() {
return this.runResult?.[0]?.split("\n")?.[1]?.includes("---"); const result = this.runResult?.[0];
if (typeof result !== "string") return false;
return result.split("\n")?.[1]?.includes("---");
}, },
}, },
mounted() { mounted() {

View File

@ -37,13 +37,7 @@ export default defineComponent({
}, },
// //
commonConfig() { commonConfig() {
return ( return this.modelValue.config || [];
// , excludeConfig[]
this.modelValue.config?.filter(
(_, index) =>
!this.getSelectSubCommand()?.excludeConfig?.includes(index)
) || []
);
}, },
// configconfig // configconfig
subCommandConfig() { subCommandConfig() {

View File

@ -53,14 +53,7 @@ export const networkCommands = {
label: "URL操作", label: "URL操作",
desc: "URL解析、格式化和参数处理", desc: "URL解析、格式化和参数处理",
icon: "link", icon: "link",
config: [ config: [],
{
label: "URL",
component: "VariableInput",
icon: "link",
width: "auto",
},
],
subCommands: [ subCommands: [
{ {
value: "quickcomposer.network.url.parse", value: "quickcomposer.network.url.parse",
@ -71,7 +64,6 @@ export const networkCommands = {
value: "quickcomposer.network.url.format", value: "quickcomposer.network.url.format",
label: "格式化URL", label: "格式化URL",
icon: "link", icon: "link",
excludeConfig: [0],
config: [ config: [
{ {
label: "协议", label: "协议",
@ -120,7 +112,6 @@ export const networkCommands = {
value: "quickcomposer.network.url.parseQuery", value: "quickcomposer.network.url.parseQuery",
label: "解析查询字符串", label: "解析查询字符串",
icon: "search", icon: "search",
excludeConfig: [0],
config: [ config: [
{ {
label: "查询字符串", label: "查询字符串",
@ -133,7 +124,6 @@ export const networkCommands = {
value: "quickcomposer.network.url.formatQuery", value: "quickcomposer.network.url.formatQuery",
label: "格式化查询字符串", label: "格式化查询字符串",
icon: "edit", icon: "edit",
excludeConfig: [0],
config: [ config: [
{ {
label: "参数", label: "参数",
@ -146,7 +136,6 @@ export const networkCommands = {
value: "quickcomposer.network.url.parsePath", value: "quickcomposer.network.url.parsePath",
label: "解析路径", label: "解析路径",
icon: "folder_open", icon: "folder_open",
excludeConfig: [0],
config: [ config: [
{ {
label: "路径", label: "路径",
@ -159,7 +148,6 @@ export const networkCommands = {
value: "quickcomposer.network.url.parseHost", value: "quickcomposer.network.url.parseHost",
label: "解析主机名", label: "解析主机名",
icon: "dns", icon: "dns",
excludeConfig: [0],
config: [ config: [
{ {
label: "主机名", label: "主机名",
@ -173,6 +161,12 @@ export const networkCommands = {
label: "获取参数", label: "获取参数",
icon: "find_in_page", icon: "find_in_page",
config: [ config: [
{
label: "URL",
component: "VariableInput",
icon: "link",
width: "auto",
},
{ {
label: "参数名", label: "参数名",
component: "VariableInput", component: "VariableInput",
@ -186,6 +180,12 @@ export const networkCommands = {
label: "添加参数", label: "添加参数",
icon: "add_circle", icon: "add_circle",
config: [ config: [
{
label: "URL",
component: "VariableInput",
icon: "link",
width: "auto",
},
{ {
label: "参数名", label: "参数名",
component: "VariableInput", component: "VariableInput",
@ -205,6 +205,12 @@ export const networkCommands = {
label: "移除参数", label: "移除参数",
icon: "remove_circle", icon: "remove_circle",
config: [ config: [
{
label: "URL",
component: "VariableInput",
icon: "link",
width: "auto",
},
{ {
label: "参数名", label: "参数名",
component: "VariableInput", component: "VariableInput",
@ -217,11 +223,27 @@ export const networkCommands = {
value: "quickcomposer.network.url.isAbsolute", value: "quickcomposer.network.url.isAbsolute",
label: "检查绝对URL", label: "检查绝对URL",
icon: "check_circle", icon: "check_circle",
config: [
{
label: "URL",
component: "VariableInput",
icon: "link",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.network.url.parseComponents", value: "quickcomposer.network.url.parseComponents",
label: "解析组成部分", label: "解析组成部分",
icon: "category", icon: "category",
config: [
{
label: "URL",
component: "VariableInput",
icon: "link",
width: "auto",
},
],
}, },
], ],
}, },
@ -231,6 +253,12 @@ export const networkCommands = {
desc: "DNS解析和查询", desc: "DNS解析和查询",
icon: "dns", icon: "dns",
isAsync: true, isAsync: true,
config: [],
subCommands: [
{
label: "DNS查询",
value: "quickcomposer.network.dns.lookupHost",
icon: "search",
config: [ config: [
{ {
label: "要查询的域名", label: "要查询的域名",
@ -238,13 +266,6 @@ export const networkCommands = {
component: "VariableInput", component: "VariableInput",
width: "auto", width: "auto",
}, },
],
subCommands: [
{
label: "DNS查询",
value: "quickcomposer.network.dns.lookupHost",
icon: "search",
config: [
{ {
label: "IP版本", label: "IP版本",
icon: "settings_ethernet", icon: "settings_ethernet",
@ -269,42 +290,97 @@ export const networkCommands = {
value: "quickcomposer.network.dns.resolveAll", value: "quickcomposer.network.dns.resolveAll",
label: "解析所有记录", label: "解析所有记录",
icon: "all_inclusive", icon: "all_inclusive",
config: [
{
label: "要查询的域名",
icon: "dns",
component: "VariableInput",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.network.dns.resolveIpv4", value: "quickcomposer.network.dns.resolveIpv4",
label: "解析IPv4", label: "解析IPv4",
icon: "filter_4", icon: "filter_4",
config: [
{
label: "要查询的域名",
icon: "dns",
component: "VariableInput",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.network.dns.resolveIpv6", value: "quickcomposer.network.dns.resolveIpv6",
label: "解析IPv6", label: "解析IPv6",
icon: "filter_6", icon: "filter_6",
config: [
{
label: "要查询的域名",
icon: "dns",
component: "VariableInput",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.network.dns.resolveMxRecords", value: "quickcomposer.network.dns.resolveMxRecords",
label: "解析MX记录", label: "解析MX记录",
icon: "mail", icon: "mail",
config: [
{
label: "要查询的域名",
icon: "dns",
component: "VariableInput",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.network.dns.resolveTxtRecords", value: "quickcomposer.network.dns.resolveTxtRecords",
label: "解析TXT记录", label: "解析TXT记录",
icon: "text_fields", icon: "text_fields",
config: [
{
label: "要查询的域名",
icon: "dns",
component: "VariableInput",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.network.dns.resolveNsRecords", value: "quickcomposer.network.dns.resolveNsRecords",
label: "解析NS记录", label: "解析NS记录",
icon: "dns", icon: "dns",
config: [
{
label: "要查询的域名",
icon: "dns",
component: "VariableInput",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.network.dns.resolveCnameRecords", value: "quickcomposer.network.dns.resolveCnameRecords",
label: "解析CNAME记录", label: "解析CNAME记录",
icon: "link", icon: "link",
config: [
{
label: "要查询的域名",
icon: "dns",
component: "VariableInput",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.network.dns.reverseResolve", value: "quickcomposer.network.dns.reverseResolve",
label: "反向解析", label: "反向解析",
icon: "swap_horiz", icon: "swap_horiz",
excludeConfig: [0],
config: [ config: [
{ {
label: "IP地址", label: "IP地址",

View File

@ -308,6 +308,12 @@ export const systemCommands = {
label: "路径操作", label: "路径操作",
desc: "路径解析和处理", desc: "路径解析和处理",
icon: "folder", icon: "folder",
config: [],
subCommands: [
{
value: "quickcomposer.system.path.normalize",
label: "规范化路径",
icon: "straighten",
config: [ config: [
{ {
label: "路径", label: "路径",
@ -316,27 +322,44 @@ export const systemCommands = {
width: "auto", width: "auto",
}, },
], ],
subCommands: [
{
value: "quickcomposer.system.path.normalize",
label: "规范化路径",
icon: "straighten",
}, },
{ {
value: "quickcomposer.system.path.parse", value: "quickcomposer.system.path.parse",
label: "解析路径", label: "解析路径",
icon: "account_tree", icon: "account_tree",
config: [
{
label: "路径",
component: "VariableInput",
icon: "folder",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.system.path.dirname", value: "quickcomposer.system.path.dirname",
label: "获取目录名", label: "获取目录名",
icon: "folder", icon: "folder",
config: [
{
label: "路径",
component: "VariableInput",
icon: "folder",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.system.path.basename", value: "quickcomposer.system.path.basename",
label: "获取文件名", label: "获取文件名",
icon: "description", icon: "description",
config: [ config: [
{
label: "路径",
component: "VariableInput",
icon: "folder",
width: "auto",
},
{ {
label: "要移除的扩展名", label: "要移除的扩展名",
component: "VariableInput", component: "VariableInput",
@ -349,17 +372,32 @@ export const systemCommands = {
value: "quickcomposer.system.path.extname", value: "quickcomposer.system.path.extname",
label: "获取扩展名", label: "获取扩展名",
icon: "extension", icon: "extension",
config: [
{
label: "路径",
component: "VariableInput",
icon: "folder",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.system.path.isAbsolute", value: "quickcomposer.system.path.isAbsolute",
label: "判断绝对路径", label: "判断绝对路径",
icon: "check_circle", icon: "check_circle",
config: [
{
label: "路径",
component: "VariableInput",
icon: "folder",
width: "auto",
},
],
}, },
{ {
value: "quickcomposer.system.path.join", value: "quickcomposer.system.path.join",
label: "连接路径", label: "连接路径",
icon: "add_link", icon: "add_link",
excludeConfig: [0],
config: [ config: [
{ {
label: "路径片段", label: "路径片段",
@ -379,7 +417,6 @@ export const systemCommands = {
value: "quickcomposer.system.path.resolve", value: "quickcomposer.system.path.resolve",
label: "解析绝对路径", label: "解析绝对路径",
icon: "assistant_direction", icon: "assistant_direction",
excludeConfig: [0],
config: [ config: [
{ {
label: "路径片段", label: "路径片段",
@ -399,7 +436,6 @@ export const systemCommands = {
value: "quickcomposer.system.path.relative", value: "quickcomposer.system.path.relative",
label: "计算相对路径", label: "计算相对路径",
icon: "compare_arrows", icon: "compare_arrows",
excludeConfig: [0],
config: [ config: [
{ {
label: "起始路径", label: "起始路径",
@ -419,7 +455,6 @@ export const systemCommands = {
value: "quickcomposer.system.path.format", value: "quickcomposer.system.path.format",
label: "格式化路径", label: "格式化路径",
icon: "format_shapes", icon: "format_shapes",
excludeConfig: [0],
config: [ config: [
{ {
label: "根路径", label: "根路径",

View File

@ -104,17 +104,7 @@ const registryPaths = [
}, },
]; ];
export const windowsCommands = { const searchWindowConfig = [
label: "Win自动化",
icon: "window",
defaultOpened: false,
commands: [
{
value: "quickcomposer.windows.window.getWindowInfo",
label: "窗口控制",
desc: "Windows窗口操作",
icon: "window",
config: [
{ {
key: "method", key: "method",
label: "查找方式", label: "查找方式",
@ -123,7 +113,9 @@ export const windowsCommands = {
width: 3, width: 3,
options: [ options: [
{ label: "标题", value: "title" }, { label: "标题", value: "title" },
// { label: "类名", value: "class" },
{ label: "句柄", value: "handle" }, { label: "句柄", value: "handle" },
// { label: "进程", value: "process" },
{ label: "活动窗口", value: "active" }, { label: "活动窗口", value: "active" },
], ],
defaultValue: "title", defaultValue: "title",
@ -136,7 +128,19 @@ export const windowsCommands = {
width: 9, width: 9,
placeholder: "标题支持模糊匹配,选择活动窗口无需输入", placeholder: "标题支持模糊匹配,选择活动窗口无需输入",
}, },
], ];
export const windowsCommands = {
label: "Win自动化",
icon: "window",
defaultOpened: false,
commands: [
{
value: "quickcomposer.windows.window.getWindowInfo",
label: "窗口控制",
desc: "Windows窗口操作",
icon: "window",
config: searchWindowConfig,
subCommands: [ subCommands: [
{ {
value: "quickcomposer.windows.window.getWindowInfo", value: "quickcomposer.windows.window.getWindowInfo",
@ -357,7 +361,7 @@ export const windowsCommands = {
items: controlClass, items: controlClass,
}, },
width: 8, width: 8,
placeholder: "可选,输入要过滤的控件类或文本", placeholder: "可选,输入要过滤的控件类或文本",
}, },
background: { background: {
label: "后台操作", label: "后台操作",
@ -394,7 +398,7 @@ export const windowsCommands = {
width: 12, width: 12,
options: { options: {
control: { control: {
label: "控件类", label: "控件类",
component: "VariableInput", component: "VariableInput",
icon: "class", icon: "class",
options: { options: {
@ -408,7 +412,7 @@ export const windowsCommands = {
component: "VariableInput", component: "VariableInput",
icon: "text_fields", icon: "text_fields",
width: 6, width: 6,
placeholder: "可选,和控件类至少输入一个", placeholder: "可选,和控件类至少输入一个",
}, },
pos: { pos: {
label: "坐标", label: "坐标",
@ -1097,5 +1101,175 @@ export const windowsCommands = {
}, },
], ],
}, },
{
value: "quickcomposer.windows.automation.inspectElement",
label: "UI自动化",
desc: "Windows界面自动化操作",
icon: "smart_button",
isAsync: true,
config: [],
subCommands: [
{
value: "quickcomposer.windows.automation.inspectElement",
label: "检查元素",
icon: "search",
},
{
value: "quickcomposer.windows.automation.listElements",
label: "列出元素",
icon: "list",
config: [
...searchWindowConfig,
{
component: "OptionEditor",
icon: "settings",
width: 12,
options: {
scope: {
label: "范围",
component: "q-select",
icon: "account_tree",
width: 3,
options: [
{ label: "子元素", value: "children" },
{ label: "所有后代", value: "descendants" },
{ label: "整个子树", value: "subtree" },
],
},
filter: {
label: "过滤条件",
component: "VariableInput",
icon: "filter_alt",
width: 9,
placeholder: "可选,按名称/类名/ControlType/AutomationId过滤",
},
},
defaultValue: {
scope: "children",
},
},
],
},
{
value: "quickcomposer.windows.automation.clickElement",
label: "点击元素",
icon: "mouse",
config: [
...searchWindowConfig,
{
key: "by",
label: "查找方式",
component: "q-select",
icon: "search",
width: 4,
options: [
{ label: "名称", value: "name" },
{ label: "类名", value: "class" },
{ label: "类型", value: "type" },
{ label: "AutomationId", value: "automationid" },
],
defaultValue: "name",
},
{
key: "value",
label: "查找值",
component: "VariableInput",
icon: "text_fields",
width: 8,
placeholder: "要点击的元素值",
},
{
key: "pattern",
label: "点击模式",
component: "ButtonGroup",
icon: "touch_app",
width: 12,
options: [
{ label: "普通点击", value: "invoke" },
{ label: "切换状态", value: "toggle" },
],
defaultValue: "invoke",
},
{
key: "background",
label: "后台操作",
component: "CheckButton",
icon: "back_hand",
width: 12,
},
],
},
{
value: "quickcomposer.windows.automation.setElementValue",
label: "设置值",
icon: "edit",
config: [
...searchWindowConfig,
{
key: "by",
label: "查找方式",
component: "q-select",
icon: "search",
width: 4,
options: [
{ label: "名称", value: "name" },
{ label: "类名", value: "class" },
{ label: "类型", value: "type" },
{ label: "AutomationId", value: "automationid" },
],
defaultValue: "name",
},
{
key: "value",
label: "查找值",
component: "VariableInput",
icon: "text_fields",
width: 8,
placeholder: "要设置值的元素",
},
{
key: "newValue",
label: "新值",
component: "VariableInput",
icon: "edit",
width: 12,
placeholder: "要设置的新值",
},
],
},
{
value: "quickcomposer.windows.automation.getElementValue",
label: "获取值",
icon: "content_paste",
outputVariable: "elementValue",
saveOutput: true,
config: [
...searchWindowConfig,
{
key: "by",
label: "查找方式",
component: "q-select",
icon: "search",
width: 4,
options: [
{ label: "名称", value: "name" },
{ label: "类名", value: "class" },
{ label: "类型", value: "type" },
{ label: "AutomationId", value: "automationid" },
],
defaultValue: "name",
},
{
key: "value",
label: "查找值",
component: "VariableInput",
icon: "text_fields",
width: 8,
placeholder: "要获取值的元素",
},
],
},
],
},
], ],
}; };