Calling unmanaged C DLL function from C# (with pointer to struct and pointer to bool array)

StackOverflow https://stackoverflow.com/questions/21137031

  •  28-09-2022
  •  | 
  •  

Question

I’ve just joined the site, and this is my first posting here. I could really do with some help calling an unmanaged DLL function from my C# application. I've been searching around, and have found various similar questions, but nothing which quite fits what I need.

The function I need to call is:

int DevConfig(struct A *a, int b, unsigned int c, BOOL *d);

Where A is defined as:

struct A
{
    double f[4];    
    unsigned int df[4];
    unsigned int dfn[4];
    double *dc[4];
    float dg[4];
    float g;
    UCHAR gs;
};

I’m getting a bit stuck and confused as to how to handle this in the C#. I think I need to do something like this, but I know it’s not quite right:

[StructLayout(LayoutKind.Sequential)]
public struct A
{
    public double[] f = new double[4];
    public uint[] df = new uint[4];
    public uint[] dfn = new uint[4];
    public double[,] dc = new uint[4,256];
    public float[] dg = new float[4];
    public float g;
    public byte gs;
}

[DllImport("DevControl.dll")]
static extern int DevConfig (ref A a, int b, uint c, bool[] d);

I think this is the correct approach, but I’m confused about the array of pointers to doubles (double *dc[4]), and also the pointer to an array of Boolean (BOOL *d). Please could someone help me get this right?

Edit with more info: In the case of this function, data flows from the C# to the DLL. That is, the C# allocates the memory. I was thinking of specifing dc as follows, unless it's easier to do it another way:

double[,] dc = new double[4, 256];

The size of this array is always 4 x 256. Somehow I need to marshal this into the IntPtr[] field of the struct?

And for the bool array:

bool[] d = new bool[8];

The length of this array may vary, it is not necessarily always 8 (but the DLL knows how long it is via one of the other parameters passed in).

Was it helpful?

Solution

The struct needs to look like this:

public struct A
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
    public double[];
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
    public uint[] df;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
    public uint[] dfn;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
    public IntPtr[] dc;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
    public float[] dg;
    public float g;
    public byte gs;
}

The key point is that these arrays are stored inline in the struct and therefore must be marshalled using UnmanagedType.ByValArray.

The function declaration should be:

[DllImport("DevControl.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int DevConfig (ref A a, int b, uint c, bool[] d);

This does assume that the final parameter is an array rather than a pointer to a single boolean. Only you know that. Note also the calling convention. As written, your unmanaged function is cdecl. And you do need to specify that in the p/invoke.


Now, the problem that you will face is the 2D array in the struct, dc. The only way to handle that is to manually allocate and marshal unmanaged memory. You cannot use a double[,] because that simply does not map to double*[]. Hence you need IntPtr[] and then some use of the Marshal class to finish the job. Exactly how all that is to be done cannot be discerned from the question. It does not specify who allocates the memory.


You state in an update that you want to allocate the double arrays in the C# to pass to the unmanaged code. Some example code for that:

IntPtr[] CreateUnmanagedArrays(double[][] arr)
{
    IntPtr[] result = new IntPtr[arr.Length];
    for (int i=0; i<arr.Length; i++)
    {
        result[i] = Marshal.AllocCoTaskMem(arr[i].Length*sizeof(double));
        Marshal.Copy(arr[i], 0, result[i], arr[i].Length);
    }
    return result;
}

void DestroyUnmanagedArrays(IntPtr[] arr)
{
    for (int i=0; i<arr.Length; i++)
    {
        Marshal.FreeCoTaskMem(arr[i]);
        arr[i] = IntPtr.Zero;
    }
}

OTHER TIPS

   [StructLayoutAttribute(LayoutKind.Sequential)]
    public struct A
    {

        [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.R8)]
        public double[] f;

        [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U4)]
        public uint[] df;

        [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U4)]
        public uint[] dfn;

        [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.SysUInt)]
        public System.IntPtr[] dc;

        [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.R4)]
        public float[] dg;

        public float g;

        public byte gs;
    }


[DllImportAttribute("DevControl.dll", EntryPoint = "DevConfig")]
public static extern int DevConfig(ref A a, int b, uint c, ref int d);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top