Let's say I have the following structs in C++
struct Base
{
USHORT size;
}
struct Inherited : public Base
{
BYTE type;
}
I want to marshal Inherited
in C# but the struct inheritance does not work in C#. Is doing the following appropriate ?
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 structs are way bigger making it difficult to validate the results. Also, the structs are coming from another software that is not so well document making it even harder to validate the results. When using inheritance in C++, are the base class fields before or after the child struct ?
I'm using the IBase
as a way to enforce the base fields to be present.
Unfortunately, I don't have control over the C++ side (SDK for an external system I integrate with).
The word "appropriate" does not exactly apply to these C# declarations. By far the best way to avoid accidents is by not relying on implementation details of properties and interfaces. This struct should be declared internal and just use plain fields.
The snippet does not demonstrate a failure mode so I'll have to assume that it is a simplified version of the real declaration that does have a problem. The way to check that C# code gets the structure declaration right 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 little test program to check 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 inspect the len and ofs variables, 4 and 2 in this case. Do the exact same thing 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 a perfect match and pinvoke should be good. When you get a mismatch on the real declaration, work your way backwards on the ofs
variable, you'll discover the member that was declared wrong. Do note the consequence of using the property, forcing to check on wonky name of the backing field. This code will be much less convoluted when the struct is declared by using fields instead of properties, strongly recommended.
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