none
How to pass double pointers for COM Interop via MethodInfo.Invoke ? RRS feed

  • Question

  • 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 its pointer to pointer(i.e. SomeUDTType **pSomeUDTType).

    I need to pass pointers to pointers. For now how can I be able to populate "object" array as pointers to pointers of SomeUDTType.

    Working Example: 

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

    tlbImp.exe generated output: 

        DTINIDLLib.RuntimeCallingClass.MyCallableMethod(ref uint, ref string, System.IntrPtr)


    Regards
    Usman

    Mutlithreading
    Monday, September 6, 2010 1:57 PM

Answers

  • Hi again,

    Here is the solution to the UDT scenario.

    Supposing that the UDT in COM is defined like this:

    [uuid(8B9624BE-522B-43FC-A9F8-1C7CBF6D701D)]
    typedef struct someUDT {
     char msg1[4];
     char msg2[4]; 
    }someUDT;
    

    The COM method that using the UDT double pointer is defined like this:

    HRESULT __stdcall ComCar::test_method3(someUDT **p2p_UDT)
    {
    	printf("\n");
    	for(int i=0;i<4;i++)
    	{
    		::printf("%c", (*p2p_UDT)->msg1[i]);
    	}
    	printf("\n");
    	for(int i=0;i<4;i++)
    	{
    		::printf("%c", (*p2p_UDT)->msg2[i]);
    	}
    	printf("\n");
    	return S_OK;
    }
    

    After add reference to the COM library (when the interop assembly is generated), we could use the following code to call that COM method.

     someUDT udt = new someUDT();
     udt.msg1 = new SByte[] { (SByte)'a', (SByte)'a', (SByte)'a', (SByte)'a' };
     udt.msg2 = new SByte[] { (SByte)'b', (SByte)'b', (SByte)'b', (SByte)'b' };
     iRadio.test_method2(ref udt);
    
    
     IntPtr p_udt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(someUDT)));
     Marshal.StructureToPtr(udt, p_udt, false);
     IntPtr p2p_udt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
     Marshal.StructureToPtr(p_udt, p2p_udt, false);
    
     iXXX.test_method3(p2p_udt);
    

     

    The above is Approach 1 as I showed before for the primitive type DWORD.

    To use Approach 2, we need to modify it like below. Otherwise we will get the "Object contains non-primitive or non-blittable data" error.

     Int32 udt_size = Marshal.SizeOf(udt);
     IntPtr p_udt = Marshal.AllocHGlobal(udt_size);
     Marshal.StructureToPtr(udt, p_udt, true);
     GCHandle handle_p2p_udt = GCHandle.Alloc(p_udt, GCHandleType.Pinned);
     IntPtr p2p_udt = handle_p2p_udt.AddrOfPinnedObject();
    
     iXXX.test_method3(p2p_udt);

     

    All in all, if the method of the COM component's interface use some UDT as its parameters, we could just rely on the interop assembly that is automatically generated to fulfill our interop purpose.

    No matter how many levels of indirection is involved using the pointers or pointer to pointers .... We can always use the above approach to handle it. What we need to do is just pass a proper IntPtr, which contains the proper address that the COM method expects.

    For primitive types, we could just pin them and pass their IntPtr for interop.

    For complex types, we could allocate some space from unmanaged memory and copy them there from managed memory. And then pass the resulted IntPtr for interop.

    The following reference could be helpful:

    1. "Object contains non-primitive or non-blittable data" error

    2. "Warning MIDL2368" When UDT Tag Name Differs from UDT Name

    3. User-Defined Data Types (Component Automation) 

     


    Please mark the right answer at right time.
    Thanks,
    Sam

     

    • Edited by SamAgain Friday, September 10, 2010 7:32 AM refine
    • Marked as answer by glitteringsound Monday, September 13, 2010 9:33 AM
    Friday, September 10, 2010 6:58 AM

All replies

  • 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_wesContex

    Translated by tlbimp.exe (COMInterop)

    UDTINIDLLib.RuntimeCallingClass.MyCallableMethod(ref uint, ref string, UDTINIDLLib

    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


    Mutlithreading
    • Merged by SamAgain Monday, September 13, 2010 9:23 AM duplicated
    Sunday, September 5, 2010 11:29 PM
  • Hi,

    Thanks for your post.

    For passing the DWORD ** pointer, please take a look the following code snippets.

     

    The COM method prototype for test is like this:

     HRESULT test_method(DWORD value, DWORD* pointer, DWORD** p2p);
    

     

    And its corresponding prototype in c# is like this:

     void test_method(uint value, ref uint pointer, System.IntPtr p2p)
    

     

     

    We can use it like this:

     

    Approach 1: Copy the parameters from managed heap to unmanaged heap and pass them to COM method.

     UInt32 by_value = 123;
     UInt32 by_pointer = 456;
     UInt32 by_p2p = 789
    
     IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(UInt32)));
     Marshal.StructureToPtr(by_p2p, p, false);
     IntPtr p2p = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
     Marshal.StructureToPtr(p, p2p, false);
    
     iXXX.test_method(by_value, ref by_pointer, p2p);
    

     

    Approach 2: Pin the parameters on the managed heap and pass them to the COM method.

     UInt32 by_value = 123;
     UInt32 by_pointer = 456;
     UInt32 by_p2p = 789
    
     GCHandle handle_p = GCHandle.Alloc(by_p2p, GCHandleType.Pinned);
     IntPtr p = handle_p.AddrOfPinnedObject();
     GCHandle handle_p2p = GCHandle.Alloc(p, GCHandleType.Pinned);
     IntPtr p2p = handle_p2p.AddrOfPinnedObject();
     
     iXXX.test_method(by_value, ref by_pointer, p2p);
    

    Passing UDTs are similar. I hope the above info could give you some hints.

     


    Please mark the right answer at right time.
    Thanks,
    Sam

     

    • Edited by SamAgain Friday, September 10, 2010 5:41 AM refine
    Friday, September 10, 2010 5:33 AM
  • Hi again,

    Here is the solution to the UDT scenario.

    Supposing that the UDT in COM is defined like this:

    [uuid(8B9624BE-522B-43FC-A9F8-1C7CBF6D701D)]
    typedef struct someUDT {
     char msg1[4];
     char msg2[4]; 
    }someUDT;
    

    The COM method that using the UDT double pointer is defined like this:

    HRESULT __stdcall ComCar::test_method3(someUDT **p2p_UDT)
    {
    	printf("\n");
    	for(int i=0;i<4;i++)
    	{
    		::printf("%c", (*p2p_UDT)->msg1[i]);
    	}
    	printf("\n");
    	for(int i=0;i<4;i++)
    	{
    		::printf("%c", (*p2p_UDT)->msg2[i]);
    	}
    	printf("\n");
    	return S_OK;
    }
    

    After add reference to the COM library (when the interop assembly is generated), we could use the following code to call that COM method.

     someUDT udt = new someUDT();
     udt.msg1 = new SByte[] { (SByte)'a', (SByte)'a', (SByte)'a', (SByte)'a' };
     udt.msg2 = new SByte[] { (SByte)'b', (SByte)'b', (SByte)'b', (SByte)'b' };
     iRadio.test_method2(ref udt);
    
    
     IntPtr p_udt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(someUDT)));
     Marshal.StructureToPtr(udt, p_udt, false);
     IntPtr p2p_udt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
     Marshal.StructureToPtr(p_udt, p2p_udt, false);
    
     iXXX.test_method3(p2p_udt);
    

     

    The above is Approach 1 as I showed before for the primitive type DWORD.

    To use Approach 2, we need to modify it like below. Otherwise we will get the "Object contains non-primitive or non-blittable data" error.

     Int32 udt_size = Marshal.SizeOf(udt);
     IntPtr p_udt = Marshal.AllocHGlobal(udt_size);
     Marshal.StructureToPtr(udt, p_udt, true);
     GCHandle handle_p2p_udt = GCHandle.Alloc(p_udt, GCHandleType.Pinned);
     IntPtr p2p_udt = handle_p2p_udt.AddrOfPinnedObject();
    
     iXXX.test_method3(p2p_udt);

     

    All in all, if the method of the COM component's interface use some UDT as its parameters, we could just rely on the interop assembly that is automatically generated to fulfill our interop purpose.

    No matter how many levels of indirection is involved using the pointers or pointer to pointers .... We can always use the above approach to handle it. What we need to do is just pass a proper IntPtr, which contains the proper address that the COM method expects.

    For primitive types, we could just pin them and pass their IntPtr for interop.

    For complex types, we could allocate some space from unmanaged memory and copy them there from managed memory. And then pass the resulted IntPtr for interop.

    The following reference could be helpful:

    1. "Object contains non-primitive or non-blittable data" error

    2. "Warning MIDL2368" When UDT Tag Name Differs from UDT Name

    3. User-Defined Data Types (Component Automation) 

     


    Please mark the right answer at right time.
    Thanks,
    Sam

     

    • Edited by SamAgain Friday, September 10, 2010 7:32 AM refine
    • Marked as answer by glitteringsound Monday, September 13, 2010 9:33 AM
    Friday, September 10, 2010 6:58 AM
  • Hi,

    Possible duplicated thread: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/6839e03a-81f0-4a01-b0c7-c8aa004113dc


    Please mark the right answer at right time.
    Thanks,
    Sam
    Friday, September 10, 2010 7:27 AM
  • Hi,

    I am writing to check the status of the thread. Does it help?


    Please mark the right answer at right time.
    Thanks,
    Sam
    Monday, September 13, 2010 9:22 AM
  • Hi SamAgain...

     

    Thanks for your valuable comments and help. For the time being I just thrown away Inter Op to adopt for calling COM components. I am planning to call COM components by iterating COM interface's vtable and little playing with assembly. So remaining in native environment  and calling native COM components suits and appeals me.

     

    I am considering u'r both approaches as right answers. SO you marked your latest post as right answer.

    If somehow I need again to go for inter op I'll pick and adopt u'r proposed solutions.

     

    Thanks and Regards

    Usman


    Mutlithreading
    Monday, September 13, 2010 9:33 AM