我有一个带有导出函数的C++ DLL:
extern "C" __declspec(dllexport) double* fft(double* dataReal, double* dataImag)
{
[...]
}
此函数计算两个双数组(真实的和虚数)的FFT,并返回实数和虚数分量交错的单个双数组:“雷、林、雷、林......”
我不知道如何在C#中调用此函数。我正在做的是:
[DllImport("fft.dll")]
static extern double[] fft(double[] dataReal, double[] dataImag);
当我这样测试时
double[] foo = fft(new double[] { 1, 2, 3, 4 }, new double[] { 0, 0, 0, 0 });
我得到一个MarshalDirectiveException异常:
无法封送行程'return value':无效的托管/非托管类型组合。
我想这是因为C++ double*
和C#double[]
不太一样,但是我不知道如何修复它。有什么想法吗?
编辑:我更改了签名,以便传递一些额外的信息:
extern "C" __declspec(dllexport) void fft(double* dataReal, double* dataImag, int length, double* output);
我们总是知道output
的长度是2x length
和
[DllImport("fft.dll")]
static extern void fft(double[] dataReal, double[] dataImag, int length, out double[] output);
测试如下:
double[] foo = new double[8];
fft(new double[] { 1, 2, 3, 4 }, new double[] { 0, 0, 0, 0 }, 4, out foo);
现在我得到的是AccessViolationException而不是MarshalDirectiveException。
3条答案
按热度按时间nhhxz33t1#
你的例子有几个问题:
1.虽然可以将指针作为返回值封送回去(您可以使用它来填充托管数组),返回值的内存没有隐含的所有者,这就有可能导致内存泄漏。是否使用new在fft内部分配了缓冲区?如果是,则需要托管代码可以调用的另一个导出来释放内存或改用LocalAlloc(然后是管理端的Marshal.FreeHGlobal)。这充其量是个问题。
相反,我的建议是这样定义fft:
相应的P/Invoke签名为:
再举一个电话的例子:
编辑:根据fft被描述要做的事情进行更新。
e37o9pze2#
无需更改签名。您可以使用以下签名:
然后你需要从返回的
IntPtr
中复制字节。因为你知道输出的大小是输入的两倍,所以你可以用下面的方法来做:result
将包含fft
函数返回的字节。编辑:我相信您会发现
P/Invoke Interop Assistant
工具很有帮助:Managed, Native, and COM Interopef1yzkbh3#
这不是很好的Interop原型。您可以使用两种方法来解决这个问题:
1.更改C函数原型并通过参数返回所有双精度值(希望您有C函数的源代码)
1.使用Rest Wing描述的IntPtr
只有在使用CoTaskMemAlloc函数分配内存的情况下,返回内存引用才能正常工作。否则,您将在内存释放期间发生运行时异常(CLR始终尝试使用CoTaskMemFree释放返回内存)。
我认为最好的方法是第一种,这在彼得·休恩的回答中有所描述。