Ivan Kishchenko Ivan Kishchenko -4 years ago 175
C# Question

Convert unmanaged C++ pointer to an object to a managed C# object

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;
}
}


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

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


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);

Error:
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.

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

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?

Answer Source

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;
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download