In my WPF c# program, I'm trying to implement a custom pattern for Windows UIAutomation, which means I need to import something that knows about IUIAutomationPatternHandler, and the only way I can find to that is to add a COM reference to UIAutomationCore, and I'm not sure which DLL it's actually importing, but what gets included seems wrong.
For example, the IUIAutomationRegistrar.RegisterPattern method that gets imported looks like this:
void RegisterPattern([In] ref UIAutomationPatternInfo pattern,
out int pPatternId, out int pPatternAvailablePropertyId,
[In] uint propertyIdCount, out int pPropertyIds,
[In] uint eventIdCount, out int pEventIds);
but the C++ documentation says this:
HRESULT RegisterPattern(
[in] const UIAutomationPatternInfo *pattern,
[out] PATTERNID *pPatternId,
[out] PROPERTYID *pPatternAvailablePropertyId,
[in] UINT propertyIdCount,
[out] PROPERTYID *pPropertyIds,
[in] UINT eventIdCount,
[out] EVENTID *pEventIds
);
which is kind of standard COM stuff, where you allocate an array to receive the information, and then pass in the array itself (pPropertyIds) and the size of the array (propertyIdCount) which means the c# import where it thinks pPropertyIds is an "out int" is wrong.
Is adding a COM reference to UIAutomationCore the right way to do this? Or is there a different process that is recommended, to get the correct signature? Or do I just have to do my own manual importing so the parameters are correct?
EDIT
In order to call RegisterPattern, I need a CUIAutomationRegistrar object from the DLL. Normally you just call
var registrar = new UIA.CUIAutomationRegistrar();
and that works, but then in order to call my own version of RegisterPattern(), I need to call InvokeMethod (right?) and when I call
registrar.GetType().GetMethods()
or
registrar.GetType().GetMethod("RegisterPattern")
It claims there's no method named "RegisterPattern", probably because the method is marked
[MethodImpl(MethodImplOptions.InternalCall)]
so it's not actually a "real" method.
EDIT 2
So this works:
var registrar = new UIA.CUIAutomationRegistrar();
MyCopyPaste.IUIAutomationRegistrar myRegistar = (MyCopyPaste.IUIAutomationRegistrar)registrar;
I assume the compiler is actually doing the QueryInterface() behind the scenes, and since MyCopyPaste.IUIAutomationRegistrar has the same UUID as UIA.IUIAutomationRegistrar, the compiler is happy.
But then using 'myRegistrar' is tricky. I modified the RegisterPattern() method in MyCopyPaste.IUIAutomationRegstrar like so:
[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);
which I call like this:
int[] propertyIds = new int[10];
myRegistar.RegisterPattern(ref patternInfo,
out int patternId, out int patternAvailablePropertyId,
10, propertyIds, 0, out int eventIds);
and it throws a "Value does not fall within the expected range." at runtime.
EDIT 3
Here's my entire copy/paste section:
namespace MyCopyPaste
{
// [TypeIdentifier("930299ce-9965-4dec-b0f4-a54848d4b667", "UIA.UIAutomationType")]
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)]
// [TypeIdentifier("930299ce-9965-4dec-b0f4-a54848d4b667", "UIA.UIAutomationParameter")]
public struct UIAutomationParameter
{
public UIAutomationType type;
public IntPtr pData;
}
[Guid("C03A7FE4-9431-409F-BED8-AE7C2299BC8D")]
[InterfaceType(1)]
// [TypeIdentifier]
public interface IUIAutomationPatternInstance
{
}
[Guid("D97022F3-A947-465E-8B2A-AC4315FA54E8")]
[InterfaceType(1)]
// [TypeIdentifier]
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)]
// [TypeIdentifier("930299ce-9965-4dec-b0f4-a54848d4b667", "UIA.UIAutomationPatternInfo")]
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)]
// [TypeIdentifier("930299ce-9965-4dec-b0f4-a54848d4b667", "UIA.UIAutomationPropertyInfo")]
public struct UIAutomationPropertyInfo
{
public Guid guid;
[MarshalAs(UnmanagedType.LPWStr)]
public string pProgrammaticName;
public UIAutomationType type;
}
[InterfaceType(1)]
[Guid("8609C4EC-4A1A-4D88-A357-5A66E060E1CF")]
// [TypeIdentifier]
[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")]
// [TypeIdentifier]
public interface CUIAutomationRegistrar : IUIAutomationRegistrar
{
}
}
and here's how I'm using it:
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("BFEF93B9-0CAD-4B0F-8FB7-51CFF6891BCD")] // GUID of IValidatingPattern
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, // (uint)properties.Length,
// struct UIAutomationPropertyInfo *pProperties;
cMethods = 0,
// struct UIAutomationMethodInfo *pMethods;
cEvents = 0,
// struct UIAutomationEventInfo *pEvents;
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/*(uint)properties.Length*/, propertyIds, 0, out int eventIds);
Note that if I change the 'propertyIdCount' to zero, the call works perfectly and returns patternId=50000 and patternAvailablePropertyId=500001:
myRegistar.RegisterPattern(ref patternInfo,
out int patternId, out int patternAvailablePropertyId,
0/* <-- WORKS WHEN ZERO*/, propertyIds, 0, out int eventIds);
EDIT 4
I also tried defining a single method:
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, // int
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, // (uint)properties.Length,
// struct UIAutomationPropertyInfo *pProperties;
cMethods = 1,
pMethods = (IntPtr)methodsHandle, // struct UIAutomationMethodInfo *pMethods;
cEvents = 0,
// struct UIAutomationEventInfo *pEvents;
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/*(uint)properties.Length*/, propertyIds,
0, out int eventIds);
That throws a NullReferenceException.