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

PInvoke: Issue with returned array of doubles?

本文关键字:问题 数组 双精度 返回 PInvoke      更新时间:2023-10-16

我使用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++的double在大小上是相同的,但也许这里有一些其他的问题。惹恼我的一件事是Marshal.Copy从来没有被告知它应该期望什么类型的数据,但我读到它应该这样使用。

有什么线索吗?如果需要,我可以发布正确的结果和返回的结果。

您缺少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返回值报告状态码。就像负值用于报告错误代码一样,正值用于返回实际复制到缓冲区中的元素数量。

您甚至不需要这样封送数据,只要正确声明p/Invoke即可。

如果你的CyberHand::Hand*实际上是一个指向双精度体的指针,那么你应该将你的p/Invoke声明为

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

然后用你的双精度数组来调用它

如果它不是真正的双精度数组,那么你当然不能做你正在做的事情。

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

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

您可能会发现(如果可以的话)为您正在调用的函数编写一个更简单更友好的'C'包装器,并调用包装器函数本身更容易。当然,只有当你可以改变'C' DLL的源代码时,你才能做到这一点。但是在不知道你的函数是做什么的情况下,我无法给你具体的建议。

[编辑]

好的,你的代码理论上应该工作,如果被传递回的内存没有被打乱(例如释放)。如果它不起作用,那么我怀疑正在发生类似的事情。你最好写一个包装器'C'函数来填充c#分配的数组,并传递给函数,而不是传递一个指向内部内存的指针。

BTW:我不喜欢传递指针到内存块的代码,而不传递该块的大小。似乎有一些讨厌的东西。