PInvoke,带有指针到指针到指针参数的函数调用 [英] PInvoke, function call with pointer-to-pointer-to-pointer parameter

查看:100
本文介绍了PInvoke,带有指针到指针到指针参数的函数调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am creating a new question here as I now know how to ask this question, but I'm still a newb in PInvoke.

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

I have a C API, with the following structures in it:

    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语言中这样调用的方式:

the way I call this in C like this:

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

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

在addPos()内部的

内存将分配给posArray,并且还将填充它.使用calloc分配是这样的:

inside addPos() memory will be allocated to posArray and it will also be populated. allocation is like this using calloc:

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

         /* Population */
         ...
      }

我还有另一个函数将被调用以释放该内存.

I have another function that will be called to deallocate that memory.

现在我想在C#中做同样的事情,

Now I want to do the same in C#,

我已经在我的C#类中创建了此代码:

I have created this in my C# class:

    [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,因为我只打算在客户代码中使用这个联合体成员.

I have only defined d, as I am only going to use this member of the union in my client code.

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

Now in order to call addPos() how should I create and pass posArray ?

非常感谢您的帮助.

推荐答案

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

One of the really important thing to note is that the third * from the PosT*** is there just to provide possibility to return the allocated pointer to the caller. This means that in fact the type is PosT** and that in C# parameter will have to be declared either ref or out modifier.

您提供的片段是不完整的,但只能说明以下内容:

The fragment you've provided is incomplete, but tells few things:

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

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

在(A)数组中创建PosT *,然后在(B1)+(B2)中将该数组的 some 单元格初始化为 some 个新数组大小未公开.请注意,使用您的代码段,第一个"some"和第二个"some"可能是不相关的.

in (A) an array, PosT* is created, then in (B1)+(B2) some cells of that array are initialized to some new arrays of undisclosed size. Please note that with your code snippet, the first 'some' and the second 'some' may be unrelated.

此外,这些数组的大小是未知的,除了最顶层的大小"可能来自参数之外.

Also, the sizes of those arrays are unknown, except for the top-most "size" that probably comes from parameters.

这意味着从C#角度来看,您要实际接收的数据结构是"PosT [] []",因此P/Invoke签名将类似于:

This means that from C# point of view, the datastructre you want to actually receive is "PosT[][]", so the P/Invoke signature would be like:

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

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

so, even in C# it would be an 2-dimensional jagged array of returned by parameter, just like in C/C++.

但是,与C中可以有指向非特定数据块的松散指针不同,在C#中,每个数组必须具有精确已知的长度.由于您可能已经知道尺寸",因此您可以告诉封送人员第一个尺寸的尺寸是什么.

However, unlike C where you can have loose pointers that points to unspecific blocks of data, in C#, every array must have a precisely known length. As the "size" is probably known to you, you can tell the marshaller what is the size of the first dimension.

如果外部尺寸"为常数:

If the outer "size" is a constant:

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

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

or, if it is passed by parameter:

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

当然,假设"size"确实是第一个参数.

of course assuming that "size" is really the very first parameter.

但是,所有这些都不指定内部数组的大小.此外,使用您提供的代码段,这些内部数组的长度可能不同.让我玩一下您介绍的代码段:

However, all of this does not specify the size of the inner arrays. What's more, with the code snippet you've provided, those inner arrays may differ in their lengths. Let me play with the code snippet you have presented:

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

更糟糕的是,您的代码段甚至不显示内部数组是否唯一,因此即使您的代码段也允许这样做:

Even worse, your snippet does not even show whether the inner arrays are unique or not, so even this is allowed with your snippet:

     *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个内部数组.这也是完全正确的数据结构,并且您的代码段也有可能.

Note that here I allocate only 2 inner arrays, and I set all cells of the outer array to point to one of those two inner arrays - outer array may have 500 cells, but there are only 2 inner ones. That's also completely correct datastructure and it is possible with your snippet.

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

In either of those two cases, there is no pretty way of telling the .Net Marshaller about the layout of such data structure. You'd have to obtain the outer array as an array of pointers:

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

您可以想象

将(PosT **)转换为(void *),然后在程序中的某个位置,您必须手动将各种IntPtr解压缩为适当长度的PosT [].当然,您实际上必须以某种方式猜测正确的长度是什么.

which you can imagine as casting your (PosT**) into (void*) and later then, somewhere in your program, you'd have to manually unpack those various IntPtrs into PosT[] of proper lengths. Of course, you'd have to actually somehow guess what is the correct length.

以下是从IntPtr读取结构数组的方法:从IntPtr获取结构数组

Here's how to read an array of structs from IntPtr: Getting Array of struct from IntPtr

是的,我完全忘记了这一点,当然,在C#端,您可以仅将参数作为原始指针来获取,因此可以代替out PosT[][] posarray来使用PosT*** posarray-但是,该 require ,即可在C#端将不安全"修饰符添加到"addPos"函数的签名中.这是有关不安全修饰符的一些入门知识:

ah, and I completely forgot that of course, on C# side, you can just obtain the parameter as a raw pointer, so instead of out PosT[][] posarray you can just PosT*** posarray - this one however will require you to add the 'unsafe' modifier to the signature of the "addPos" function on the C# side. Here's some primer on unsafe modifier:

http://www.codeproject.com /Articles/1453/在C中获取不安全的指针

这篇关于PInvoke,带有指针到指针到指针参数的函数调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆