user562566
user562566

Reputation:

Translating unmanaged struct to C#

First off, I'm brand new (as in 1-2 weeks in) to C#. I'm trying to wrap WinDivert methods for use inside C# and I've been having mixed success and failure. Though I'm not sure, I believe I've narrowed down the issue to a misalignment (literally) between how certain structs are defined in the unmanaged code and how they are defined in C#, due to (as far as I know) a limitation of C#.

And example from https://github.com/basil00/Divert/blob/master/include/windivert.h

typedef struct
{
    UINT8  HdrLength:4;
    UINT8  Version:4;
    UINT8  TOS;
    UINT16 Length;
    UINT16 Id;
    UINT16 FragOff0;
    UINT8  TTL;
    UINT8  Protocol;
    UINT16 Checksum;
    UINT32 SrcAddr;
    UINT32 DstAddr;
} WINDIVERT_IPHDR, *PWINDIVERT_IPHDR;

This is one of the structure I'm having an issue translating. As you can see, HdrLength and Version are defined to occupy 4 bits of the structure each. In the C# version, I've tried simply declaring byte HdrLengthAndVersion, I've tried changing layout to explicity and manually defining the position of the members so that they overlap in cases like this to ensure the same memory length and position, etc. This gets exceptionally complicated with some of the other, larger structs.

I'm curious if there is any way to properly translate this to C#? I'm also curious if this is even possible, or if it will be an issue on different architectures. I know that we're getting in to memory alignment, padding etc here which I'll admit is currently a topic I'm less than an expert on, which is why I'm here :).

As a second option, I'm considering just doing some void pointers (since the managed stuff allocates and works with these objects) and just shifting position then type casting the pointer back to the specific values I actually need to access. But again, not sure if this possible.

Also just for the sake of mentioning, I've tried SWIG. Didn't work, I get 998 ERROR_NOACCESS errors when trying to use it so, just a huge heap of classes I didn't write with more problems.

Upvotes: 1

Views: 291

Answers (1)

Alovchin
Alovchin

Reputation: 683

Since FieldOffsetAttribute accepts offset in bytes, you can set HdrLength's and Version's offset to 0 and add helper properties that will return the correct values for each of the fields using bit shift.

For example:

[StructLayout(LayoutKind.Explicit)]
struct WINDIVERT_IPHDR
{
    [FieldOffset(0)]
    private byte hdrLength;

    [FieldOffset(0)]
    private byte version;

    [FieldOffset(1)]
    private byte tos;
    ...

    public byte HdrLength
    {
        get { return (byte)(hdrLength & 0xF); }
    }

    public byte Version
    {
        get { return (byte)(version >> 4); }
    }

    public byte TOS { get { return tos; } }
    ...
}

Note that since you are using LayoutKind.Explicit, you'll have to define FieldOffset for all struct's fields.

Upvotes: 3

Related Questions