从C#调用非托管C DLL函数(带有指向struct的指针和指向bool数组的指针)

时间:2014-01-15 12:07:39

标签: c# c pinvoke

我刚刚加入了这个网站,这是我在这里发布的第一篇文章。 我真的可以帮助从我的C#应用​​程序调用非托管DLL函数。我一直在寻找,并找到了各种类似的问题,但没有任何东西非常符合我的需要。

我需要调用的函数是:

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

其中A定义为:

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

我对如何在C#中处理这个问题感到困惑和困惑。我想我需要做这样的事情,但我知道这不太正确:

[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);

我认为这是正确的方法,但我对双精度指针数组(double * dc [4])以及指向布尔数组(BOOL * d)的指针感到困惑。 请有人帮我解决这个问题吗?

使用更多信息进行修改: 在此函数的情况下,数据从C#流向DLL。也就是说,C#分配内存。 我正在考虑如下指定dc,除非以另一种方式更容易:

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

这个数组的大小总是4 x 256.不知怎的,我需要将它编组到结构的IntPtr []字段中?

对于bool数组:

bool[] d = new bool[8];

此数组的长度可能会有所不同,但不一定总是为8(但DLL通过其中一个传入的参数知道它的长度)。

2 个答案:

答案 0 :(得分:3)

结构需要如下所示:

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;
}

关键是这些数组内联存储在结构中,因此必须使用UnmanagedType.ByValArray进行编组。

函数声明应为:

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

这假设最终参数是一个数组而不是指向单个布尔值的指针。只有你知道。还要注意调用约定。如上所述,您的非托管函数为cdecl。你需要在p / invoke中指定它。


现在,您将面临的问题是结构中的2D数组dc。处理它的唯一方法是手动分配和编组非托管内存。您无法使用double[,],因为它不会映射到double*[]。因此,您需要IntPtr[]然后使用Marshal类来完成工作。究竟如何做到这一切都无法从这个问题中看出来。它没有指定谁分配内存。


您在更新中声明要在C#中分配双数组以传递给非托管代码。一些示例代码:

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;
    }
}

答案 1 :(得分:2)

   [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);