PInvoke,带指针到指针指针参数的函数调用

时间:2012-12-20 14:53:44

标签: c# pointers struct pinvoke

我正在创建一个新问题,因为我现在知道如何提出这个问题,但我仍然是PInvoke的新手。

我有一个C API,其中包含以下结构:

    typedef union pu
    {
        struct  dpos   d;
        struct  epo    e;
        struct  bpos   b;
        struct  spos   c;
     } PosT ;


    typedef struct dpos
    {
        int     id;                       
        char    id2[6];      
        int     num;
        char    code[10];
        char    type[3];
     } DPosT ;

以及以下API函数:

    int addPos(..., PosT ***posArray,...)

我在C中这样称呼的方式如下:

    int main(int argc, const char *argv[])
    {
        ...
        PosT  **posArray = NULL;
        ...

        ret_sts = addPos(..., &posArray, ...);
        ...
    }

内部的addPos()内存将被分配给posArray,它也将被填充。使用calloc分配是这样的:

    int addPos(..., PosT ***posArray, ...)
    {
         PosT **ptr;
         ...
         *posArray = (PosT **) calloc(size, sizeof(PosT *));
         *ptr = (PosT *)calloc(...);
         ...
         (*posArray)[(*cntr)++] = *ptr;
         ...

         /* Population */
         ...
      }

我有另一个函数将被调用来释放该内存。

现在我想在C#,

中做同样的事情

我在C#类中创建了这个:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct DPositionT
    {
       public int Id;

       [MarshalAs(UnmanagedType.LPStr, SizeConst = Constants.Id2Len)]
       public string Id2;

       public int Num;

       [MarshalAs(UnmanagedType.LPStr, SizeConst = Constants.CodeLen)]
       public string Code;

       [MarshalAs(UnmanagedType.LPStr, SizeConst = Constants.TypeLen)]
       public string type;
    }

    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit )]
    struct PosT
    {
        [System.Runtime.InteropServices.FieldOffset(0)]
        DPosT d;
    };

我只定义了d,因为我只会在我的客户端代码中使用union的这个成员。

现在为了调用addPos()我应该如何创建并传递posArray?

非常感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

需要注意的一件非常重要的事情是来自PosT ***的第三个*只是为了提供将分配的指针返回给调用者的可能性。这意味着实际上类型是PosT **,并且在C#参数中必须声明refout修饰符。

您提供的片段不完整,但却说的很少:

     *posArray = (PosT **) calloc(size, sizeof(PosT *)); // A

     *ptr = (PosT *)calloc(...);   // B1
     ...
     (*posArray)[(*cntr)++] = *ptr;  // B2

在(A)数组中,创建PosT *,然后在(B1)+(B2)该数组的某些单元格初始化为某些新数组未公开的尺寸。请注意,使用您的代码段,第一个“some”和第二个“some”可能不相关。

此外,这些数组的大小是未知的,除了可能来自参数的最顶层“大小”。

这意味着从C#的角度来看,你想要实际接收的数据结构是“PosT [] []”,所以P / Invoke签名就像:

[...]
... addPos(..., out PosT[][] posArray, ....)

所以,即使在C#中,它也是一个由参数返回的二维锯齿状数组,就像在C / C ++中一样。

然而,与C不同,在C中你可以指向指向非特定数据块的松散指针,在C#中,每个数组必须具有精确已知的长度。由于您可能知道“大小”,您可以告诉编组人员第一维的大小是多少。

如果外部“尺寸”是常数:

[...]
... addPos(..., [MarshalAs(SizeConst=100)]out PosT[][] posArray, ....)

或者,如果它是通过参数传递的:

[...]
... addPos(int size, ...., [MarshalAs(SizeParamIndex=0)]out PosT[][] posArray, ....)

当然假设“size”确实是第一个参数。

但是,所有都没有指定内部数组的大小。更重要的是,使用您提供的代码段,这些内部数组可能长度不同。让我来玩你提供的代码片段:

     *posArray = (PosT **) calloc(size, sizeof(PosT *)); // A

     (*cntr) = 0

     for(int idx = 0; idx<size; ++idx)
     {
         *ptr = (PosT *)calloc(5+40*(*cntr));   // B1

         (*posArray)[(*cntr)++] = *ptr;  // B2
     }

更糟糕的是,你的代码片段甚至没有显示内部数组是否是唯一的,所以即使是你的代码片段也允许这样做:

     *posArray = (PosT **) calloc(size, sizeof(PosT *)); // A

     (*cntr) = 0

     *ptr = (PosT *)calloc(5);   // B1
     *ptr2 = (PosT *)calloc(25);

     for(int idx = 0; idx<size / 2; ++idx)
     {
         (*posArray)[(*cntr)++] = *ptr;  // B2
         (*posArray)[(*cntr)++] = *ptr2;
     }

请注意,这里我只分配了2个内部数组,并且我将外部数组的所有单元格设置为指向这两个内部数组中的一个 - 外部数组可能有500个单元格,但只有2个内部数组。这也是完全正确的数据结构,并且可以使用您的代码段。

在这两种情况中的任何一种情况下,都没有很好的方式告诉.Net Marshaller关于这种数据结构的布局。您必须将外部数组作为指针数组获取:

[...]
... addPos(int size, ...., [MarshalAs(SizeParamIndex=0)]out IntPtr[] posArray, ....)

你可以想象将你的(PosT **)投射到(void *)然后,在程序的某个地方,你必须手动将那些不同的IntPtrs解包到适当长度的PosT []中。当然,你必须以某种方式猜测正确的长度。

以下是如何从IntPtr中读取结构数组:Getting Array of struct from IntPtr

编辑:

啊,我完全忘记了,当然,在C#方面,你可以将参数作为原始指针获取,所以代替out PosT[][] posarray你只能PosT*** posarray - 但这个会< strong> require 您可以将“unsafe”修饰符添加到C#端“addPos”函数的签名中。以下是关于不安全修饰语的一些入门知识:

http://www.codeproject.com/Articles/1453/Getting-unsafe-with-pointers-in-C

答案 1 :(得分:0)

<强> [更新] 好的,我在这里取得了一些进展,

我无法让它工作,当我将其作为 out IntPtr[] 传递时,从非托管代码返回时它会抛出异常。但是我遇到了这个link,所以我尝试使用 out IntPtr 传递它,这似乎有效。至少现在我得到了指针。我现在需要开始编写所有这些以获得我需要的东西。