activities
latest
false
重要 :
请注意此内容已使用机器翻译进行了部分本地化。
UiPath logo, featuring letters U and I in white
用户界面自动化活动
Last updated 2024年10月28日

注入 .NET 代码

UiPath.Core.Activities.InjectDotNetCode

描述

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

属性

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

    注意:如果 Try Catch 异常处理中包含该活动且出错时继续属性的值为 True,则系统在执行项目时不会捕获任何错误。
  • “显示名称”- 活动的显示名称。
输入
  • 程序集”- 要注入的已编译 .NET 程序集的完整路径。此字段仅支持字符串和 String 变量。
  • 方法名称- 要执行的方法的名称。仅支持公共静态方法。

    注意:一个类型不能包含多个具有相同名称的方法。如果出现这种情况,则会在运行时引发错误。
  • Target.ClippingRegion - 沿以下方向定义相对于用户界面元素的剪切矩形(以像素为单位):左、上、右、下。 它支持正数和负数。
  • “目标.元素” - 使用另一个活动返回的用户界面元素变量。 此属性不能与“选取器”属性一起使用。 此字段仅支持用户界面元素变量。
  • “目标选择器” - 执行活动时用于查找特定用户界面元素的文本属性。 它实际上是一个 XML 片段,用于指定您要查找的 GUI 元素及其某些父元素的属性。
  • Target.TimeoutMS - 指定在引发 SelectorNotFoundException 错误之前等待活动运行的时间(以毫秒为单位)。 默认值为 30000 毫秒 (30 秒)。
  • Target.WaitForReady - 在执行操作之前,请等待目标就绪。 提供以下选项:

    • “无”- 在执行操作之前,系统不会等待显示除目标用户界面元素之外的任何内容。例如,如果希望只从网页检索文本或单击特定按钮,则可以使用此选项,而不必等待用户界面元素全部加载。请注意,如果按钮依赖于尚未加载的元素(例如脚本),则可能会产生不良后果。
    • “交互/完成” - 待目标应用程序中的所有用户界面元素全部显示之后,再实际执行操作。

      为了评估应用程序处于“交互”状态还是“完成”状态,系统将验证以下标签:

    • “桌面应用程序”- 系统会发出一条“wm_null”消息,以检查是否存在“<wnd>”、“<ctrl>”、“<java>”或“<uia>”标签。如存在,则执行活动。
    • 网页应用程序:
    1. Internet Explorer - <webctrl> 标签用于检查 HTML 文档的就绪状态是否设为完成。此外,必须将忙碌状态设置为“False”。
    2. 其他 - <webctrl> 标签用于检查 HTML 文档的“就绪”状态是否为“完成”
    • “SAP 应用程序”- 首先验证是否存在“<wnd>”标签,然后使用 SAP 特定 API 来检测会话是否繁忙。
  • 类型名称- 包含执行方法的公共类的名称。此字段仅支持字符串和 String 变量。
其他
  • “私有”- 选中后将不再以“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,
                // ...
                }
                }公共静态类原生方法 { [DllImport("ole32.dll")] 公共静态 extern int CoInitializeEx(IntPtr pvReversed, COINIT dwCoInit); public enum COINIT : uint { /// 为多线程对象并发初始化线程。
                COINIT_MULTITHREADED = 0x0, /// 为公寓线程对象并发初始化线程。 
                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由默认 AppDomain 中的“注入 .NET 代码”活动执行:
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 中枚举和注入代码

此页面有帮助吗?

获取您需要的帮助
了解 RPA - 自动化课程
UiPath Community 论坛
Uipath Logo White
信任与安全
© 2005-2024 UiPath。保留所有权利。