问题描述
我已通过P/Invoke在C ++ Interop上阅读了各种MSDN页面,此处,并且此处,但我仍然感到困惑.
I've read the various MSDN pages on C++ Interop with P/Invoke here and here but I am still confused.
我需要进入本机代码的一些大双打数组,以及一些需要返回的结果数组.我不知道输出数组的大小.为简单起见,在示例中,我将只使用一个数组.该平台是x64;我读到,在32位和64位环境中,编组内部结构有很大不同,因此这可能很重要.
I have some large arrays of doubles that I need to get into native code, and some resulting arrays that need to get back. I do not know the sizes of the output arrays in advance. For simplicity, I will use only a single array in the example. The platform is x64; I read that marshalling internals are quite different between 32- and 64-bit environments so this might be important.
C#
[DllImport("NativeLib.dll")]
public static extern void ComputeSomething(double[] inputs, int inlen,
[Out] out IntPtr outputs, [Out] out int outlen);
[DllImport("NativeLib.dll")]
public static extern void FreeArray(IntPtr outputs);
public void Compute(double[] inputs, out double[] outputs)
{
IntPtr output_ptr;
int outlen;
ComputeSomething(inputs, inputs.Length, out output_ptr, out outlen);
outputs = new double[outlen];
Marshal.Copy(output_ptr, outputs, 0, outlen);
FreeArray(output_ptr);
}
C ++
extern "C"
{
void ComputeSomething(double* inputs, int input_length,
double** outputs, int* output_length)
{
//...
*output_length = ...;
*outputs = new double[output_length];
//...
}
void FreeArray(double* outputs)
{
delete[] outputs;
}
}
它起作用了,也就是说,我可以读出我在C ++端写到数组中的双打.但是,我想知道:
It works, that is, I can read out the doubles I wrote into the array on the C++ side. However, I wonder:
- 这是使用P/Invoke的正确方法吗?
- 我的签名不是不必要地复杂吗?
- 可以更有效地使用P/Invoke解决此问题吗?
- 我相信我读过,可以避免对内置类型的一维数组进行编组.有没有办法处理Marshal.Copy?
请注意,我们有一个工作正常的C ++/Cli版本,但是在第三方库代码中存在与本地静态相关的一些问题,这些问题会导致崩溃. Microsoft标记了此问题作为WONTFIX ,这就是为什么我要寻找替代品的原因.
Note that we have a working C++/Cli version, but there are some problems related to local statics in third-party library code that lead to crashes. Microsoft marked this issue as WONTFIX, which is why I am looking for alternatives.
推荐答案
如果将确定输出长度的代码与填充输出的代码分开是可行的,则可以:
If it were practical to separate the code that determines the output length from the code that populates the output then you could:
- 导出一个返回输出长度的函数.
- 从C#代码中调用它,然后分配输出缓冲区.
- 再次调用非托管代码,这一次要求它填充输出缓冲区.
但是我假设您已经拒绝了此选项,因为这是不切实际的.在这种情况下,您的代码是解决问题的完全合理的方法.实际上,我会说您做得很好.
But I'm assuming that you have rejected this option because it is impractical. In which case your code is a perfectly reasonable way to solve your problem. In fact I would say that you've done a very good job.
一旦解决了调用约定不匹配的问题,代码将在x86中工作相同.在C ++方面,调用约定为cdecl
,但在C#方面为stdcall
.在x64上这无关紧要,因为只有一种调用约定.但这在x86下会是个问题.
The code will work just the same in x86 once you fix the calling convention mismatch. On the C++ side the calling convention is cdecl
, but on the C# side it is stdcall
. That doesn't matter on x64 since there is only one calling convention. But it would be a problem under x86.
一些评论:
- 您不需要同时使用
[Out]
和out
.后者意味着前者. - 您可以通过分配共享堆来避免导出解除分配器.例如,在C ++端使用
CoTaskMemAlloc
,然后在C#端使用Mashal.FreeCoTaskMem
取消分配.
- You don't need to use
[Out]
as well asout
. The latter implies the former. - You can avoid exporting the deallocator by allocating off a shared heap. For instance
CoTaskMemAlloc
on the C++ side, and then deallocate withMashal.FreeCoTaskMem
on the C# side.
这篇关于在C#和C ++之间使用双精度数组数据进行P/Invoke的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!