I have a matrix class which contains a fixed size float array that I want to expose directly in the C# wrapper class:
class Matrix
{
public:
float data[16];
...
};
Normally, when wrapping this structure in C# I would define the wrapped type as a POD struct that would utilize [StructLayout(LayoutKind.Sequential)] and would allow me to directly marshall the class to and from C++ / C#. However, the c++ class exposes other types (via members or functions) which can't be marshalled in this way so I instead have to use a PIMPL mechanism where my C# class contains an opaque pointer (IntPtr) to the underlying C++ structure. However, I would still like to directly expose the underlying data member of the c++ structure to the C# object. By directly, I mean that modifications to the elements of data in C# will be directly mirrored in the underlying C++ structure (i.e. the C# member can not be a copy of the C++ member). My idea was to expose the data member in C# like this:
public class Matrix
{
protected HandleRef cptr; // opaque reference to equivalent C++ instance
public float [] data
{
get
{
return Get_data(cptr);
}
}
[DllImport(MY_CPP_DLL, EntryPoint = "CSharp_Get_data", CharSet = CHARSET)]
[return: MarshalAs(UnmanagedType.LPArray, SizeConst = 16, ArraySubType = UnmanagedType.R4)]
private static extern float [] Get_Data(HandleRef in_object);
};
Where my C++ dll contains the function CSharp_Get_data which looks like this:
DLLEXPORT float * STDCALL CSharp_Get_data(Matrix *obj)
{
return obj->data;
}
This compiles fine in both C++ and C#, but when I try and do something like this in c#:
Matrix asdf = new Matrix();
asdf.data[0] = 2.0f;
I receive a System.Runtime.InteropServices.MarshalDirectiveException which states Additional information: Cannot marshal 'return value': Invalid managed/unmanaged type combination.
Is there a way I can use this approach but not encounter this error? I know this is a pretty odd use case but I feel as if the approach does not violate any of the marshalling standards described by MS here: https://learn.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-arrays.
Thanks for any help.
datamember in C# will be different than the C++ object (i.e. still a copy, so if a user modifiesdata[0]in the C# object it will not be reflected in the C++ object'sdata[0]element).float*in the pinvoke declaration. Theunsafekeyword is required, it is.