You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WPF实现UIAutomation自定义Pattern时DLL引用与签名异常问题

WPF C# 实现 UIAutomation 自定义模式的互操作问题

在WPF C#程序中为Windows UIAutomation实现自定义模式时,需要引入包含IUIAutomationPatternHandler定义的相关类型。目前可查的唯一实现路径是添加UIAutomationCore的COM引用,但存在两个不确定点:一是不清楚该操作实际引入的是哪个DLL,二是IDE自动导入的类型定义存在参数错误。

自动生成的IUIAutomationRegistrar.RegisterPattern方法签名如下:

void RegisterPattern([In] ref UIAutomationPatternInfo pattern,
   out int pPatternId, out int pPatternAvailablePropertyId,
   [In] uint propertyIdCount, out int pPropertyIds,
   [In] uint eventIdCount, out int pEventIds);

而C++官方文档中该方法的签名为:

HRESULT RegisterPattern(
  [in]  const UIAutomationPatternInfo *pattern,
  [out] PATTERNID                     *pPatternId,
  [out] PROPERTYID                    *pPatternAvailablePropertyId,
  [in]  UINT                          propertyIdCount,
  [out] PROPERTYID                    *pPropertyIds,
  [in]  UINT                          eventIdCount,
  [out] EVENTID                       *pEventIds
);

按照标准COM交互逻辑,调用方需要预先分配用于接收返回信息的数组,传入数组本身(pPropertyIds)以及数组长度(propertyIdCount),显然C#自动导入的定义中将pPropertyIds识别为out int类型是错误的。

针对该场景的核心疑问如下:

  • 添加UIAutomationCore的COM引用是否是实现该需求的正确方式
  • 是否存在其他官方推荐的流程可获取正确的方法签名
  • 是否必须手动编写互操作导入定义来修正参数类型

补充1:COM实例反射调用异常

要调用RegisterPattern方法,需要从DLL中获取CUIAutomationRegistrar对象,常规实现中直接调用以下代码即可创建实例:

var registrar = new UIA.CUIAutomationRegistrar();

该代码可以正常运行,但如果要调用自定义修正版本的RegisterPattern(),最初尝试通过反射调用时,执行以下代码:

registrar.GetType().GetMethods()
// 或
registrar.GetType().GetMethod("RegisterPattern")

运行结果提示不存在名为RegisterPattern的方法,推测原因是该方法被标记了以下特性:

[MethodImpl(MethodImplOptions.InternalCall)]

因此它并不是常规意义上可通过反射获取的"真实"托管方法。

补充2:自定义互操作接口转换与范围异常

测试发现以下写法可以正常工作:

var registrar = new UIA.CUIAutomationRegistrar();
MyCopyPaste.IUIAutomationRegistrar myRegistar = (MyCopyPaste.IUIAutomationRegistrar)registrar;

推测编译器在后台自动执行了QueryInterface()调用,由于MyCopyPaste.IUIAutomationRegistrar与UIA.IUIAutomationRegistrar的UUID一致,因此类型转换可以通过编译。

但调用myRegistar实例时存在问题。修改后的MyCopyPaste.IUIAutomationRegstrar中RegisterPattern()方法定义如下:

[MethodImpl(MethodImplOptions.InternalCall)]
void RegisterPattern([In] ref UIAutomationPatternInfo pattern,
    out int pPatternId, out int pPatternAvailablePropertyId,
    [In] uint propertyIdCount,
    [MarshalAs(UnmanagedType.LPArray)] int[] pPropertyIds,
    [In] uint eventIdCount, out int pEventIds);

使用如下代码调用该方法:

int[] propertyIds = new int[10];
myRegistar.RegisterPattern(ref patternInfo,
   out int patternId, out int patternAvailablePropertyId,
   10, propertyIds, 0, out int eventIds);

运行时会抛出 "Value does not fall within the expected range."(值不在预期范围内) 异常。

补充3:手动互操作定义与测试结果

以下是手动编写的全部互操作定义代码:

namespace MyCopyPaste
{
    public enum UIAutomationType
    {
        UIAutomationType_Int = 1,
        UIAutomationType_Bool = 2,
        UIAutomationType_String = 3,
        UIAutomationType_Double = 4,
        UIAutomationType_Point = 5,
        UIAutomationType_Rect = 6,
        UIAutomationType_Element = 7,
        UIAutomationType_Array = 65536,
        UIAutomationType_Out = 131072,
        UIAutomationType_IntArray = 65537,
        UIAutomationType_BoolArray = 65538,
        UIAutomationType_StringArray = 65539,
        UIAutomationType_DoubleArray = 65540,
        UIAutomationType_PointArray = 65541,
        UIAutomationType_RectArray = 65542,
        UIAutomationType_ElementArray = 65543,
        UIAutomationType_OutInt = 131073,
        UIAutomationType_OutBool = 131074,
        UIAutomationType_OutString = 131075,
        UIAutomationType_OutDouble = 131076,
        UIAutomationType_OutPoint = 131077,
        UIAutomationType_OutRect = 131078,
        UIAutomationType_OutElement = 131079,
        UIAutomationType_OutIntArray = 196609,
        UIAutomationType_OutBoolArray = 196610,
        UIAutomationType_OutStringArray = 196611,
        UIAutomationType_OutDoubleArray = 196612,
        UIAutomationType_OutPointArray = 196613,
        UIAutomationType_OutRectArray = 196614,
        UIAutomationType_OutElementArray = 196615
    }

    [StructLayout(LayoutKind.Sequential, Pack = 8)]
    public struct UIAutomationParameter
    {
        public UIAutomationType type;
        public IntPtr pData;
    }

    [Guid("C03A7FE4-9431-409F-BED8-AE7C2299BC8D")]
    [InterfaceType(1)]
    public interface IUIAutomationPatternInstance
    {
    }

    [Guid("D97022F3-A947-465E-8B2A-AC4315FA54E8")]
    [InterfaceType(1)]
    public interface IUIAutomationPatternHandler
    {
        [MethodImpl(MethodImplOptions.InternalCall)]
        void CreateClientWrapper([In][MarshalAs(UnmanagedType.Interface)] IUIAutomationPatternInstance pPatternInstance, [MarshalAs(UnmanagedType.IUnknown)] out object pClientWrapper);

        [MethodImpl(MethodImplOptions.InternalCall)]
        void Dispatch([In][MarshalAs(UnmanagedType.IUnknown)] object pTarget, [In] uint index, [In] ref UIAutomationParameter pParams, [In] uint cParams);
    }

    [StructLayout(LayoutKind.Sequential, Pack = 8)]
    public struct UIAutomationPatternInfo
    {
        public Guid guid;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string pProgrammaticName;
        public Guid providerInterfaceId;
        public Guid clientInterfaceId;
        public uint cProperties;
        public IntPtr pProperties;
        public uint cMethods;
        public IntPtr pMethods;
        public uint cEvents;
        public IntPtr pEvents;
        [MarshalAs(UnmanagedType.Interface)]
        public IUIAutomationPatternHandler pPatternHandler;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 8)]
    public struct UIAutomationPropertyInfo
    {
        public Guid guid;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string pProgrammaticName;
        public UIAutomationType type;
    }

    [InterfaceType(1)]
    [Guid("8609C4EC-4A1A-4D88-A357-5A66E060E1CF")]
    [ComImport]
    public interface IUIAutomationRegistrar
    {
        [MethodImpl(MethodImplOptions.InternalCall)]
        void RegisterProperty([In] ref UIAutomationPropertyInfo property, out int propertyId);

        void _VtblGap1_1();

        [MethodImpl(MethodImplOptions.InternalCall)]
        void RegisterPattern([In] ref UIAutomationPatternInfo pattern,
            out int pPatternId, out int pPatternAvailablePropertyId,
            [In] uint propertyIdCount,
            [MarshalAs(UnmanagedType.LPArray)] int[] pPropertyIds,
            [In] uint eventIdCount, out int pEventIds);
    }

    [Guid("8609C4EC-4A1A-4D88-A357-5A66E060E1CF")]
    public interface CUIAutomationRegistrar : IUIAutomationRegistrar
    {
    }
}

对应的调用代码如下:

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("BFEF93B9-0CAD-4B0F-8FB7-51CFF6891BCD")]
public interface IValidatingPattern
{
    static readonly Guid PatternGUID = new(0x66e36682, 0x42f6, 0x4266, 0xaf, 0x27, 0xf, 0x34, 0xfa, 0xe6, 0x58, 0x3e);
    static readonly string PatternName = "Validating";
    static readonly Guid IsValidGUID = new(0x5dbe95c5, 0xb18c, 0x4904, 0xa3, 0xae, 0x57, 0xcb, 0xe4, 0xc2, 0x41, 0x5e);
    public bool IsValid { get; }
}

internal class ValidatingPattern : IValidatingPattern
{
    public bool IsValid
    {
        get
        {
            Debug.WriteLine("Not implemented (yet)");
            throw new NotImplementedException();
        }
    }
}

UIAutomationPatternInfo patternInfo = new()
{
   guid = IValidatingPattern.PatternGUID,
   pProgrammaticName = IValidatingPattern.PatternName,
   providerInterfaceId = typeof(IValidatingPattern).GUID,
   cProperties = 0,
   cMethods = 0,
   cEvents = 0,
   pPatternHandler = Handler
};
var registrar = new UIA.CUIAutomationRegistrar();
IUIAutomationRegistrar myRegistar = (IUIAutomationRegistrar)registrar;
int[] propertyIds = new int[10];
myRegistar.RegisterPattern(ref patternInfo,
   out int patternId, out int patternAvailablePropertyId,
   10, propertyIds, 0, out int eventIds);

测试发现特殊现象:如果将propertyIdCount参数设置为0,调用可以完美执行,返回patternId=50000、patternAvailablePropertyId=500001,对应代码如下:

myRegistar.RegisterPattern(ref patternInfo,
   out int patternId, out int patternAvailablePropertyId,
   0/* 传0时可以正常运行*/, propertyIds, 0, out int eventIds);

补充4:定义模式方法时的空引用异常

尝试为模式定义单个方法时,代码如下:

UIAutomationType[] methodOutParamTypes = { UIAutomationType.UIAutomationType_Bool };
GCHandle methodOutParamTypesHandle = GCHandle.Alloc(methodOutParamTypes);

string[] methodOutParamNames = { "IsValid" };
GCHandle methodOutParamNamesHandle = GCHandle.Alloc(methodOutParamNames);

UIAutomationMethodInfo getIsValidMethod = new()
{
   pProgrammaticName = "get_IsValid",
   doSetFocus = 0,
   cInParameters = 0,
   cOutParameters = 1,
   pParameterTypes = (IntPtr)methodOutParamTypesHandle,
   pParameterNames = (IntPtr)methodOutParamNamesHandle
};

UIAutomationMethodInfo[] methods = { getIsValidMethod };
GCHandle methodsHandle = GCHandle.Alloc(methods);

UIAutomationPatternInfo patternInfo = new()
{
   guid = IValidatingPattern.PatternGUID,
   pProgrammaticName = IValidatingPattern.PatternName,
   providerInterfaceId = typeof(IValidatingPattern).GUID,
   cProperties = 0,
   cMethods = 1,
   pMethods = (IntPtr)methodsHandle,
   cEvents = 0,
   pPatternHandler = Handler
};

var registrar = new UIA.CUIAutomationRegistrar();
MyCopyPaste.IUIAutomationRegistrar myRegistar = (MyCopyPaste.IUIAutomationRegistrar)registrar;

int[] propertyIds = new int[10];
myRegistar.RegisterPattern(ref patternInfo,
   out int patternId, out int patternAvailablePropertyId,
   0, propertyIds,
   0, out int eventIds);

执行该代码时会抛出NullReferenceException(空引用)异常。


内容的提问来源于stack exchange,提问作者Betty Crokker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 16:51:26