我正在尝试从托管代码调用本机函数,以便读取我作为数组sbyte参数传递的返回值。 但是,我看不懂任何东西。数组sbyte始终相同,并且永远不会被本机函数修改。
以下是C ++函数签名:
void __cdecl NativeFunction(int8_t StatusFlagOut[], int32_t *len);
另一个是我的C#Wrapper:
[DllImport("NativeDll.dll")]
internal static extern void NativeFunction([Out] sbyte[] statusFlagOut, ref int len);
最后,这是我用来调用函数的方式:
sbyte[] output = null;
int len = 0;
NativeFunction(output, ref len); // while len is being filled properly, output never being changed.
答案 0 :(得分:2)
该函数是cdecl,因此您需要在p / invoke中指定:
[DllImport("NativeDll.dll", CallingConvention=CallingConvention.Cdecl)]
internal static extern void NativeFunction(
[Out] sbyte[] statusFlagOut,
ref int len
);
最大的问题是你没有为本地函数分配内存来填充。您似乎期望本机代码分配内存。这显然不是它的设计方式,这是正确的。你总是希望调用者分配内存有很多原因,我不会去这里。
看起来本机函数可以帮助您分配多少。所以你似乎可以编写这样的代码:
int len = 0;
NativeFunction(null, ref len);
sbyte[] output = new sbyte[len];
NativeFunction(output, ref len);
最后,更一般的观点。这是一个互操作问题。它们总是需要完全了解二进制互操作接口的两面。您没有提供C ++方面的完整详细信息。您提供了函数签名但未提供该函数的语义。所以,我做了一个有根据的猜测。包含这些细节非常重要,以防我们的猜测技能失败!
答案 1 :(得分:0)
使用cdecl
调用约定调用本机函数时,应明确指定它:
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
如果你不这样做,CLR默认会把它解释为stdcall
,并期望被叫方清理堆栈,在cdecl
的情况下,调用者会这样做,会发生坏事。
正如评论中所提到的,arrays
在使用平台调用时非常特殊,因为它们是按值传递的。在[Out]
页面上的MSDN上描述了以下内容:
例如,按值传递的数组,作为In-only参数封送 默认情况下,可以更改为Out-only。但是,行为没有 当类型包含all-blittable时,总是提供预期的语义 元素或字段,因为interop marshaler使用固定。如果你 不关心将数据传递给被调用者,Out-only marshaling 可以为非blittable类型提供更好的性能。结合 InAttribute和OutAttribute在应用时特别有用 数组和格式化,非blittable类型。 来电者看到了变化a 只有在应用这两个属性时,被调用者才会使用这些类型。从那以后 这些类型需要在编组期间进行复制,您可以使用InAttribute 和OutAttribute减少不必要的副本。
这意味着您的最终签名应如下所示:
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void
NativeFunction([In, Out] sbyte[] statusFlagOut, ref int len);
答案 2 :(得分:0)
我已经解决了。你是对的,本机功能有一个错误。
但是我还必须像你提出的那样添加调用约定规范,所以最后签名包装将如下所示:
[DllImport("NativeDll.dll", CallingConvention=CallingConvention.Cdecl)]
internal static extern void NativeFunction(
[Out] sbyte[] statusFlagOut,
ref int len
);
非常感谢。