2

I am trying to create the following struct in this msdn article. I am trying to learn the whole FieldOffset but have no clue where to start.

I basically did something like this.

[StructLayout(LayoutKind.Explicit, Size=12)]
public struct DHCP_OPTION_DATA_ELEMENT {
    [FieldOffset(0)]
    public DHCP_OPTION_DATA_TYPE OptionType;
    [FieldOffset(4)]
    public byte ByteOption;
    [FieldOffset(4)]
    public uint WordOption;
    [FieldOffset(4)]
    public UInt32 DWordOption;
    [FieldOffset(4)]
    public UInt32 DWordDWordOption;
    [FieldOffset(4)]
    public uint IpAddressOption;
    [FieldOffset(4)]
    public IntPtr StringDataOption;
    [FieldOffset(4)]
    public DHCP_BINARY_DATA BinaryDataOption;
    [FieldOffset(4)]
    public DHCP_BINARY_DATA EncapsulatedDataOption;
    [FieldOffset(4)]
    public string Ipv6AddressDataOption;
}

However, it barked at me stating the following exception.

it contains an object field at offset 4 that is incorrectly aligned or 
overlapped by a non-object field.
Steven Combs
  • 1,890
  • 6
  • 29
  • 54
  • What are `DHCP_OPTION_DATA_TYPE` and `DHCP_BINARY_DATA`? – VMAtm Nov 28 '12 at 05:48
  • @VMAtm It says in the link. – Marlon Nov 28 '12 at 05:48
  • Your answers are all reasonable. I would comment that it's a lot easier to separate the union from the struct. Declare the union as a C# struct with explicit layout. Don't specify `Size`. Use `FieldOffset(0)` for all elements. Then include that union type in the overall struct. For the overall struct, use sequential layout which is the default. So you don't actually need the `StructLayout` attribute. – David Heffernan Nov 28 '12 at 09:09

3 Answers3

2

Treat it as an IntPtr, instead of a string.

However, when using an IntPtr, be darn sure you take care of cleaning up after yourself, because you will now be working with unmanaged memory and thus the GC won't be helping you out, leading to a nice memory leak each time you pass this struct around.

You're going to want to be using the Marshal.PtrToStringUni function, most likely, as suggested by shf301 in another answer.

dodexahedron
  • 4,584
  • 1
  • 25
  • 37
  • Removed my suggestion to add the additional MarshalAs tag, since that doesn't actually solve the problem in this case. The problem is due to the overlap, not the runtime guessing the type incorrectly. Using the Marshal.PtrToString* methods, you should be able to get what you need, if you make the field an IntPtr. – dodexahedron Nov 28 '12 at 07:26
  • Ok that fixed my problem there now I have another problem. – Steven Combs Nov 30 '12 at 04:34
1

The error

it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field.

Is due to overlapping a non-object (blittable) type (e.g. Uint32) with an object type (non-blittable). The marshaler cannot handle that. The marhshaler doesn't know which field of the union is valid (since it doesn't know how to decode OptionType so it doesn't know if it should marshal a string value or an integer value. Trying to marshal an integer value to a string would lead to a crash (since an integer value won't point to a valid string), so the marshaller throws the exception instead of allowing you to crash.

So you have to marshal the strings manually by defining them as IntPtr's and using Marshal.PtrToStringUni() or Marshal.PtrToStringAnsi().

You may have the same issue with DHCP_BINARY_DATA as well.

shf301
  • 31,086
  • 2
  • 52
  • 86
0

You have this code:

[FieldOffset(4)]
public string Ipv6AddressDataOption;

String is reference type (object), and other fields are value type (non-object). So you have to change the Offset for the Ipv6AddressDataOption.

VMAtm
  • 27,943
  • 17
  • 79
  • 125