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

I have an unmanaged static library (.dll) written in 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 a pointer to a MyData object.

I imported this library into C # projct 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 use of the 'data' member of type MyData (in C ++ and C #), I will get an exception in the C # code on this line:

var ms = Marshal.PtrToStructure (ptr);

Error: System.Runtime.InteropServices.SafeArrayTypeMismatchException:
'A mismatch occurred between the runtime type of the array and the subtype recorded in the metadata.'

As I understand it, the offset argument in the FieldOffset attribute is the shift in bytes in unmanaged memory during the conversion of an unmanaged C ++ object to a C # managed object.

The "index" field is 4 bytes in size because it is a 32-bit type.

The 'name' field is a pointer to a char array. For 32-bit architecture, this is also a 32-bit number (4 bytes).

What offset in the 'FieldOffset' attribute do I need to use for the 'data' field?

+5
source share
1 answer

You cannot do it easily ... As expected, 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; } 

There is another (small) problem here: I am against using LayoutKind.Explicit if you really don't need it. Start with LayoutKind.Sequential and see if that is enough. Using LayoutKind.Sequential , you will be more ready to switch from 32 to 64 bits, because the structure will be stretched for the size of the pointers.

+3
source

Source: https://habr.com/ru/post/1265825/


All Articles