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.
I am trying to marshal data from C# to C++ DLL. For which, I have following C++ code.
// C++ Function in DLL
int __declspec(dllexport) DataAnalyze(AllInfo *&all)
{
return 0;
}
// Structure
typedef struct
{
char model[100];
char log[_MAX_PATH];
char path[_MAX_PATH];
}AllInfo;
Here is my C# code.
public unsafe void Execute()
{
// Assigning values
public AllInfo ana_alls = new AllInfo();
AllInfo adt = new AllInfo(0);
adt.Eye_img_num = 5;
adt.Flow_log = #"D:\\HTML\\File.txt";
adt.Pano_path = #"D:\\HTML\\Img.bmp";
ana_alls = adt;
// Preparing data for marshalling
IntPtr intPtrsAll = new IntPtr();
intPtrsAll = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(AllInfo)));
Marshal.StructureToPtr<AllInfo>(ana_alls, intPtrsAll, false);
// DLL calling
int ret = 0 - DataAnalyze(ref intPtrsAll);
}
// Structure
[StructLayout(LayoutKind.Sequential)]
public struct AllInfo
{
public AllInfo(int n)
{
this.model = string.Empty;
this.path = string.Empty;
this.log = string.Empty;
}
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string model;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string log;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string path;
}
// DLL Calling
[DllImport(#"D:\\HTML\\Data\\DLL\\Dara_Dll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int DataAnalyze(ref IntPtr ana_all);
In the C++ method, I am getting data in path of AllInfo *&all as follow:
path :{'\0','\0','\0','\0','\0','\0','\0','\0','D',':','\\','H','T','M','L','\\','I','m','g','.','b','m','p','\0','\0',........}
I am getting similar output for log also. I don't know why '\0' added from 0 to 7 index of each char[] data in C++ that is coming from C#. Where am I doing it wrong?
I could not find out what I'm doing wrong. I think I'm doing everything by the numbers, but it looks like I'm missing something.
On the managed side:
public override List<TimerPair> GetNativeTimers()
{
var listPtr = HgrDll.GetNativeTimers(out var size, out var ptrShift);
if (listPtr == IntPtr.Zero)
throw new NullReferenceException("sounds like the list"
+ " pointer was not given");
var timerPairList = new List<TimerPair>(size);
var structSize = Marshal.SizeOf(typeof(TimerPairStruct2));
for (var i = 0; i < size; i++)
{
var o = Marshal.PtrToStructure<TimerPairStruct2>(listPtr).ToTimerPair();
timerPairList.Add(o);
listPtr += structSize;
}
return timerPairList;
}
The target struct (used for intermediate compute) and class:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct TimerPairStruct2 : ITimerPairStruct
{
public long Time;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
public string Descr;
public TimerPair ToTimerPair() => new TimerPair(Descr, Time);
}
public class TimerPair
{
public string Descr { get; set; }
public TimeSpan Time { get; set; }
public TimerPair(string descr, long time)
{
Descr = descr;
Time = new TimeSpan(0,0,0,0, (int)((double)time/1000000));
}
}
For the Pinvoke part:
[DllImport("DwarfHomograph.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr GetNativeTimers(out int size, out int ptrShift);
and now on the native side:
DwarfHgr* DwarfAsset = nullptr;
extern "C" __declspec(dllexport) list<TimerPair>* GetNativeTimers(
int* size,
int* ptrShift)
{
return DwarfAsset->GetNativeTimers(size, ptrShift);
}
the used method from the DwarfHgr class:
list<TimerPair>* DwarfHgr::GetNativeTimers(int* size, int* ptrShift)
{
TimerPairsList = Sw.AsTimerPairs();
*size = static_cast<int>(TimerPairsList.size());
*ptrShift = sizeof(TimerPair);
return &TimerPairsList;
}
and the member is list<TimerPair> TimerPairsList;
The struct I'm marshaling is:
//#pragma pack(push,8)
struct __declspec(dllexport) TimerPair final
{
long long Time{}; // ns
char Descr[64]{};
};
//#pragma pack(pop)
I'm having unit tests in both native and managed sides; the expected result looks like (native):
ImageA processing: 22.0704ms
ImageB processing: 22.0448ms
MatFullA.copyTo: 1.87452ms
MatToSparse: 1.93482ms
calcOpticalFlowFarneback: 1115.48ms
create flowSparse image: 10.1416ms
But I have this (managed):
°óŠqÉ: 1,964,675.000000ms
q$Fý: 140,725,780.000000ms
xr$Fý: 140,725,780.000000ms
: 549,755.000000ms
à¡CUÉ: 1,964,675.000000ms
: 0.000000ms
Lets say I have the following structure in C
typedef struct
{
int field1;
char field2[16];
} MYSTRUCT;
Now I have a C routine that is called with a pointer to MYSTRUCT and I need to populate the structure, e.g.,
int MyCall(MYSTRUCT *ms)
{
char *hello = "hello world";
int hlen = strlen(hello);
ms->field1 = hlen;
strcpy_s(ms->field2,16,hello);
return(hlen);
}
How would I write MyCall in C#? I have tried this in Visual Studio 2010:
...
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
public struct MYSTRUCT
{
[FieldOffset(0)]
UInt32 field1;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
string field2;
}
public int MyProc(ref MYSTRUCT ms)
{
string hello = "hello world";
int hlen = hello.Length;
Marshal.Copy(hello, ms.field2, 0, hlen); // doesn't work
Array.Copy(hello, ms.field2, hlen); // doesn't work
// tried a number of other ways with no luck
// ms.field2 is not a resolved reference
return(hlen);
}
Thanks for any tips on the right way to do this.
Try changing the StructLayout.
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct MYSTRUCT
{
public UInt32 field1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string field2;
}
Since, you're passing as a reference, have you tried setting it as:
public int MyProc(ref MYSTRUCT ms)
{
string hello = "hello world";
ms.field2 = hello;
return hello.Length;
}
When using the ref keyword, you'll call MyProc like so:
static void Main(string[] args)
{
var s = new MYSTRUCT();
Console.WriteLine(MyProc(ref s)); // you must use "ref" when passing an argument
Console.WriteLine(s.field2);
Console.ReadKey();
}
As the topic says, trying to pass a struct from c# environnement to c++.
c++ code that defines both the struct and the interface:
#pragma pack(push, 4)
struct CEA708CONFIG
{
BYTE b608Service;
BYTE bCompactStream;
BYTE pActiveServices[63];
LONG lActiveServiceCount; //
POINT ptAlignmentPosition;
};
#pragma pack(pop)
interface
__declspec(uuid("{some clsid}"))
ICEA708Decoder : IUnknown {
virtual HRESULT SetConfig(IN const CEA708CONFIG* pConfig) = 0;
virtual HRESULT GetConfig(OUT CEA708CONFIG* pConfig) = 0;
};
now to the c# code, i defined the same struct in c#
[StructLayout(LayoutKind.Sequential, Pack = 4), Serializable]
public struct CEA708CONFIG
{
public byte is608Service;
public byte isCompactStream;
//[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)]
public IntPtr activeServices;
public long activeServiceCount;
public Point alignmentPosition;
};
and the corresponding interface that accepts the config structure
[ComVisible(true), ComImport, Guid("same clsid as above"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICEA708Decoder
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int SetConfig([In, MarshalAs(UnmanagedType.Struct)] ref CEA708CONFIG config);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetConfig([Out, MarshalAs(UnmanagedType.Struct)] out CEA708CONFIG config);
}
my problem occurs whenever i try to pass the structure, i can clearly see that while executing the c# code the entire struct is intialized with "reasonable" values, but once passed to the c++, i see that something has happened during the transaction.
the c# code that makes the magic happen:
CEA708CONFIG cc708Config;
ICEA708Decoder CC708DecoderConfig = CC708Filter as ICEA708Decoder;
if (CC708DecoderConfig == null)
{
throw new ApplicationException("Couldn't get ICEA708Decoder structure");
}
byte[] dataByte = new byte[63];
int size = Marshal.SizeOf(dataByte[0]) * dataByte.Length;
IntPtr pnt = Marshal.AllocHGlobal(size);
dataByte[0] = 1;
Marshal.Copy(dataByte, 0, pnt, dataByte.Length);
cc708Config.activeServices = pnt;
if (0 != (hr = CC708DecoderConfig.SetConfig(ref cc708Config)))
{
throw new ApplicationException("Couldn't SetConfig() because: " + DirectShowLib.DsError.GetErrorText(hr));
}
and the exception triggered by the SetConfig is:
{"Cannot marshal field 'activeServices' of type
'CCReIndexer.Graphs.CEA708CONFIG': Invalid managed/unmanaged type
combination (Int/UInt must be paired with SysInt or SysUInt).":""}
thanks for your help!!
Have you tried transfer array as array?
[StructLayout(LayoutKind.Sequential, Pack = 4), Serializable]
public struct CEA708CONFIG
{
public byte is608Service;
public byte isCompactStream;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)]
public byte[] activeServices;
public long activeServiceCount;
public Point alignmentPosition;
};
byte[] dataByte = new byte[63];
cc708Config.activeServices = dataByte;