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

Iva*_*nko 5 .net c# c++ pinvoke marshalling

我有一个用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

'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<MyData>(ptr);

        // Print my data into console
        var str = ms.index.ToString();
        str += ", " + ms.name;
        //str += ", [" + string.Join(", ", ms.data) + "]";
        Console.WriteLine(str);
        Console.ReadKey();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

var ms = Marshal.PtrToStructure(ptr);

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

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

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

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

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

xan*_*tos 3

你不能轻易做到这一点...按照 \xc3\x90\xd0\xb0n 的建议手动执行或

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

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

\n