将非托管C ++指针转换为对象到托管C#对象

我有一个用C ++编写的非托管静态库(.dll):

// This is a personal academic project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include "program.h" struct MyData { int32_t index; char* name; //uint8_t* data; }; extern "C" { __declspec(dllexport) MyData* GetMyData() { MyData* ms = new MyData(); ms->index = 5; ms->name = "Happy string"; //ms->data = new uint8_t[5] { 4, 8, 16, 32, 64 }; return ms; } } 

‘GetMyData’方法返回指向’MyData’对象的指针。

我使用’PInvoke’将此库导入C#projeсt并调用’GetMyData’方法。

 // This is a personal academic project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com using System; using System.Runtime.InteropServices; [StructLayout(LayoutKind.Explicit)] public class MyData { [FieldOffset(0)] public Int32 index; [FieldOffset(4)] public String name; //[FieldOffset(8)] //public Byte[] data; }; class Program { [DllImport("TestCpp.dll", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr GetMyData(); public static void Main(string[] args) { // Call 'GetMyData' method and get structure from pointer using marshaling. var ptr = GetMyData(); var ms = Marshal.PtrToStructure(ptr); // Print my data into console var str = ms.index.ToString(); str += ", " + ms.name; //str += ", [" + string.Join(", ", ms.data) + "]"; Console.WriteLine(str); Console.ReadKey(); } } 

这段代码工作正常,但是如果我取消注释使用’MyData’类型的’data’成员(在C ++和C#代码中),我将在此行的C#代码中获得exception:

var ms = Marshal.PtrToStructure(ptr);

错误:System.Runtime.InteropServices.SafeArrayTypeMismatchException:
‘数组的运行时类型与元数据中记录的子类型之间发生了不匹配。

据我所知,’FieldOffset’属性中的偏移量参数 – 它是在非托管C ++对象转换为托管C#对象期间非托管内存中的字节转换。

字段’索引’有4个字节大小,因为它是32位类型。

字段’name’是char数组的指针。 对于32位架构,它也是32位数(4字节)。

我需要在“数据”字段中使用“FieldOffset”属性中的哪个偏移量?

你不能轻易做到……正如Ðannn手动或做的那样

 [FieldOffset(8)] public IntPtr _data; public byte[] GetData() { // YOU MUST FREE _data C-side! You can't use // C++ delete C#-side var bytes = new byte[5]; Marshal.Copy(_data, bytes, 0, bytes.Length); return bytes; } 

这里还有另一个(小)问题:我反对使用LayoutKind.Explicit除非你确实需要它。 从LayoutKind.Sequential开始,看看它是否足够。 使用LayoutKind.Sequential您将更愿意从32位切换到64位,因为结构将根据指针的大小进行拉伸。