从托管代码调用本机函数

时间:2014-04-08 07:07:36

标签: c# c++ pinvoke wrapper

我正在尝试从托管代码调用本机函数,以便读取我作为数组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.

3 个答案:

答案 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
);

非常感谢。