1

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.

  • 2
    Look at the StructLayoutAttribute class in C# – Fratyx Oct 08 '14 at 09:54
  • Yeah, try to use `StructLayoutAttribute` with `LayoutKind.Explicit` and set `FieldOffsetAttribute` for each struct's field. – Alovchin Oct 08 '14 at 09:59
  • possible duplicate of [Bit fields in C#](http://stackoverflow.com/questions/14464/bit-fields-in-c-sharp) – Athari Oct 08 '14 at 10:05
  • Libraries like this have the usability of a hundred horsepower chainsaw without a safety switch. Getting errors like ERROR_NOACCESS is a serious problem, it doesn't have anything to do with your pinvoke declarations. You can't even get the DLL loaded, it fails in its DllMain() function. Debugging the native code is required to diagnose the cause. Pretty important for a new programmer to tackle what he knows and can support, if you've been doing only two weeks of C# then you should *not* be messing with this. Leave it on the shelf, cast an eye to C++/CLI once in a while. – Hans Passant Oct 08 '14 at 10:55
  • FWIW, NOACCESS is caused by passing an invalid pointer (e.g. NULL) to some WinDivert functions. – Basil Oct 10 '14 at 03:06
  • @Basil thanks man, I know this from your docs. Great library by the way and thanks for making it open source. I've given up on trying to map structures in this way or mess with raw pointers from C# to your library and I'm just writing a CLR lib that safely wraps the unmanaged objects. –  Oct 10 '14 at 13:47

1 Answers1

3

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.

Alovchin
  • 663
  • 3
  • 9
  • 1
    This seems needlessly complicated. Why not simply have a single `private byte hdrLengthAndVersion;`, used by both the `HdrLength` and `Version` properties? It would also avoid the need to explicitly specify the field offsets. –  Oct 08 '14 at 10:43
  • @hvd, agree. This version is just a bit more readable, I think. But your suggestion is definitely great. – Alovchin Oct 08 '14 at 10:43
  • Thanks @Alovchin, I've actually voted to close my own answer as it does seem to be a duplicate. Thanks for your answer though. –  Oct 08 '14 at 12:06