我已经阅读了有关P / Invoke here和here的C ++ Interop上的各种MSDN页面,但我仍感到困惑。
我有一些大型的双打数组,我需要进入本机代码,以及一些需要返回的结果数组。我事先并不知道输出数组的大小。为简单起见,我将在示例中仅使用单个数组。该平台是x64;我读到在32位和64位环境之间编组内部是完全不同的,所以这可能很重要。
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 ++端写入数组的双打。但是,我想知道:
请注意,我们有一个可用的C ++ / Cli版本,但是在第三方库代码中存在与导致崩溃的本地静态相关的一些问题。 Microsoft marked this issue as WONTFIX,这就是为什么我在寻找其他选择。
答案 0 :(得分:5)
没关系。完全没有返回错误代码的方法非常糟糕,当数组很大并且程序内存不足时会受到伤害。你得到的严重崩溃是非常不可知的。
复制阵列并明确释放它们的需要当然不会赢得任何奖品。您可以通过让调用者将指针传递给自己的数组来解决这个问题,然后编写元素。但是,您需要一个协议让调用者确定数组需要多大的数量,这需要调用该方法两次。第一个调用返回所需的大小,第二个调用完成工作。
样板示例如下:
[DllImport("foo.dll")]
private static int ReturnData(double[] data, ref int dataLength);
示例用法:
int len = 0;
double[] data = null;
int err = ReturnData(data, ref len);
if (err == ERROR_MORE_DATA) { // NOTE: expected
data = new double[len];
err = ReturnData(data, len);
}
无需复制,无需释放内存,好事。如果本地代码没有注意传递的 len ,那么本机代码可能会损坏GC堆,这不是一件好事。但当然容易避免。
答案 1 :(得分:3)
如果将确定输出长度的代码与填充输出的代码分开是可行的,那么您可以:
但我假设您已拒绝此选项,因为它不切实际。在这种情况下,您的代码是解决问题的完美合理方式。事实上,我会说你做得很好。
修复调用约定不匹配后,代码在x86中的工作方式将相同。在C ++方面,调用约定是cdecl
,但在C#方面它是stdcall
。这与x64无关,因为只有一个调用约定。但是在x86下这将是一个问题。
一些意见:
[Out]
以及out
。后者意味着前者。CoTaskMemAlloc
,然后在C#端释放Mashal.FreeCoTaskMem
。答案 2 :(得分:1)
如果你事先知道了数组大小,你可以编写一个C ++ / CLI DLL,它将托管数组作为参数,将其固定,并在它获得的固定指针上调用本机C ++ DLL。
但如果只是输出,我没有看到没有副本的任何版本。您可以使用SAFEARRAY,因此P / Invoke会复制而不是您,但这就是全部。