- 概述
- 用户界面自动化
- 关于“用户界面自动化”活动包
- 通过用户界面自动化实现自动化的应用程序和技术
- 项目兼容性
- UI-ANA-016 - 拉取打开浏览器 URL
- UI-ANA-017 - 出错时继续 True
- UI-ANA-018 - 列出 OCR/图像活动
- UI-DBP-006 - 容器使用情况
- UI-DBP-013 - Excel 自动化误用
- UI-DBP-030 - 选取器中的禁止变量使用情况
- UI-DBP-031 - 活动验证
- UI-PRR-001 - 模拟单击
- UI-PRR-002 - 模拟键入
- UI-PRR-003 - 打开应用程序误用
- UI-PRR-004 - 硬编码延迟
- UI-REL-001 - 选取器中的大 IDX
- UI-SEC-004 - 选取器电子邮件数据
- UI-SEC-010 - 应用程序/Url 限制
- UI-USG-011 - 不允许的属性
- UX-SEC-010 - 应用程序/Url 限制
- UX-DBP-029 - 使用不安全的密码
- UI-PST-001 - 项目设置中的审核日志级别
- UiPath 浏览器迁移工具
- 剪切区域
- 计算机视觉录制器
- 活动索引
- 激活
- 锚点基准
- 附加浏览器
- 附加窗口
- 阻止用户输入
- 标注
- 选中
- 单击
- 单击图像
- 单击图像触发器
- 单击 OCR 文本
- 单击文本
- 单击触发器
- 关闭应用程序
- 关闭选项卡
- 关闭窗口
- 上下文感知锚点
- 复制选定文本
- 元素属性更改触发器
- 存在元素
- 元素作用域
- 元素状态更改触发器
- 导出用户界面树
- 提取结构化数据
- 查找子元素
- 查找元素
- 查找图像
- 查找图像匹配项
- 查找 OCR 文本位置
- 查找相对元素
- 查找文本位置
- 获取活动窗口
- 获取上级
- 获取属性
- 获取事件信息
- 从剪贴板获取
- 获取全文
- 获取 OCR 文本
- 获取密码
- 获取位置
- 获取源元素
- 获取文本
- 获取可见文本
- 返回
- 前往
- 转至主页
- Google Cloud Vision OCR
- 隐藏窗口
- 高亮显示
- 热键触发器
- 悬停
- 悬停在图像上方
- 悬停在 OCR 文本上方
- 悬停文本
- 存在图像
- 在屏幕上指定
- 注入 .NET 代码
- 插入 Js 脚本
- 调用 ActiveX 方法
- 按键触发器
- 加载图像
- 最大化窗口
- Microsoft Azure 计算机视觉 OCR
- Microsoft OCR
- Microsoft Project Oxford Online OCR
- 最小化窗口
- 监控事件
- 鼠标触发器
- 移动窗口
- 导航至
- 存在 OCR 文本
- 在元素出现时
- 在元素消失时
- 在图像出现时
- 在图像消失时
- 打开应用程序
- 打开浏览器
- 刷新浏览器
- 重播用户事件
- 还原窗口
- 保存图像
- 选择项目
- 选择多个项目
- 发送热键
- 设置剪切区域
- 设置焦点
- 设置文本
- 设置为剪贴板
- 设置网页属性
- 显示窗口
- 启动进程
- 系统触发器
- 截取屏幕截图
- Tesseract OCR
- 存在文本
- 工具提示
- 键入
- 输入安全文本
- 使用前台
- 等待属性
- 等待元素消失
- 等待图像消失
- Computer Vision Local Server
- 移动自动化
- 终端

用户界面自动化活动
注入 .NET 代码
UiPath.Core.Activities.InjectDotNetCode
描述
将 .NET 代码注入目标应用程序的主用户界面线程。适用于不公开传统自动化技术或无法通过传统方式正确定位的 .NET UI 应用程序。
属性
常见
-
ContinueOnError - Specifies if the automation should continue even when the activity throws an error. This field only supports Boolean values (True, False). The default value is False. As a result, if the field is blank and an error is thrown, the execution of the project stops. If the value is set to True, the execution of the project continues regardless of any error.
备注:如果“Try Catch 异常处理”中包含该活动且“出错时继续”属性的值为“True”,则系统在执行项目时不会捕获任何错误。
-
“显示名称”- 活动的显示名称。
输入
-
“程序集”- 要注入的已编译 .NET 程序集的完整路径。此字段仅支持字符串和
String变量。 -
“方法名称”- 要执行的方法的名称。仅支持公共静态方法。
备注:一个类型不能包含多个具有相同名称的方法。如果出现这种情况,则会在运行时引发错误。
-
“Target.ClippingRegion”- 按照左、上、右、下四个方向定义相对于用户界面元素的剪切矩形(以像素为单位)。支持正数和负数。
-
“Target.Element”- 使用另一个活动返回的“用户界面元素”变量。该属性不能与“选取器”属性一起使用。该字段仅支持用户界面元素变量。
-
“Target.Selector”- 用于在执行活动时查找特定用户界面元素的“文本”属性。它实际上是 XML 片段,用于指定要查找的图形用户界面元素及其一些父元素的属性。
-
“Target.Timeout(毫秒)”- 指定最长等待时间(以毫秒为单位),如果超出该时间后活动并未运行,系统便会抛出“
SelectorNotFoundException”错误。默认值为 30000 毫秒(30 秒)。 -
“Target.WaitForReady”- 待目标准备就绪后,再执行操作。可用的选项如下:
- “无”- 在执行操作之前,系统不会等待显示除目标用户界面元素之外的任何内容。例如,如果希望只从网页检索文本或单击特定按钮,则可以使用此选项,而不必等待用户界面元素全部加载。请注意,如果按钮依赖尚未加载的元素(如脚本),这可能会产生不良后果。
- “交互/完成” - 待目标应用程序中的所有用户界面元素全部显示之后,再实际执行操作。 为了评估应用程序处于“交互”状态还是“完成”状态,系统将验证以下标签:
- “桌面应用程序”- 系统会发出一条“
wm_null”消息,以检查是否存在“<wnd>”、“<ctrl>”、“<java>”或“<uia>”标签。如存在,则执行活动。 - 网页应用程序:
- Internet Explorer - The
<webctrl>tag is used to check if the Ready state of the HTML document is set to Complete. Additionally, the Busy state has to be set to "False". - Others - The
<webctrl>tag is used to check if the Ready state of the HTML document is Complete.
- “SAP 应用程序”- 首先验证是否存在“
<wnd>”标签,然后使用 SAP 特定 API 来检测会话是否繁忙。
-
“类型名称”- 包含执行方法的公共类的名称。此字段仅支持字符串和
String变量。 -
“将用户界面元素用作第一个函数参数” - 将用户界面元素用作调用函数的第一个参数。仅支持 Windows 表单和 WPF 应用程序。
对于 Windows 表单应用程序,所调用函数的第一个参数的类型必须为
System.Windows.Forms.Control。对于 WPF 应用程序,调用函数的第一个参数的类型必须为
System.Windows.DependencyObject。
其他
- “私有”- 选中后将不再以“Verbose”级别记录变量和参数的值。
输出
- “结果”- 已调用方法的结果,存储在“
Object”变量中。此字段仅支持Object变量。
在非默认应用程序域中注入代码
注入 .NET 代码活动仅在默认的 AppDomain 中注入代码。
某些应用程序的结构使得您可能需要访问或操作的元素并不位于默认应用程序域中,因此在默认应用程序域中注入代码是不够的。 因此,如果您要指定的用户界面元素位于另一个应用程序域中,则注入的代码将无法使用这些元素。
要解决此问题,您必须修改注入流程,以精确定位包含用户界面元素的应用程序域。 更确切地说,您需要将在默认应用程序域中执行的函数中的代码注入到所有非默认应用程序域或包含要指定的用户界面元素的特定非默认应用程序域中。
创建跨应用程序域运行器
若要在非默认 AppDomain 中执行代码,您需要构建一个从MarshalByRefObject派生的类,其中包含要在非默认 AppDomain 中执行的函数:
public class CrossAppDomainRunner : MarshalByRefObject
{
public void Execute(IntPtr controlHandle)
{
// Implementation of the method that will be executed in the target AppDomain
Trace.WriteLine("Sunt in " + AppDomain.CurrentDomain.FriendlyName);
Control foundControl = null;
try
{
foundControl = Control.FromHandle(controlHandle);
}
catch (Exception controlException)
{
int lastWin32Error = Marshal.GetLastWin32Error();
Trace.WriteLine($"+++++ An exception occurred while getting the control from handle {controlHandle}. The message is: {controlException.Message}, Win32 Error: {lastWin32Error}");
}
if (foundControl != null)
{
Trace.WriteLine($"+++++ Found control {foundControl.Name} from handle {controlHandle}");
}
else
{
int lastWin32Error = Marshal.GetLastWin32Error();
Trace.WriteLine($"+++++ Control NOT found based on handle {controlHandle}, Win32 Error: {lastWin32Error}");
}
}
}
public class CrossAppDomainRunner : MarshalByRefObject
{
public void Execute(IntPtr controlHandle)
{
// Implementation of the method that will be executed in the target AppDomain
Trace.WriteLine("Sunt in " + AppDomain.CurrentDomain.FriendlyName);
Control foundControl = null;
try
{
foundControl = Control.FromHandle(controlHandle);
}
catch (Exception controlException)
{
int lastWin32Error = Marshal.GetLastWin32Error();
Trace.WriteLine($"+++++ An exception occurred while getting the control from handle {controlHandle}. The message is: {controlException.Message}, Win32 Error: {lastWin32Error}");
}
if (foundControl != null)
{
Trace.WriteLine($"+++++ Found control {foundControl.Name} from handle {controlHandle}");
}
else
{
int lastWin32Error = Marshal.GetLastWin32Error();
Trace.WriteLine($"+++++ Control NOT found based on handle {controlHandle}, Win32 Error: {lastWin32Error}");
}
}
}
基本上,您有一个接收IntPtr参数的Execute函数。 此参数包含您要查找的控件的值。 在此示例中,您只需传递IntPtr参数,但在特定情况下,您可以根据需要添加任意数量的参数。
在非默认 AppDomain 中枚举和注入代码
要枚举其他应用程序域,您需要一个从mscoree指向ICorRuntimeHost接口的指针。 为此,您需要声明该接口:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
namespace mscoree
{
[CompilerGenerated]
[Guid("CB2F6722-AB3A-11D2-9C40-00C04FA30A3E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[TypeIdentifier]
[ComImport]
[CLSCompliant(false)]
public interface ICorRuntimeHost
{
void _VtblGap1_11();
void EnumDomains(out IntPtr enumHandle);
void NextDomain([In] IntPtr enumHandle, [MarshalAs(UnmanagedType.IUnknown)] out object appDomain);
void CloseEnum([In] IntPtr enumHandle);
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
namespace mscoree
{
[CompilerGenerated]
[Guid("CB2F6722-AB3A-11D2-9C40-00C04FA30A3E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[TypeIdentifier]
[ComImport]
[CLSCompliant(false)]
public interface ICorRuntimeHost
{
void _VtblGap1_11();
void EnumDomains(out IntPtr enumHandle);
void NextDomain([In] IntPtr enumHandle, [MarshalAs(UnmanagedType.IUnknown)] out object appDomain);
void CloseEnum([In] IntPtr enumHandle);
}
}
您需要声明一个包含原生方法的类,以初始化组件对象模型 (COM):
public static class NativeMethods
{
[DllImport("ole32.dll")]
public static extern int CoInitializeEx(IntPtr pvReserved, COINIT dwCoInit);
public enum COINIT : uint
{
/// Initializes the thread for multi-threaded object concurrency.
COINIT_MULTITHREADED = 0x0,
/// Initializes the thread for apartment-threaded object concurrency.
COINIT_APARTMENTTHREADED = 0x2,
// ...
}
}
public static class NativeMethods
{
[DllImport("ole32.dll")]
public static extern int CoInitializeEx(IntPtr pvReserved, COINIT dwCoInit);
public enum COINIT : uint
{
/// Initializes the thread for multi-threaded object concurrency.
COINIT_MULTITHREADED = 0x0,
/// Initializes the thread for apartment-threaded object concurrency.
COINIT_APARTMENTTHREADED = 0x2,
// ...
}
}
您需要一种方法来获取ICorRunttimeHost对象:
public static ICorRuntimeHost GetCorRuntimeHost()
{
return (ICorRuntimeHost)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("CB2F6723-AB3A-11D2-9C40-00C04FA30A3E")));
}
public static ICorRuntimeHost GetCorRuntimeHost()
{
return (ICorRuntimeHost)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("CB2F6723-AB3A-11D2-9C40-00C04FA30A3E")));
}
现在,您已万事俱备,可以枚举其他 AppDomain 并在其中执行代码。函数GetControlData由“注入 .NET 代码活动”在默认 AppDomain 中执行:
public static void GetControlData(Int64 controlHandle, out string response)
{
//initialising COM
NativeMethods.CoInitializeEx(IntPtr.Zero, NativeMethods.COINIT.COINIT_MULTITHREADED);
mscoree.ICorRuntimeHost host = null;
try
{
host = (ICorRuntimeHost)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("CB2F6723-AB3A-11D2-9C40-00C04FA30A3E")));
}
catch (COMException comEx)
{
Trace.WriteLine($"COMException: {comEx.Message}, HRESULT: {comEx.ErrorCode}");
}
catch (Exception ex)
{
// Handle other exceptions
Trace.WriteLine($"Exception: {ex.Message}");
}
IntPtr enumHandle = IntPtr.Zero;
try
{
//now that we have ICorRuntimeHost object we can use it to enumerate the other domains
host.EnumDomains(out enumHandle);
object domain = null;
host.NextDomain(enumHandle, out domain);
while (domain != null)
{
//for each appdomain obtained
AppDomain appDomain = (AppDomain)domain;
//appDomain.BaseDirectory; - you might want to copy your dll
//in the appDomain.BaseDirectory cause otherwise it might not find
//the assembly
ObjectHandle handle = appDomain.CreateInstance(
typeof(CrossAppDomainRunner).Assembly.FullName,
typeof(CrossAppDomainRunner).FullName);
// Unwrap to get the actual object
var runnerProxy = handle.Unwrap();
// Use reflection to call the Execute method
MethodInfo executeMethod = runnerProxy.GetType().GetMethod("Execute", new Type[] { typeof(IntPtr) });
//pass parameters as new object[]
executeMethod.Invoke(runnerProxy, new object[] { new IntPtr(controlHandle) });
//go to next appdomain
host.NextDomain(enumHandle, out domain);
if (domain == null)
break;
}
}
finally
{
if (host != null)
{
if (enumHandle != IntPtr.Zero)
{
host.CloseEnum(enumHandle);
}
Marshal.ReleaseComObject(host);
}
}
response = string.Empty;
}
public static void GetControlData(Int64 controlHandle, out string response)
{
//initialising COM
NativeMethods.CoInitializeEx(IntPtr.Zero, NativeMethods.COINIT.COINIT_MULTITHREADED);
mscoree.ICorRuntimeHost host = null;
try
{
host = (ICorRuntimeHost)Activator.CreateInstance(Marshal.GetTypeFromCLSID(new Guid("CB2F6723-AB3A-11D2-9C40-00C04FA30A3E")));
}
catch (COMException comEx)
{
Trace.WriteLine($"COMException: {comEx.Message}, HRESULT: {comEx.ErrorCode}");
}
catch (Exception ex)
{
// Handle other exceptions
Trace.WriteLine($"Exception: {ex.Message}");
}
IntPtr enumHandle = IntPtr.Zero;
try
{
//now that we have ICorRuntimeHost object we can use it to enumerate the other domains
host.EnumDomains(out enumHandle);
object domain = null;
host.NextDomain(enumHandle, out domain);
while (domain != null)
{
//for each appdomain obtained
AppDomain appDomain = (AppDomain)domain;
//appDomain.BaseDirectory; - you might want to copy your dll
//in the appDomain.BaseDirectory cause otherwise it might not find
//the assembly
ObjectHandle handle = appDomain.CreateInstance(
typeof(CrossAppDomainRunner).Assembly.FullName,
typeof(CrossAppDomainRunner).FullName);
// Unwrap to get the actual object
var runnerProxy = handle.Unwrap();
// Use reflection to call the Execute method
MethodInfo executeMethod = runnerProxy.GetType().GetMethod("Execute", new Type[] { typeof(IntPtr) });
//pass parameters as new object[]
executeMethod.Invoke(runnerProxy, new object[] { new IntPtr(controlHandle) });
//go to next appdomain
host.NextDomain(enumHandle, out domain);
if (domain == null)
break;
}
}
finally
{
if (host != null)
{
if (enumHandle != IntPtr.Zero)
{
host.CloseEnum(enumHandle);
}
Marshal.ReleaseComObject(host);
}
}
response = string.Empty;
}
上面的示例在所有应用程序域中注入代码。 找到要指定的用户界面元素所在的 AppDomain 后,您可以枚举所有 AppDomain,但只能在必要的 AppDomain 中注入代码。