PInvoke:返回的双精度数组的问题?

时间:2013-04-04 08:42:29

标签: c# c++ types double pinvoke

我正在使用PInvoke从我的C#程序中调用C ++函数。代码如下所示:

IntPtr data = Poll(this.vhtHand);
double[] arr = new double[NR_FINGERS /* = 5 */ * NR_JOINTS /* = 3*/];
Marshal.Copy(data, arr, 0, arr.Length);

Poll()的签名如下所示:

[DllImport("VirtualHandBridge.dll")]
static public extern IntPtr Poll(IntPtr hand);

C函数Poll的签名:

extern "C" __declspec(dllexport) double* Poll(CyberHand::Hand* hand)

除非我有一个巨大的脑力衰竭(不可否认,对我来说相当普遍),这看起来像是应该有效。

但是,我得到的双值完全不正确,我认为这是因为内存使用不正确。我查了一下,我认为C#和C ++中的双打大小相同,但也许还有其他问题在这里播放。让我错误的一点是,Marshal.Copy永远不会被告知它应该期望什么类型的数据,但我读到它应该以这种方式使用。

任何线索,任何人?如果需要,我可以发布正确的结果和返回的结果。

2 个答案:

答案 0 :(得分:4)

您缺少CallingConvention属性,它是Cdecl。

你真的想要更好的功能签名,由于内存管理问题,所需的手动编组,获得正确大小的数组的不确定性以及复制数据的要求,你所拥有的功能签名非常脆弱。始终支持调用者传递您的本机代码填充的缓冲区:

extern "C" __declspec(dllexport)
int __stdcall Poll(CyberHand::Hand* hand, double* buffer, size_t bufferSize)

[DllImport("foo.dll")]
private static extern int Poll(IntPtr hand, double[] buffer, int bufferSize)

使用int返回值报告状态代码。像报告错误代码的负值一样,返回实际复制到缓冲区中的元素数量的正值。

答案 1 :(得分:1)

只要正确声明P / Invoke,您甚至不需要编组这样的数据。

如果你的CyberHand::Hand*实际上是指向double的指针,那么你应该将你的P / Invoke声明为

[DllImport("VirtualHandBridge.dll")]
static public extern IntPtr Poll(double[] data);

然后用你的双打阵列调用它。

如果不是真的是一系列双打,那么你肯定无法做你正在做的事情。

另外,你的'C'函数如何知道数组有多大?它是固定大小的吗?

IntPtr返回值将是一个问题。 double*指向的是什么?一个数组或一个项目?

您可以发现,为您正在调用的函数编写一个更简单,更友好的“C”包装器更容易(如果可以的话),并调用包装器函数本身。当然,如果您可以更改“C”DLL的源代码,则只能这样做。但是如果不确切知道你的功能是什么,我就不能给你具体的建议。

[编辑]

好的,你的代码理论上应该工作如果传回的内存没有被搞乱(例如释放)。如果它不起作用,那么我怀疑这样的事情正在发生。你肯定会更好地编写一个包装器'C'函数,它填充由C#分配的数组并传递给函数,而不是将指针传回一些内部存储器。

BTW:我不喜欢在没有传递该块大小的情况下传递指向内存块的指针的代码。似乎有点容易讨厌的东西。