UiPath Documentation
activities
latest
false
重要 :
请注意,此内容已使用机器翻译进行了部分本地化。 新发布内容的本地化可能需要 1-2 周的时间才能完成。
UiPath logo, featuring letters U and I in white

用户界面自动化活动

上次更新日期 2026年4月24日

注入 .NET 代码

UiPath.Core.Activities.InjectDotNetCode

描述

将 .NET 代码注入目标应用程序的主用户界面线程。适用于不公开传统自动化技术或无法通过传统方式正确定位的 .NET UI 应用程序。

属性

常见
  • “出错时继续”- 指定在活动引发错误时是否应继续自动化操作。该字段仅支持“布尔值”(True 和 False)。默认值为 False。因此,如果该字段为空且活动引发错误,系统将停止执行项目。如果该值设为“True”,则无论出现何种错误,系统都将继续执行项目。

    备注:

    如果“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>”标签。如存在,则执行活动。
    • 网页应用程序:
    1. 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".
    2. 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 中注入代码。

  • 描述
  • 属性
  • 在非默认应用程序域中注入代码
  • 创建跨应用程序域运行器
  • 在非默认 AppDomain 中枚举和注入代码

此页面有帮助吗?

连接

需要帮助? 支持

想要了解详细内容? UiPath Academy

有问题? UiPath 论坛

保持更新