views:

76

answers:

1

Hello,

I need to make COM IntetrOp at runtime using reflections. My native COM Object's exposed methods have some parameters as pointers (DWORD*) and some double pointers (DWORD**) and some are user defined types(e.g SomeUDTType objSmeUDTType) and vice versa its pointer(i.e. SomeUDTType *pSomeUDTType).

Now for dynamic method invocation, we have single option for passing parameters as array of object i.e object[] and filling this array statically.

But I need to pass pointers and references and pointers to pointers. For now how can I be able to populate object array as mixed data of simple data types, pointers or references and pointers to pointers.

Working Example:

Native COM exposed method :

STDMETHODIMP MyCallableMethod(DWORD *value_1,BSTR *bstrName,WESContext **a_wesContext)

Translated by tlbimp.exe (COMInterop)

UDTINIDLLib.IRuntimeCalling.MyCallableMethod(ref uint, ref string, System.IntPtr)

Now calling these methods at runtime using reflection at runtime,

See here :

       Assembly asembly = Assembly.LoadFrom("E:\\UDTInIDL\\Debug\\UDTINIDLLib.dll");
        Type[] types = asembly.GetTypes();


        Type type = null;
        //foreach (Type oType in types)
        {
            try
            {
                type = asembly.GetType("UDTINIDLLib.RuntimeCallingClass");

            }
            catch (TypeLoadException e)
            {
                Console.WriteLine(e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            object parameters = new object[3];

            Type CustomType = asembly.GetType("UDTINIDLLib.WESContext");
            object oCustomType = Activator.CreateInstance(CustomType);
            FieldInfo fieldInfo = CustomType.GetField("MachineName",          BindingFlags.Public | BindingFlags.Instance);

            string MachineName = "ss01-cpu-102";
            string MachineIp = "127.0.0.1";
            string Certificate = "UK/78T";

            fieldInfo.SetValue(oCustomType, MachineName);
            fieldInfo.SetValue(oCustomType, MachineIp);
            fieldInfo.SetValue(oCustomType, Certificate);


            object obj = Activator.CreateInstance(type);
            MethodInfo mInfo = type.GetMethod("MyCallableMethod");
            int lengthOfParams = mInfo.GetParameters().Length;
            ParameterInfo [] oParamInfos = mInfo.GetParameters();



           object[] a_params = new object[lengthOfParams];

            int ValueType = 0;

            for(int iCount = 0; iCount<lengthOfParams; iCount++)
            {
                a_params[iCount] = ???; //Now here this array should be populated with corresponding pointers and other objects (i.e WESContext's obj)
            }

            mInfo.Invoke(obj,  a_params);   

Hope code will clarifies ...If any any confusion do let me know I'll edit my question accordingly.

I am stuck here , I'll be obliged if you help me out.(I am confused about "dynamic" keyword might hope it solves the problem)

Is there any need to generate C++/CLI wrappers? and if in which context? Regards Usman

A: 

Just put the values of your arguments directly into the array. For out/ref parameters, the corresponding elements of the array will be replaced by new values returned by the function.

For the double pointer, by far the easiest approach is to use /unsafe and unmanaged pointers, like so (assuming the parameter is used by the method to return a value):

 WESContext* pWesContext; // the returned pointer will end up here
 IntPtr ppWesContext = (IntPtr)&pWesContext;

 // direct call
 MyCallableMethod(..., ppWesContext);

 // reflection
 a_params[3] = ppWesContext;
 mInfo.Invoke(obj, a_params);

After you'll get the pointer to struct in pWesContext, you can use -> to access the members in C#. I'm not sure what memory management rules for your API are, though; it may be that you will, eventually, need to free that struct, but how exactly to do that should be described by the documentation of the API you're trying to call.

Pavel Minaev
Hassan
That's because it expects an `uint`, and you gave it an `int`. If you used a literal (e.g. 123), then suffix it with "u": `123u`. If you used a value of some computation, then explicitly cast it to `uint`.
Pavel Minaev
Can u please tell me how to pass pointers adn references of User defined types e.g in above case SomeUDT* pSomeUDT.? In .NET everything is reference. But when I put oCustomType in one of params i.e in params[3] . It generates the runtime exception : **Target Invocation Method was unhandeled : Exception has been thrown by the target of an invocation.**
Hassan
I already explained how references are handled in Reflection. There really isn't anything more to it - CLR references aren't first-class, and cannot be put as values into an array. The best you can get is the approach I've described, where you put in boxed values, and then Reflection passes a reference to each box to the actual code. Pointers are not relevant here because your method declaration doesn't have any pointers (and refs and pointers are different types).
Pavel Minaev
As for your exception, it simply indicates that the method itself threw an exception once invoked. To find out the actual exception that it threw, you'll have to look at `InnerException` property of the `TargetInvocationException` that you've got. Please post the message you get from there, as well as the code that gives you that (just update the question), and we'll go from there.
Pavel Minaev
Thanks Pavel : This is working almost. But only one problem left with double pointer( I have edited the question at top. ) In this case it is expecting double pointer(** for WESContext) and translated Interop is System.IntPtr. So Now how to tackle this one as well?
Hassan
..One thing it generates tlbimp warning while generating managed tlb: TlbImp : warning TI3015 : At least one of the arguments for 'UDTINIDLLib.RuntimeCallingClass.MyCallableMethod' cannot be marshaled by the runtime marshaler. Such arguments will therefore be passed as a pointer and may require unsafe code to manipulate.
Hassan
@Hassan: I've edited the answer to include info on double pointer, have a look.
Pavel Minaev
Thanks Pavel for your continuous cooperation: But again WESContext is the type which's not included in current assembly rather it exists in other assembly which just produced after interop(produced by tlbim.exe) . It should work in any case. But while I create its pointer (*) in unsafe tag , it says that **"Error: Cannot take the address of, get the size of, or declare a pointer to a managed type ('UDTINIDLLib.WESContext')**
Hassan
That is quite unfortunate - it seems that your COM struct has some fields of pointer-to-interface type, and, in combination with a double-pointer, it makes tlbimp produce a declaration that is, effectively, unusable. One option I see here is to manually tweak the interop assembly - if you change the type of last argument to `ref WESContext[]` (i.e. array passed by reference), that should match the level of indirection described by two pointers in COM. You'll then have to pass it a single-element array, and will receive the value in that first element.
Pavel Minaev
Pavel Minaev