1

I have a c++ struct as follow:

struct Vehicle
{
 u32 something;
 Info *info;
 u8 something2[ 0x14 ];
 Vector3f location;
 Template* data;
};

struct Info
{
 u32 speed;
 std::string name;
 BuffCollection** buffCollection;
 void* sectionPtr;
};

struct Template
{
 u32 templateID;
};

From this question, I figured out the meaning of the u32, u8, and so on, or so I think I did.

Then I tried to make my own C# struct out of it:

[StructLayout(LayoutKind.Sequential)]
public struct Vehicle
{
    public uint Something;
    public Info Info;
    public byte Something2;
    public Vector3f Location;
    public Template Data;
}

[StructLayout(LayoutKind.Sequential)]
public struct Info
{
    public uint Speed;
    public string Name;
    public byte[] BuffCollection;
    public IntPtr SectionPointer;
}

[StructLayout(LayoutKind.Sequential)]
public struct Template
{
    public uint TemplateId;
}

public struct Vector3f
{
    public float X, Y, Z;
    public Vector3f(float x, float y, float z)
    {
        X = x;
        Y = y;
        Z = z;
    }
}

However, when I try to read the Vehicle:

[DllImport("Core.dll")]
static extern Vehicle GetVehicle();

static void Main() 
{
    var vehicle = GetVehicle();
    Console.WriteLine(vehicle.Info.Name);
    Console.ReadKey();
}

I get the following error:

System.Runtime.InteropServices.MarshalDirectiveException: Method's type signature is not PInvoke compatible

From the search I did on it, it lead me to believe that my structure conversion is wrong.

  • What is wrong with my converted structures?
Community
  • 1
  • 1
Guapo
  • 3,446
  • 9
  • 36
  • 63
  • 1
    Is my question wrong? I see a vote to close without any information why... Is it because at the bottom I have 3 points? if so I can remove the other 2 and make them a new question. – Guapo Jun 07 '15 at 06:46
  • What's the C++ signature of the function? – Theodoros Chatzigiannakis Jun 07 '15 at 06:55
  • @TheodorosChatzigiannakis `extern "C" __declspec(dllexport) Vehicle* GetVehicle();` not sure if this is what you're looking for but that's all I know besides the structures as I don't have access to the DLL source code. – Guapo Jun 07 '15 at 06:57
  • Take note of `something2` within `Vehicle`, since it's an array. – Caramiriel Jun 07 '15 at 07:05
  • @Caramiriel so it should be a `byte[]`? – Guapo Jun 07 '15 at 07:09
  • I think so, but I'm not sure (since I don't use interop). It's just something I noticed. Edit to comment below: yes, any list/array typed c field should match a field of the same type in c# (array as well). – Caramiriel Jun 07 '15 at 07:10
  • @Caramiriel thanks I will give it a try, would a `u32 driver[29];` be any different than `uint Driver` as well? Its unrelated to the structs above but also seen on another I will soon have to work with. – Guapo Jun 07 '15 at 07:13
  • You say that the C++ function returns a `Vehicle*`, but in C# you have declared it as returning `Vehicle`. Are you sure this is what you intended? – Theodoros Chatzigiannakis Jun 07 '15 at 07:24
  • @TheodorosChatzigiannakis I am not a c++ person and honestly I don't know what the * means in fact I am reading about it now after u mentioned they are different. – Guapo Jun 07 '15 at 07:28
  • `Vehicle*` is a pointer to a `Vehicle`. You can declare the return type as `Vehicle*` in C# as well, I believe (provided that you are in an `unsafe` type and you have set your project settings to allow compilation of unsafe code - that's when C# allows you to use pointers). – Theodoros Chatzigiannakis Jun 07 '15 at 07:30
  • @TheodorosChatzigiannakis I see, I will try that thanks. About the structures are they right? – Guapo Jun 07 '15 at 07:38

1 Answers1

1

Regarding structures:

  1. Vehicle.Info is a pointer, so you need to declare it as IntPtr Info, and then use Marshal.PtrToStructure / Marshal.StructureToPtr to read/write its value in managed code;

  2. Vehicle.something2 is a byte array, not a byte, so you need to declare it this way:

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=20)]
    byte[] something2=new byte[20];

  3. Vehicle.Data - see #1, the same problem

  4. Info.Name - .NET does not provide marshalling for std::string, so you will either need to write your own marshaler (see this: Custom Marshaler for PInvoke with std::string) or change the type to something like char* in your c++ library.
  5. Info.BuffCollection should also be an IntPtr or BuffCollection[] (depending on what the BuffCollection type is about - it's not provided in your question)

Regarding the signature and invocation of GetVehicle(); method:

it is likely that the method returns the pointer to the structure, not the structure itself (just speculating, please double check). If so, you need to declare it as

static extern IntPtr GetVehicle();

and then use Marshal.PtrToStructure to convert it to your structure like this:

var vehiclePtr=GetVehicle();
var vehicle = (Vehicle)Marshal.PtrToStructure(vehiclePtr, typeof(Vehicle));
Community
  • 1
  • 1
Denis Yarkovoy
  • 1,277
  • 8
  • 16
  • Hi Denis, thanks for the detailed answer, could you post an example of how I would use Marshal.PtrToStructure on the function? – Guapo Jun 07 '15 at 08:14
  • Sure, added the example to my response – Denis Yarkovoy Jun 07 '15 at 08:18
  • Would it be possible to use `[MarshalAs(UnmanagedType.Struct)] public Info Info;` instead of `IntPtr Data`? – Guapo Jun 07 '15 at 09:17
  • I don't think so. UnmanagedType.Struct indicates that the variable contains the structure, but in your case it is a pointer to the structure (Info *info). You may try to use UnmanagedType.LPStruct, but I've never tried it. Marshal.PtrToStructure is more "conventional" way IMHO – Denis Yarkovoy Jun 07 '15 at 09:43