C # marshaling Inheriting C ++ Structure

Let's say I have the following structures in C ++

struct Base { USHORT size; } struct Inherited : public Base { BYTE type; } 

I want to marshal Inherited in C #, but structure inheritance does not work in C #. Does the following:

 public interface IBase { ushort Size { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct Inherited : IBase { public ushort Size { get; set; } public byte Type { get; set; } } 

I simplified the problem here, and my structures are much larger, which makes it difficult to confirm the results. In addition, the structures come from other software that doesn’t document well, which makes it even harder to verify the results. When using inheritance in C ++, are the fields of the base class before or after the child structure?

I use IBase to ensure the availability of base fields.

Unfortunately, I have no control over the C ++ side (SDK for an external system with which I integrate).

+5
source share
3 answers

The word "appropriate" does not refer specifically to these C # declarations. By far, the best way to avoid accidents is to not rely on implementation details of properties and interfaces. This structure should be declared internal and just use simple fields.

The snippet does not show a failure mode, so I have to assume that this is a simplified version of a real declaration that has a problem. A way to verify that C # code gains the right to declare a structure is to verify that the size of the structure and the offset of the last field are the same in both C ++ and C #. Start by writing a small test program to verify that the C ++ version for this snippet should look like this:

 #include <Windows.h> #include <stddef.h> struct Base { USHORT size; }; struct Inherited : public Base { BYTE type; }; int main() { int len = sizeof(Inherited); int ofs = offsetof(Inherited, type); return 0; } 

And use the debugger to check the variables len and ofs, 4 and 2 in this case. Do the same in C #:

 using System; using System.Runtime.InteropServices; class Program { static void Main(string[] args) { var len = Marshal.SizeOf(typeof(Inherited)); var ofs = Marshal.OffsetOf(typeof(Inherited), "<Type>k__BackingField"); } } public interface IBase { ushort Size { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct Inherited : IBase { public ushort Size { get; set; } public byte Type { get; set; } } 

Still 4 and 2, so perfect match and pinvoke should be good. When you get a mismatch with the real declaration, navigate your way back in the ofs variable, you will find the member that was declared invalid. Pay attention to the consequences of using the property, forcing to check the bellicose name of the support field. Real code would be much less confusing if you declare a construct using fields instead of properties.

+2
source

You make assumptions about how the C ++ compiler will output the class in memory. Depending on your compiler and which flags you use, this may change. Also depending on the fields you use. For example, it might be wise for some compilers to align the object as follows:

 struct Obj { char c; // <-- starts at 0 byte int i; // <-- starts at 4 byte - 4 byte alignment improves performance. } 

So, you can see how C ++ classes may not compare with how you expect them to be laid out in C #.

There are flags to control this - you can set the packaging in C ++ to 0, then use a sequential layout in C #, and then your approach is reasonable.

Your use of properties is not a serious problem - if you understand how and more importantly, where the implicit support field will be created for you by the compiler.

+1
source

I finally found the real problem in what I was trying to do. The callback that I use from the SDK that I use sends me a struct Base and, by analyzing the fields inside, I determine which inherited type it is. Then I need to β€œgive” the base type to the inherited type. Here is what I originally did:

 static T CopyStruct<T>(ref object s1) { GCHandle handle = GCHandle.Alloc(s1, GCHandleType.Pinned); T typedStruct = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); handle.Free(); return typedStruct; } 

This way to do this will never go beyond the size of a struct Base . Therefore, all additional fields of the Inherited type will not be correctly initialized (reading outside the copied memory). I ended up doing it unsafe, as follows:

 fixed (Base* basePtr= &base) { inherited= *(Inherited*) basePtr; } 

Thus, Inherited points to the original block of memory and can read outside the base size.

Thanks for all your previous answers! I actually created a C ++ application to check the sizes and offsets of all the C ++ models that I had.

0
source

All Articles