-1

我在这里创建一个新问题,因为我现在知道如何提出这个问题,但我仍然是 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,因为我只打算在我的客户端代码中使用这个联合成员。

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

非常感激你的帮助。

4

2 回答 2

2

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

您提供的片段不完整,但说明了一些事情:

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

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

在 (A) 一个数组中,创建了 PosT*,然后在 (B1)+(B2)中,该数组的一些单元被初始化为一些未公开大小的新数组。请注意,对于您的代码片段,第一个“一些”和第二个“一些”可能不相关。

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

这意味着从 C# 的角度来看,您想要实际接收的数据结构是“PosT[][]”,因此 P/Invoke 签名将如下所示:

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

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

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

如果外部“大小”是一个常数:

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

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

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

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

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

     *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- 但是这个需要你在签名中添加“不安全”修饰符C# 端的“addPos”函数。这里有一些关于 unsafe 修饰符的入门:

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

于 2012-12-20T16:52:43.843 回答
0

[更新] 好的,我这里已经有了一些进展,

我无法让它工作,当我传递它时, out IntPtr[] 就像从非托管代码返回时一样,它会引发异常。但是我遇到了这个链接,所以我尝试使用它来传递它 out IntPtr ,这似乎有效。至少现在我找回了指针。我现在需要开始着手编组这一切,以便从中得到我需要的东西。

于 2013-01-03T14:31:14.240 回答