.NET Framework4.8 PInvoke调用返回结构体C函数提示类型签名不兼容
问题解答
对应代码片段
C 端 DLL 代码
const int SIZE = 5; typedef struct Foo{ uint8_t data[SIZE]; } Foo; DLL_EXPORT Foo bar(){ Foo f; //fill f return f; }
C# 端互操作定义
struct Foo{ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] byte[] data; } [DllImport(...)] static extern Foo bar();
不同版本.NET运行表现差异原因
- .NET Framework 4.8的P/Invoke编组器存在原生限制:不支持非blittable值类型作为P/Invoke的返回值。你定义的
Foo结构体中包含标记了[MarshalAs]特性的数组成员,不属于可直接内存拷贝的blittable类型,因此会抛出Method's type signature is not PInvoke compatible异常。 - .NET Core 3.0及后续版本(包括.NET 5.0+)对P/Invoke编组器做了大量功能扩展,新增了非blittable结构体作为返回值的编组支持,因此这段代码可以在.NET 5.0环境下正常运行。
内存安全风险说明
不存在访问已释放内存的风险,原因如下:
- C端
bar函数采用值返回的方式返回结构体,C语言规范中,值返回结构体时会将整个结构体的完整内容拷贝到调用方的栈上下文空间,不会返回指向函数栈内存的指针,函数返回后栈回收不会影响已经完成拷贝的结构体数据。 - C#侧P/Invoke编组过程中,会将C端返回的结构体数据完整拷贝到托管内存中的
Foo实例,其中的data数组成员会被编组为独立的托管数组,和C DLL的原生内存空间完全独立,后续访问不会涉及到原生侧已经释放的栈内存。
内容的提问来源于stack exchange,提问作者elo
相关产品推荐
相关产品推荐

