我在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?
答案 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命令)
希望有人可以提供更具体的回复,但我认为这些信息至少可以让你开始。