简体   繁体   English

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

[英]Convert unmanaged C++ pointer to an object to a managed C# object

I have an unmanaged static library (.dll) written on 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;
    }
}

The 'GetMyData' method returns pointer to 'MyData' object. 'GetMyData'方法返回指向'MyData'对象的指针。

I imported this library into C# projeсt using 'PInvoke' and called the 'GetMyData' method. 我使用'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();
    }
}

This code works fine, but if I uncomment the using of 'data' member of 'MyData' type (in C++ and C# code), I will get exception in C# code on this line: 这段代码工作正常,但是如果我取消注释使用'MyData'类型的'data'成员(在C ++和C#代码中),我将在此行的C#代码中获得异常:

var ms = Marshal.PtrToStructure(ptr); var ms = Marshal.PtrToStructure(ptr);

Error: System.Runtime.InteropServices.SafeArrayTypeMismatchException: 错误:System.Runtime.InteropServices.SafeArrayTypeMismatchException:
'Mismatch has occurred between the runtime type of the array and the sub type recorded in the metadata.' '数组的运行时类型与元数据中记录的子类型之间发生了不匹配。

As I understand the offset argument in the 'FieldOffset' attribute - it's a shift in bytes in unmanaged memory during convert unmanaged C++ object to managed C# object. 据我所知,'FieldOffset'属性中的偏移量参数 - 在非托管C ++对象转换为托管C#对象期间,它是非托管内存中的字节变化。

Field 'index' has 4 bytes size because it's 32-bit type. 字段'索引'有4个字节大小,因为它是32位类型。

Field 'name' is pointer to char array. 字段'name'是char数组的指针。 For 32-bit architecture it's also 32-bit number (4 bytes). 对于32位架构,它也是32位数(4字节)。

Which offset in 'FieldOffset' attribute i need to use for 'data' field? 我需要在“数据”字段中使用“FieldOffset”属性中的哪个偏移量?

You can't do it easily... As suggested by Ðаn do it manually or 你不能轻易做到......正如Ð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;
}

There is another (little) problem here: I'm against using LayoutKind.Explicit unless you really need it. 这里还有另一个(小)问题:我反对使用LayoutKind.Explicit除非你确实需要它。 Start with LayoutKind.Sequential and see if it is enough. LayoutKind.Sequential开始,看看它是否足够。 Using LayoutKind.Sequential you'll be more ready to switch from 32 to 64 bits, because the struct will be stretched for the size of the pointers. 使用LayoutKind.Sequential您将更准备从32位切换到64位,因为结构将根据指针的大小进行拉伸。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM