mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-29 04:12:45 +08:00
automation实现inspect元素
This commit is contained in:
parent
021cce5947
commit
a3dc6479f2
9
automation.cs
Normal file
9
automation.cs
Normal 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)
|
||||
);
|
||||
}
|
@ -9,9 +9,16 @@ 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<string, AutomationElement> elementCache = new Dictionary<string, AutomationElement>();
|
||||
|
||||
@ -59,6 +66,15 @@ public class AutomationManager
|
||||
[DllImport("user32.dll")]
|
||||
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)
|
||||
{
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "--help")
|
||||
@ -79,17 +95,10 @@ public class AutomationManager
|
||||
switch (type.ToLower())
|
||||
{
|
||||
case "list":
|
||||
// 列出所有元素
|
||||
string filter = GetArgumentValue(args, "-filter"); // 可选的过滤条件
|
||||
ListElements(args, filter);
|
||||
break;
|
||||
|
||||
case "find":
|
||||
// 查找元素
|
||||
string by = GetArgumentValue(args, "-by") ?? "name";
|
||||
string value = GetArgumentValue(args, "-value");
|
||||
string scope = GetArgumentValue(args, "-scope") ?? "children";
|
||||
FindElement(args, by, value, scope);
|
||||
// 列出所有元素或查找元素
|
||||
string filter = GetArgumentValue(args, "-value") ?? GetArgumentValue(args, "-filter"); // 兼容两种参数名
|
||||
ListElements(args, filter);
|
||||
break;
|
||||
|
||||
case "getinfo":
|
||||
@ -135,8 +144,8 @@ public class AutomationManager
|
||||
|
||||
case "wait":
|
||||
// 等待元素
|
||||
by = GetArgumentValue(args, "-by") ?? "name";
|
||||
value = GetArgumentValue(args, "-value");
|
||||
string by = GetArgumentValue(args, "-by") ?? "name";
|
||||
string value = GetArgumentValue(args, "-value");
|
||||
int timeout = int.Parse(GetArgumentValue(args, "-timeout") ?? "30");
|
||||
WaitForElement(args, by, value, timeout);
|
||||
break;
|
||||
@ -171,8 +180,7 @@ public class AutomationManager
|
||||
|
||||
case "inspect":
|
||||
// 生成元素识别器
|
||||
int inspectTimeout = int.Parse(GetArgumentValue(args, "-timeout") ?? "30");
|
||||
InspectElement(args, inspectTimeout);
|
||||
InspectElement(args);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -203,6 +211,7 @@ public class AutomationManager
|
||||
- process 进程名
|
||||
- active 当前活动窗口(默认)
|
||||
不指定时默认在整个桌面范围内查找
|
||||
-background 后台操作模式,不激活窗口(可选)
|
||||
|
||||
元素识别参数:
|
||||
-value <标识值> 要查找的元素的值
|
||||
@ -215,10 +224,8 @@ public class AutomationManager
|
||||
|
||||
操作类型及示例:
|
||||
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>();
|
||||
info.Add("name", element.Current.Name);
|
||||
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("processId", element.Current.ProcessId);
|
||||
info.Add("boundingRectangle", new
|
||||
@ -486,7 +495,18 @@ public class AutomationManager
|
||||
string identifier = GetArgumentValue(args, "-value");
|
||||
string by = GetArgumentValue(args, "-by");
|
||||
string pattern = GetArgumentValue(args, "-pattern") ?? "invoke";
|
||||
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); // 等待窗口激活
|
||||
}
|
||||
|
||||
switch (pattern.ToLower())
|
||||
{
|
||||
case "invoke":
|
||||
@ -512,6 +532,11 @@ public class AutomationManager
|
||||
throw new Exception("元素不支持指定的操作模式");
|
||||
}
|
||||
|
||||
private static bool HasArgument(string[] args, string key)
|
||||
{
|
||||
return args.Contains(key, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string[] GetSupportedPatterns(AutomationElement element)
|
||||
{
|
||||
var patterns = new List<string>();
|
||||
@ -527,8 +552,18 @@ public class AutomationManager
|
||||
{
|
||||
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); // 等待窗口激活
|
||||
}
|
||||
|
||||
// 尝试ValuePattern
|
||||
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
|
||||
if (valuePattern != null)
|
||||
@ -698,7 +733,7 @@ public class AutomationManager
|
||||
{
|
||||
try
|
||||
{
|
||||
FindElement(args, by, value, "subtree");
|
||||
ListElements(args, value);
|
||||
Console.WriteLine("成功找到元素");
|
||||
return;
|
||||
}
|
||||
@ -734,15 +769,25 @@ public class AutomationManager
|
||||
|
||||
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))
|
||||
{
|
||||
throw new Exception("必须指定要发送的按键");
|
||||
}
|
||||
|
||||
string identifier = GetArgumentValue(args, "-value");
|
||||
string by = GetArgumentValue(args, "-by");
|
||||
var element = GetElementByIdentifier(args, identifier, by);
|
||||
|
||||
// 确保元素可以接收输入
|
||||
if (!element.Current.IsKeyboardFocusable)
|
||||
{
|
||||
@ -824,7 +869,9 @@ public class AutomationManager
|
||||
childInfo.Add("id", CacheElement(child));
|
||||
childInfo.Add("name", child.Current.Name);
|
||||
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);
|
||||
result.Add(childInfo);
|
||||
}
|
||||
@ -846,7 +893,9 @@ public class AutomationManager
|
||||
parentInfo.Add("id", CacheElement(parent));
|
||||
parentInfo.Add("name", parent.Current.Name);
|
||||
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);
|
||||
|
||||
var serializer = new JavaScriptSerializer();
|
||||
@ -891,32 +940,131 @@ public class AutomationManager
|
||||
{
|
||||
var root = GetRootElement(args);
|
||||
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.Subtree,
|
||||
Condition.TrueCondition
|
||||
);
|
||||
|
||||
foreach (AutomationElement element in elements)
|
||||
// 确定搜索范围
|
||||
TreeScope searchScope = TreeScope.Children;
|
||||
switch (scope.ToLower())
|
||||
{
|
||||
// 如果指定了过滤条件,检查元素名称是否包含过滤文本
|
||||
if (!string.IsNullOrEmpty(filter) &&
|
||||
!element.Current.Name.Contains(filter) &&
|
||||
!element.Current.ClassName.Contains(filter) &&
|
||||
!element.Current.AutomationId.Contains(filter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
case "descendants":
|
||||
searchScope = TreeScope.Descendants;
|
||||
break;
|
||||
case "subtree":
|
||||
searchScope = TreeScope.Subtree;
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
elementInfo.Add("path", GetElementPath(element));
|
||||
result.Add(elementInfo);
|
||||
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>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
@ -963,7 +1111,15 @@ public class AutomationManager
|
||||
string window = GetArgumentValue(args, "-window");
|
||||
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;
|
||||
}
|
||||
@ -977,10 +1133,15 @@ public class AutomationManager
|
||||
|
||||
case "class":
|
||||
// 通过窗口类名查找
|
||||
return AutomationElement.RootElement.FindFirst(
|
||||
var classElements = AutomationElement.RootElement.FindAll(
|
||||
TreeScope.Children,
|
||||
new PropertyCondition(AutomationElement.ClassNameProperty, window)
|
||||
);
|
||||
if (classElements.Count > 0)
|
||||
{
|
||||
return classElements[0];
|
||||
}
|
||||
break;
|
||||
|
||||
case "process":
|
||||
// 通过进程名查找
|
||||
@ -991,151 +1152,199 @@ public class AutomationManager
|
||||
}
|
||||
break;
|
||||
|
||||
case "active":
|
||||
// 获取当前活动窗口
|
||||
IntPtr activeHandle = GetForegroundWindow();
|
||||
return AutomationElement.FromHandle(activeHandle);
|
||||
|
||||
case "title":
|
||||
default:
|
||||
// 通过窗口标题查找(支持模糊匹配)
|
||||
var windows = AutomationElement.RootElement.FindAll(
|
||||
TreeScope.Children,
|
||||
Condition.TrueCondition
|
||||
);
|
||||
foreach (AutomationElement win in windows)
|
||||
{
|
||||
if (win.Current.Name.Contains(window))
|
||||
{
|
||||
return win;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// 返回根元素,让调用方自己处理查找逻辑
|
||||
return AutomationElement.RootElement;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
throw new Exception("必须指定查找值 (-value)");
|
||||
}
|
||||
// 创建一个半透明的全屏窗口来捕获鼠标事件
|
||||
Form overlayForm = new Form();
|
||||
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);
|
||||
TreeScope treeScope = TreeScope.Children;
|
||||
switch (scope.ToLower())
|
||||
{
|
||||
case "descendants":
|
||||
treeScope = TreeScope.Descendants;
|
||||
break;
|
||||
case "subtree":
|
||||
treeScope = TreeScope.Subtree;
|
||||
break;
|
||||
}
|
||||
bool completed = false;
|
||||
|
||||
Condition condition;
|
||||
switch (by.ToLower())
|
||||
{
|
||||
case "name":
|
||||
condition = new PropertyCondition(AutomationElement.NameProperty, value);
|
||||
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);
|
||||
}
|
||||
overlayForm.MouseClick += (sender, e) => {
|
||||
if (e.Button == MouseButtons.Left) {
|
||||
try {
|
||||
Point clickPosition;
|
||||
GetCursorPos(out clickPosition);
|
||||
|
||||
var elements = root.FindAll(treeScope, condition);
|
||||
var result = new List<Dictionary<string, string>>();
|
||||
// 先关闭覆盖窗口
|
||||
overlayForm.Hide();
|
||||
|
||||
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("type", element.Current.ControlType.ProgrammaticName);
|
||||
elementInfo.Add("automationId", element.Current.AutomationId);
|
||||
result.Add(elementInfo);
|
||||
}
|
||||
// 等待一小段时间确保窗口完全消失
|
||||
Thread.Sleep(100);
|
||||
|
||||
var serializer = new JavaScriptSerializer();
|
||||
Console.Write(serializer.Serialize(result));
|
||||
}
|
||||
// 获取点击位置的元素
|
||||
var element = AutomationElement.FromPoint(clickPosition.ToWindowsPoint());
|
||||
|
||||
private static void InspectElement(string[] args, int timeout)
|
||||
{
|
||||
Console.WriteLine("请在" + timeout + "秒内点击要识别的元素...");
|
||||
|
||||
// 记录当前鼠标位置
|
||||
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;
|
||||
if (element != null && element != AutomationElement.RootElement) {
|
||||
InspectElementInfo(element);
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略获取元素时的错误
|
||||
catch (Exception ex) {
|
||||
Console.Error.WriteLine(string.Format("Error: {0}", ex.Message));
|
||||
}
|
||||
finally {
|
||||
overlayForm.Close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
wasPressed = isPressed;
|
||||
Thread.Sleep(100);
|
||||
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.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
thread.Join();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
throw new Exception("操作超时");
|
||||
// 添加窗口详细信息
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ const iconv = require("iconv-lite");
|
||||
const child_process = require("child_process");
|
||||
const { getQuickcommandFolderFile } = require("../getQuickcommandFile");
|
||||
|
||||
let currentChild = null;
|
||||
|
||||
const getAssemblyPath = (assembly) => {
|
||||
const { version } = getCscPath();
|
||||
const is64bit = process.arch === "x64";
|
||||
@ -37,15 +39,9 @@ const getAssemblyPath = (assembly) => {
|
||||
|
||||
// v3.0/v3.5 路径
|
||||
path.join(
|
||||
process.env["ProgramFiles(x86)"] || process.env.ProgramFiles,
|
||||
"Reference Assemblies",
|
||||
"Microsoft",
|
||||
"Framework",
|
||||
"v3.0",
|
||||
assembly + ".dll"
|
||||
),
|
||||
path.join(
|
||||
process.env.ProgramFiles,
|
||||
process.arch === "x64"
|
||||
? process.env.ProgramFiles
|
||||
: process.env["ProgramFiles(x86)"],
|
||||
"Reference Assemblies",
|
||||
"Microsoft",
|
||||
"Framework",
|
||||
@ -81,13 +77,16 @@ const getFeatureReferences = (feature) => {
|
||||
const formsDll = getAssemblyPath("System.Windows.Forms");
|
||||
const typesDll = getAssemblyPath("UIAutomationTypes");
|
||||
const baseDll = getAssemblyPath("WindowsBase");
|
||||
const drawingDll = getAssemblyPath("System.Drawing");
|
||||
if (!automationDll) throw new Error("找不到UIAutomationClient.dll");
|
||||
if (!formsDll) throw new Error("找不到System.Windows.Forms.dll");
|
||||
if (!typesDll) throw new Error("找不到UIAutomationTypes.dll");
|
||||
if (!baseDll) throw new Error("找不到WindowsBase.dll");
|
||||
if (!drawingDll) throw new Error("找不到System.Drawing.dll");
|
||||
references =
|
||||
`/reference:"${automationDll}" /reference:"${formsDll}" ` +
|
||||
`/reference:"${typesDll}" /reference:"${baseDll}" `;
|
||||
`/reference:"${typesDll}" /reference:"${baseDll}" ` +
|
||||
`/reference:"${drawingDll}" `;
|
||||
}
|
||||
return references;
|
||||
};
|
||||
@ -128,10 +127,11 @@ const buildCsharpFeature = async (feature) => {
|
||||
};
|
||||
|
||||
const getCscPath = () => {
|
||||
const is64bit = process.arch === "x64";
|
||||
let cscPath = path.join(
|
||||
process.env.WINDIR,
|
||||
"Microsoft.NET",
|
||||
"Framework",
|
||||
is64bit ? "Framework64" : "Framework",
|
||||
"v4.0.30319",
|
||||
"csc.exe"
|
||||
);
|
||||
@ -141,7 +141,7 @@ const getCscPath = () => {
|
||||
cscPath = path.join(
|
||||
process.env.WINDIR,
|
||||
"Microsoft.NET",
|
||||
"Framework",
|
||||
is64bit ? "Framework64" : "Framework",
|
||||
"v3.5",
|
||||
"csc.exe"
|
||||
);
|
||||
@ -163,13 +163,17 @@ const getCscPath = () => {
|
||||
*/
|
||||
const runCsharpFeature = async (feature, args = [], options = {}) => {
|
||||
return new Promise(async (reslove, reject) => {
|
||||
const { alwaysBuildNewExe = false } = options;
|
||||
const { alwaysBuildNewExe = window.utools.isDev(), killPrevious = true } =
|
||||
options;
|
||||
try {
|
||||
const featureExePath = await getCsharpFeatureExe(
|
||||
feature,
|
||||
alwaysBuildNewExe
|
||||
);
|
||||
child_process.execFile(
|
||||
if (killPrevious && currentChild) {
|
||||
quickcommand.kill(currentChild.pid, "SIGKILL");
|
||||
}
|
||||
currentChild = child_process.execFile(
|
||||
featureExePath,
|
||||
args,
|
||||
{
|
||||
|
165
plugin/lib/quickcomposer/windows/automation.js
Normal file
165
plugin/lib/quickcomposer/windows/automation.js
Normal 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,
|
||||
};
|
@ -6,6 +6,7 @@ const registry = require("./registry");
|
||||
const service = require("./service");
|
||||
const software = require("./software");
|
||||
const utils = require("./utils");
|
||||
const automation = require("./automation");
|
||||
|
||||
module.exports = {
|
||||
window,
|
||||
@ -16,4 +17,5 @@ module.exports = {
|
||||
service,
|
||||
software,
|
||||
utils,
|
||||
automation,
|
||||
};
|
||||
|
@ -48,7 +48,7 @@ const getQuickcommandIconPath = () => {
|
||||
};
|
||||
|
||||
// 修改对话框函数,使用新的 dialog.cs
|
||||
const showSystemMessageBox = async function (content, title = "") {
|
||||
const showSystemMessageBox = async function (content, title = "提示") {
|
||||
try {
|
||||
const iconPath = getQuickcommandIconPath();
|
||||
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)) {
|
||||
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();
|
||||
if (window.utools.isMacOs()) {
|
||||
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();
|
||||
if (window.utools.isMacOs()) {
|
||||
const itemList = buttons.map((item) => `"${item}"`).join(", ");
|
||||
@ -250,7 +250,7 @@ const showSystemButtonBox = async function (buttons, title = "") {
|
||||
const itemsList = buttons
|
||||
.map((btn, index) => `"${index}" "${btn}"`)
|
||||
.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);
|
||||
if (!result) return {};
|
||||
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();
|
||||
if (window.utools.isWindows()) {
|
||||
const args = [
|
||||
|
@ -85,7 +85,9 @@ export default {
|
||||
},
|
||||
// 判断是否是转为表格的结果,表格结果不需要换行,第二行一般包含分隔符---
|
||||
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() {
|
||||
|
@ -37,13 +37,7 @@ export default defineComponent({
|
||||
},
|
||||
// 通用参数配置
|
||||
commonConfig() {
|
||||
return (
|
||||
// 过滤掉特定函数排除的参数, excludeConfig格式为[要排除的参数索引]
|
||||
this.modelValue.config?.filter(
|
||||
(_, index) =>
|
||||
!this.getSelectSubCommand()?.excludeConfig?.includes(index)
|
||||
) || []
|
||||
);
|
||||
return this.modelValue.config || [];
|
||||
},
|
||||
// 特定函数独有参数配置,config格式和通用的config一致
|
||||
subCommandConfig() {
|
||||
|
@ -53,14 +53,7 @@ export const networkCommands = {
|
||||
label: "URL操作",
|
||||
desc: "URL解析、格式化和参数处理",
|
||||
icon: "link",
|
||||
config: [
|
||||
{
|
||||
label: "URL",
|
||||
component: "VariableInput",
|
||||
icon: "link",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
config: [],
|
||||
subCommands: [
|
||||
{
|
||||
value: "quickcomposer.network.url.parse",
|
||||
@ -71,7 +64,6 @@ export const networkCommands = {
|
||||
value: "quickcomposer.network.url.format",
|
||||
label: "格式化URL",
|
||||
icon: "link",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "协议",
|
||||
@ -120,7 +112,6 @@ export const networkCommands = {
|
||||
value: "quickcomposer.network.url.parseQuery",
|
||||
label: "解析查询字符串",
|
||||
icon: "search",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "查询字符串",
|
||||
@ -133,7 +124,6 @@ export const networkCommands = {
|
||||
value: "quickcomposer.network.url.formatQuery",
|
||||
label: "格式化查询字符串",
|
||||
icon: "edit",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "参数",
|
||||
@ -146,7 +136,6 @@ export const networkCommands = {
|
||||
value: "quickcomposer.network.url.parsePath",
|
||||
label: "解析路径",
|
||||
icon: "folder_open",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "路径",
|
||||
@ -159,7 +148,6 @@ export const networkCommands = {
|
||||
value: "quickcomposer.network.url.parseHost",
|
||||
label: "解析主机名",
|
||||
icon: "dns",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "主机名",
|
||||
@ -173,6 +161,12 @@ export const networkCommands = {
|
||||
label: "获取参数",
|
||||
icon: "find_in_page",
|
||||
config: [
|
||||
{
|
||||
label: "URL",
|
||||
component: "VariableInput",
|
||||
icon: "link",
|
||||
width: "auto",
|
||||
},
|
||||
{
|
||||
label: "参数名",
|
||||
component: "VariableInput",
|
||||
@ -186,6 +180,12 @@ export const networkCommands = {
|
||||
label: "添加参数",
|
||||
icon: "add_circle",
|
||||
config: [
|
||||
{
|
||||
label: "URL",
|
||||
component: "VariableInput",
|
||||
icon: "link",
|
||||
width: "auto",
|
||||
},
|
||||
{
|
||||
label: "参数名",
|
||||
component: "VariableInput",
|
||||
@ -205,6 +205,12 @@ export const networkCommands = {
|
||||
label: "移除参数",
|
||||
icon: "remove_circle",
|
||||
config: [
|
||||
{
|
||||
label: "URL",
|
||||
component: "VariableInput",
|
||||
icon: "link",
|
||||
width: "auto",
|
||||
},
|
||||
{
|
||||
label: "参数名",
|
||||
component: "VariableInput",
|
||||
@ -217,11 +223,27 @@ export const networkCommands = {
|
||||
value: "quickcomposer.network.url.isAbsolute",
|
||||
label: "检查绝对URL",
|
||||
icon: "check_circle",
|
||||
config: [
|
||||
{
|
||||
label: "URL",
|
||||
component: "VariableInput",
|
||||
icon: "link",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.network.url.parseComponents",
|
||||
label: "解析组成部分",
|
||||
icon: "category",
|
||||
config: [
|
||||
{
|
||||
label: "URL",
|
||||
component: "VariableInput",
|
||||
icon: "link",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -231,20 +253,19 @@ export const networkCommands = {
|
||||
desc: "DNS解析和查询",
|
||||
icon: "dns",
|
||||
isAsync: true,
|
||||
config: [
|
||||
{
|
||||
label: "要查询的域名",
|
||||
icon: "dns",
|
||||
component: "VariableInput",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
config: [],
|
||||
subCommands: [
|
||||
{
|
||||
label: "DNS查询",
|
||||
value: "quickcomposer.network.dns.lookupHost",
|
||||
icon: "search",
|
||||
config: [
|
||||
{
|
||||
label: "要查询的域名",
|
||||
icon: "dns",
|
||||
component: "VariableInput",
|
||||
width: "auto",
|
||||
},
|
||||
{
|
||||
label: "IP版本",
|
||||
icon: "settings_ethernet",
|
||||
@ -269,42 +290,97 @@ export const networkCommands = {
|
||||
value: "quickcomposer.network.dns.resolveAll",
|
||||
label: "解析所有记录",
|
||||
icon: "all_inclusive",
|
||||
config: [
|
||||
{
|
||||
label: "要查询的域名",
|
||||
icon: "dns",
|
||||
component: "VariableInput",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.network.dns.resolveIpv4",
|
||||
label: "解析IPv4",
|
||||
icon: "filter_4",
|
||||
config: [
|
||||
{
|
||||
label: "要查询的域名",
|
||||
icon: "dns",
|
||||
component: "VariableInput",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.network.dns.resolveIpv6",
|
||||
label: "解析IPv6",
|
||||
icon: "filter_6",
|
||||
config: [
|
||||
{
|
||||
label: "要查询的域名",
|
||||
icon: "dns",
|
||||
component: "VariableInput",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.network.dns.resolveMxRecords",
|
||||
label: "解析MX记录",
|
||||
icon: "mail",
|
||||
config: [
|
||||
{
|
||||
label: "要查询的域名",
|
||||
icon: "dns",
|
||||
component: "VariableInput",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.network.dns.resolveTxtRecords",
|
||||
label: "解析TXT记录",
|
||||
icon: "text_fields",
|
||||
config: [
|
||||
{
|
||||
label: "要查询的域名",
|
||||
icon: "dns",
|
||||
component: "VariableInput",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.network.dns.resolveNsRecords",
|
||||
label: "解析NS记录",
|
||||
icon: "dns",
|
||||
config: [
|
||||
{
|
||||
label: "要查询的域名",
|
||||
icon: "dns",
|
||||
component: "VariableInput",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.network.dns.resolveCnameRecords",
|
||||
label: "解析CNAME记录",
|
||||
icon: "link",
|
||||
config: [
|
||||
{
|
||||
label: "要查询的域名",
|
||||
icon: "dns",
|
||||
component: "VariableInput",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.network.dns.reverseResolve",
|
||||
label: "反向解析",
|
||||
icon: "swap_horiz",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "IP地址",
|
||||
|
@ -308,35 +308,58 @@ export const systemCommands = {
|
||||
label: "路径操作",
|
||||
desc: "路径解析和处理",
|
||||
icon: "folder",
|
||||
config: [
|
||||
{
|
||||
label: "路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
config: [],
|
||||
subCommands: [
|
||||
{
|
||||
value: "quickcomposer.system.path.normalize",
|
||||
label: "规范化路径",
|
||||
icon: "straighten",
|
||||
config: [
|
||||
{
|
||||
label: "路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.system.path.parse",
|
||||
label: "解析路径",
|
||||
icon: "account_tree",
|
||||
config: [
|
||||
{
|
||||
label: "路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.system.path.dirname",
|
||||
label: "获取目录名",
|
||||
icon: "folder",
|
||||
config: [
|
||||
{
|
||||
label: "路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.system.path.basename",
|
||||
label: "获取文件名",
|
||||
icon: "description",
|
||||
config: [
|
||||
{
|
||||
label: "路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: "auto",
|
||||
},
|
||||
{
|
||||
label: "要移除的扩展名",
|
||||
component: "VariableInput",
|
||||
@ -349,17 +372,32 @@ export const systemCommands = {
|
||||
value: "quickcomposer.system.path.extname",
|
||||
label: "获取扩展名",
|
||||
icon: "extension",
|
||||
config: [
|
||||
{
|
||||
label: "路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.system.path.isAbsolute",
|
||||
label: "判断绝对路径",
|
||||
icon: "check_circle",
|
||||
config: [
|
||||
{
|
||||
label: "路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.system.path.join",
|
||||
label: "连接路径",
|
||||
icon: "add_link",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "路径片段",
|
||||
@ -379,7 +417,6 @@ export const systemCommands = {
|
||||
value: "quickcomposer.system.path.resolve",
|
||||
label: "解析绝对路径",
|
||||
icon: "assistant_direction",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "路径片段",
|
||||
@ -399,7 +436,6 @@ export const systemCommands = {
|
||||
value: "quickcomposer.system.path.relative",
|
||||
label: "计算相对路径",
|
||||
icon: "compare_arrows",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "起始路径",
|
||||
@ -419,7 +455,6 @@ export const systemCommands = {
|
||||
value: "quickcomposer.system.path.format",
|
||||
label: "格式化路径",
|
||||
icon: "format_shapes",
|
||||
excludeConfig: [0],
|
||||
config: [
|
||||
{
|
||||
label: "根路径",
|
||||
|
@ -104,6 +104,32 @@ const registryPaths = [
|
||||
},
|
||||
];
|
||||
|
||||
const searchWindowConfig = [
|
||||
{
|
||||
key: "method",
|
||||
label: "查找方式",
|
||||
component: "q-select",
|
||||
icon: "search",
|
||||
width: 3,
|
||||
options: [
|
||||
{ label: "标题", value: "title" },
|
||||
// { label: "类名", value: "class" },
|
||||
{ label: "句柄", value: "handle" },
|
||||
// { label: "进程", value: "process" },
|
||||
{ label: "活动窗口", value: "active" },
|
||||
],
|
||||
defaultValue: "title",
|
||||
},
|
||||
{
|
||||
key: "value",
|
||||
label: "窗口标题/句柄",
|
||||
component: "VariableInput",
|
||||
icon: "title",
|
||||
width: 9,
|
||||
placeholder: "标题支持模糊匹配,选择活动窗口无需输入",
|
||||
},
|
||||
];
|
||||
|
||||
export const windowsCommands = {
|
||||
label: "Win自动化",
|
||||
icon: "window",
|
||||
@ -114,29 +140,7 @@ export const windowsCommands = {
|
||||
label: "窗口控制",
|
||||
desc: "Windows窗口操作",
|
||||
icon: "window",
|
||||
config: [
|
||||
{
|
||||
key: "method",
|
||||
label: "查找方式",
|
||||
component: "q-select",
|
||||
icon: "search",
|
||||
width: 3,
|
||||
options: [
|
||||
{ label: "标题", value: "title" },
|
||||
{ label: "句柄", value: "handle" },
|
||||
{ label: "活动窗口", value: "active" },
|
||||
],
|
||||
defaultValue: "title",
|
||||
},
|
||||
{
|
||||
key: "value",
|
||||
label: "窗口标题/句柄",
|
||||
component: "VariableInput",
|
||||
icon: "title",
|
||||
width: 9,
|
||||
placeholder: "标题支持模糊匹配,选择活动窗口无需输入",
|
||||
},
|
||||
],
|
||||
config: searchWindowConfig,
|
||||
subCommands: [
|
||||
{
|
||||
value: "quickcomposer.windows.window.getWindowInfo",
|
||||
@ -357,7 +361,7 @@ export const windowsCommands = {
|
||||
items: controlClass,
|
||||
},
|
||||
width: 8,
|
||||
placeholder: "可选,输入要过滤的控件类名或文本",
|
||||
placeholder: "可选,输入要过滤的控件类型或文本",
|
||||
},
|
||||
background: {
|
||||
label: "后台操作",
|
||||
@ -394,7 +398,7 @@ export const windowsCommands = {
|
||||
width: 12,
|
||||
options: {
|
||||
control: {
|
||||
label: "控件类名",
|
||||
label: "控件类型",
|
||||
component: "VariableInput",
|
||||
icon: "class",
|
||||
options: {
|
||||
@ -408,7 +412,7 @@ export const windowsCommands = {
|
||||
component: "VariableInput",
|
||||
icon: "text_fields",
|
||||
width: 6,
|
||||
placeholder: "可选,和控件类名至少输入一个",
|
||||
placeholder: "可选,和控件类型至少输入一个",
|
||||
},
|
||||
pos: {
|
||||
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: "要获取值的元素",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user