Pinvoke-用指针指向指针参数来调用函数

时间:2012-12-19 17:02:25

标签: c# pointers struct pinvoke union

我在C中有一个带有此签名的函数:

int addPos(int init_array_size, 
           int *cnt, 
           int *array_size, 
           PosT ***posArray, 
           char *infoMsg);

这是PosT的样子:

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

通过P / Invoke在C#中调用此方法的最佳方法是什么?我是否需要在C#中定义一个代表PosT的类?如何将Pos#*** posArray参数从C#传递到C?

2 个答案:

答案 0 :(得分:2)

你已经描述了PosT的样子,但这还不够。首先,必须知道函数期望作为*** PosT参数传递什么,并且只有那么你可以考虑从C ++或C#端调用它。

我知道这可能不符合您的意愿,但请查看:

PosT p;
PosT* ptr = &p;
PosT** ptr2 = &ptr;
PosT*** ptr3 = &ptr2;
func(...., ptr3, ...); // OK!?


PosT* ptr = new PosT[123];
PosT** ptr2 = &ptr;
PosT*** ptr3 = &ptr2;
func(...., ptr3, ...); // OK!?


PosT** ptr2 = new PosT[5];
for(int i=0;i<5;++i) ptr2[i] = new PosT[123];
PosT*** ptr3 = &ptr2;
func(...., ptr3, ...); // OK!??

等等。我快速构建的哪一个内存结构对于该功能是否正确?这就是确定您必须从C#端传递的数据类型。

如果函数接受了一个你称之为“jagged aray的可变引用”的东西(所以我提供了最后一个例子),所以P / Invoke声明将是:

[..extern..]
void func(....., ref PosT[][] posArray, ...);

调用类似于:

func(...., new PosT[][] { 
         new PosT[] { new PosT{ d = ... }, new PosT{ d = ... }, ... },
         new PosT[] { new PosT{ d = ... }, new PosT{ d = ... }, ... },
         ... },
    .... );

但请首先检查此功能的预期。使用 * ,实际上只有猜测的可能性太多了。你说它来自某个API - 首先检查它的文档!告诉我们这个函数到底需要什么,我/有人会告诉你如何在C#中构建这样的POD。其他方式它将无法正常工作! :)

PS。抱歉蹩脚的C ++ / C#代码,我匆匆而且只有几分钟写这个:/

答案 1 :(得分:1)

问题#1我需要在CSharp中定义一个代表PosT的类吗?
您应该将PosT定义为c#中的结构。由于这是一个union结构,因此您需要应用StructLayout属性,并完全定义其他结构。它可能看起来像这样:

struct  dpos { };
struct  epo  { };
struct  bpos { };
struct  spos { };
[System.Runtime.InteropServices.StructLayout(
    System.Runtime.InteropServices.LayoutKind.Explicit, Size=99)]
struct PosT {
    [System.Runtime.InteropServices.FieldOffset(0)] dpos   d;
    [System.Runtime.InteropServices.FieldOffset(0)] epo    e;
    [System.Runtime.InteropServices.FieldOffset(0)] bpos   b;
    [System.Runtime.InteropServices.FieldOffset(0)] spos   c;
    };

**请注意,“尺寸= 99”不是正确。理想情况下,您应将此大小设置为最大的封闭结构使用的字节数。

问题#2-我如何将跨越frm CSharp的PosT *** posArray参数传递给C?
你必须非常小心这样做,特别是如果你希望在C#下为posArray分配内存。您不希望将缓冲区传递给.NET,以便.NET GC即将移动/更改。如果C正在返回缓冲区,你应该没问题(但如果没有C函数让你释放内存,你会泄漏内存)。 你应该看看Default Marshaling for Arrays,www.pinvoke.net可能有一些例子。如果要将posArray缓冲区从C#发送到C,则必须使用不安全的上下文(见下文)。我不确定如何处理该级别或重定向。您可以使用'ref'关键字处理您的cnt和array_size。

问题#3-如何为所有人指定编组?
看一下上面的链接,特别是默认的阵列编组。您可能还需要使用一些测试代码,在调用后中断并使用即时窗口来调查结构。


记住

如果使用“UNSAFE”开关编译c#,你几乎可以做一个直接(类似c)的调用。例如:

unsafe public MainWindow() {
    InitializeComponent();
    int abc = 4;
    int* abcPtr = &abc;
    *abcPtr = 8;

    fixed (PosT* gog = new PosT[30]) {
        PosT* gogPtr = (gog + 1);
        }
    }

你必须 非常 小心,因为C#管理它自己的记忆并可以移动你的东西(参见上面的“修复”)。我不推荐这个,但它对于快速和肮脏的事情很有用

此外,如果API在tlb(表库)中定义,Visual Studio通常会提供通过stub dll为您创建绑定(请参阅tlbimp命令)


希望有人可以提供更具体的回复,但我认为这些信息至少可以让你开始。