none
C# DllImport C dll RRS feed

  • Question

  • Hi Dear,

    Recently, I have encountered some confusion when I try to call C language interface to C#, I need your help please.

    The following is C language head file:

    MyTest.h:

    #pragma once
    #define MYTEST_API  __declspec(dllimport)
    
    #if defined (__cplusplus)
    extern "C" {
    #endif
    
    	typedef enum _MyTestErrorCode
    	{
    		ERROR0 = 0,
    		ERROR1,
    	} MyTestErrorCode;
    
    	typedef struct _MyTestLocation
    	{
    		int left;
    		int top;
    	} MyTestLocation;
    
    	typedef enum _MyTestEmotion
    	{
    		Emotion0 = 0,
    		Emotion1 = 1,
    	} MyTestEmotion;
    
    	typedef struct _MyTestAttributes
    	{
    		int width;
    		int height;
    		char* MyImageBytes; //image bytes
    		MyTestEmotion myTestEmotion;
    		MyTestLocation myTestLocation;
    	} MyTestAttributes;
    	
    	MYTEST_API MyTestErrorCode MyTest_detect(
    		void* myTestHandle,
    		int* num,
    		MyTestAttributes* myTestAttributes);
    
    #if defined (__cplusplus)
    }
    #endif

    The following is the C# code to import the C language dll.

    MyTestCSharp.cs

    using System;
    using System.Runtime.InteropServices;
    
    namespace MyTest
    {
        class MyTestCSharp
        {
            public enum MyTestErrorCode : int
            {
                ERROR0 = 0,
                ERROR1,
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct MyTestLocation
            {
                public int left;
                public int top;
            }
    
            public enum MyTestEmotion : int
            {
                Emotion0 = 0,
                Emotion1,
            }
            
            [StructLayout(LayoutKind.Sequential)]
            public struct MyTestAttributes
            {
                public int width;
                public int height;
                //Question1
                //How do I transform here from [char*] within C language to C# ?????
                public IntPtr MyImageBytes; //char* MyImageBytes; 
                public MyTestEmotion myTestEmotion;
                public MyTestLocation myTestLocation;
            }
    
            public static class MyTestHelper
            {
                [DllImport("MyTest.dll", CharSet = CharSet.Ansi)]
                public extern static MyTestErrorCode MyTest_detect(
                    IntPtr myTestHandle,
                    ref int num,
                    //Question2
                    //How do I transform here from [struct*] within C langusge to C# ?????
                    //ref MyTestAttributes myTestAttributes); // MyTestAttributes* myTestAttributes
            }
        }
    }

    Now I have two questions:

    Q1, How do I transform [char*] in C language to C#?

    From:

    	typedef struct _MyTestAttributes
    	{
    		int age;
    		char* MyImageBytes; //image bytes
    		MyTestEmotion myTestEmotion;
    		MyTestLocation myTestLocation;
    	} MyTestAttributes;

    To:

        [StructLayout(LayoutKind.Sequential)]
        public struct MyTestAttributes
        {
            public readonly int age;
            //Question1
            //How do I transform here from [char*] within C language to C# ?????
            public IntPtr MyImageBytes; //char* MyImageBytes; 
            public MyTestEmotion myTestEmotion;
            public MyTestLocation myTestLocation;
        }

    [char*] in C language is an unmanaged type, I try to use IntPtr in C#, however, there is a problem that it seems the IntPtr can not get the memory length of the stored binary photo stream which I need to know, so do you have any idea here?

    Q2, How do I transform [struct*] which including another struct and enum in C language to C#?

    From:

    	MYTEST_API MyTestErrorCode MyTest_detect(
    		void* myTestHandle,
    		int* num,
    		MyTestAttributes* myTestAttributes);

    To:

        public static class MyTestHelper
        {
            [DllImport("MyTest.dll", CharSet = CharSet.Ansi)]
            public extern static MyTestErrorCode MyTest_detect(
                IntPtr myTestHandle,
                ref int num,
                //Question2
                //How do I transform here from [struct*] within C language to C# ?????
                //ref MyTestAttributes myTestAttributes); // MyTestAttributes* myTestAttributes
        }

    How do I transform [struct*] which including another struct and enum in C language to C#?

    [struct*] in C language is also an unmanaged type, and stand for an unmanaged structural array here with a fixed array length, also I need to use more than one item within the struct, how can I define the return type in C# here?

    I try to return an array, but it seems throw error when I call it:

        public static class MyTestHelper
        {
            [DllImport("MyTest.dll", CharSet = CharSet.Ansi)]
            public extern static MyTestErrorCode MyTest_detect(
                IntPtr myTestHandle,
                ref int num,
                //I try to return an array, but it seems throw error when I call it
                ref MyTestAttributes[] myTestAttributes); // MyTestAttributes* myTestAttributes
        }

    Call it like:

        int myNum = 10;
        IntPtr myHandle = ...;
        MyTestAttributes[] myTestAttributes = ...;
    
        //error:
        //...... 
        //The bug in the verification section. 
        //Common sources of this bug include user marshaling errors with COM-interop or PInvoke, which can corrupt the stack
        var er = MyTestHelper.MyTest_detect(myHandle, ref myNum, ref myTestAttributes);
        for (int i = 0; i < myNum; i++)
        {
            point.X = myTestAttributes[i].myTestLocation.left;
            point.Y = myTestAttributes[i].myTestLocation.top;
            //...
        }

    Thanks for any kind advice.

    Regards,

    Starry

    • Edited by Stanly Fan Tuesday, May 14, 2019 11:15 AM
    Tuesday, May 14, 2019 9:53 AM

Answers

  • [StructLayout(LayoutKind.Sequential)]
    public struct MyTestAttributes
    {
       public int Width;
       public int Height;
       public byte[] MyImageBytes; //image bytes
       public MyTestEmotion myTestEmotion;
       public MyTestLocation myTestLocation;
    }
    
    [DllImport("dllname")]
    private extern static MyTestErrorCode MyTest_detect (
           IntPtr myTestHandle, ref int num,
           MyTestAttributes[] myTestAttributes);

    Q1: A char* can either be a string or a byte array in C. In C# use either string (or StringBuilder if it is modifiable) or byte[].

    Q2: That depends on what is happening on the native side. If `myTestAttributes` in the function call is an array you're passing to the native code and it is just going to read that data then pass it as a normal array. However, based upon your syntax, if the native call is going to return to you an array of objects along with the # of items (via `num` then the `num` parameter needs to be a ref so it can be modified. 

    In C when you are requesting blocks of data from a function either the caller or callee is responsible for allocating the data. If the caller is responsible then `num` would represent the # of items in the array and `myTestAttributes` would be the preallocated array. So you'd pass it as a normal array after you inited the array to the size you want and set `num` accordingly. Often, when the call returns the callee has updated `num` with the actual # of items that were set so `num` would remain a ref and your code needs to ensure it only uses that amount of the array.

    On the other hand if the callee will allocate the memory then you're really dealing with a pointer so you'd pass the array as `IntPtr` instead. On return `num` would indicate the # of items and `myTestAttributes` would be pointing to a (native) array of the appropriate size.  At this point you'd need to marshal the array back into a managed array you can work with. 

    If the array is fixed size in your struct then attribute it was an unmanaged array with SizeConst set. Ensure the array is properly allocated before making the call. For arrays passed to functions there is no such thing as "fixed size" in C. All arrays are open. So you'd have to ensure it is the correct size. You can still attribute it with the correct size on the parameter though IIRC.

    MSDN has more information on how arrays should be marshalled given the various ways it is done in native code.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Stanly Fan Wednesday, May 15, 2019 6:06 AM
    • Unmarked as answer by Stanly Fan Wednesday, May 15, 2019 6:34 AM
    • Marked as answer by Stanly Fan Wednesday, May 15, 2019 8:00 AM
    Tuesday, May 14, 2019 2:59 PM
    Moderator

All replies

  • [StructLayout(LayoutKind.Sequential)]
    public struct MyTestAttributes
    {
       public int Width;
       public int Height;
       public byte[] MyImageBytes; //image bytes
       public MyTestEmotion myTestEmotion;
       public MyTestLocation myTestLocation;
    }
    
    [DllImport("dllname")]
    private extern static MyTestErrorCode MyTest_detect (
           IntPtr myTestHandle, ref int num,
           MyTestAttributes[] myTestAttributes);

    Q1: A char* can either be a string or a byte array in C. In C# use either string (or StringBuilder if it is modifiable) or byte[].

    Q2: That depends on what is happening on the native side. If `myTestAttributes` in the function call is an array you're passing to the native code and it is just going to read that data then pass it as a normal array. However, based upon your syntax, if the native call is going to return to you an array of objects along with the # of items (via `num` then the `num` parameter needs to be a ref so it can be modified. 

    In C when you are requesting blocks of data from a function either the caller or callee is responsible for allocating the data. If the caller is responsible then `num` would represent the # of items in the array and `myTestAttributes` would be the preallocated array. So you'd pass it as a normal array after you inited the array to the size you want and set `num` accordingly. Often, when the call returns the callee has updated `num` with the actual # of items that were set so `num` would remain a ref and your code needs to ensure it only uses that amount of the array.

    On the other hand if the callee will allocate the memory then you're really dealing with a pointer so you'd pass the array as `IntPtr` instead. On return `num` would indicate the # of items and `myTestAttributes` would be pointing to a (native) array of the appropriate size.  At this point you'd need to marshal the array back into a managed array you can work with. 

    If the array is fixed size in your struct then attribute it was an unmanaged array with SizeConst set. Ensure the array is properly allocated before making the call. For arrays passed to functions there is no such thing as "fixed size" in C. All arrays are open. So you'd have to ensure it is the correct size. You can still attribute it with the correct size on the parameter though IIRC.

    MSDN has more information on how arrays should be marshalled given the various ways it is done in native code.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Stanly Fan Wednesday, May 15, 2019 6:06 AM
    • Unmarked as answer by Stanly Fan Wednesday, May 15, 2019 6:34 AM
    • Marked as answer by Stanly Fan Wednesday, May 15, 2019 8:00 AM
    Tuesday, May 14, 2019 2:59 PM
    Moderator
  • Hi,

    I solved my problem now, to import a struct array from C to C#, it seems need to use [In, Out] attribute:

        [DllImport("Test.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern void TestInterface2([In, Out] pt[] points, ref int count);

    Thanks for your advice :)

    Regards,

    Starry

    • Edited by Stanly Fan Wednesday, May 15, 2019 8:56 AM
    Wednesday, May 15, 2019 8:55 AM