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?
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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With