将对象的非托管C ++指针转换为托管C#对象 [英] Convert unmanaged C++ pointer to an object to a managed C# object

查看:82
本文介绍了将对象的非托管C ++指针转换为托管C#对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an unmanaged static library (.dll) written on C++:

// 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"对象的指针.

The 'GetMyData' method returns pointer to 'MyData' object.

我使用"PInvoke"将此库导入到C#项目中,并称为"GetMyData"方法.

I imported this library into C# projeсt using 'PInvoke' and called the 'GetMyData' method.

// 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();
    }
}

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

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:

var ms = Marshal.PtrToStructure(ptr);

var ms = Marshal.PtrToStructure(ptr);

错误: System.Runtime.InteropServices.SafeArrayTypeMismatchException:
'阵列的执行阶段类型与 记录在元数据中的子类型.

Error: System.Runtime.InteropServices.SafeArrayTypeMismatchException:
'Mismatch has occurred between the runtime type of the array and the sub type recorded in the metadata.'

据我了解,"FieldOffset"属性中的offset参数是将非托管C ++对象转换为托管C#对象期间非托管内存中字节的移位.

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.

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

Field 'index' has 4 bytes size because it's 32-bit type.

字段名称"是指向char数组的指针.对于32位架构,它也是32位数字(4个字节).

Field 'name' is pointer to char array. For 32-bit architecture it's also 32-bit number (4 bytes).

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

Which offset in 'FieldOffset' attribute i need to use for 'data' field?

推荐答案

您不能轻易做到...正如Ðаn手动或

You can't do it easily... As suggested by Ðаn do it manually or

[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位,因为该结构将针对指针的大小进行扩展.

There is another (little) problem here: I'm against using LayoutKind.Explicit unless you really need it. Start with LayoutKind.Sequential and see if it is enough. 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.

这篇关于将对象的非托管C ++指针转换为托管C#对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆