1

I need to use this Kernel32 structure: NUMA_NODE_RELATIONSHIP

But the Reserved field is declared as: BYTE Reserved[20];

To declare the structure properly, is there a better way than declaring:

public struct NUMA_NODE_RELATIONSHIP
{
    public UInt16 NodeNumber;
    public BYTE Reserved1;
    public BYTE Reserved2;
    ...
    public BYTE Reserved20;
    public GROUP_AFFINITY GroupMask;
}

or using:

[StructLayout(LayoutKind.Explicit)]
public struct NUMA_NODE_RELATIONSHIP2
{
    [FieldOffset(0)]
    public UInt16 NodeNumber;
    // [FieldOffset(2)] // **
    // public byte Reserved;
    [FieldOffset(22)] 
    public GROUP_AFFINITY GroupMask;
}

** I'm not sure my code is ok... Here FieldOffset is a byte offset or multiple of OS word size (32 or 64 bits)? Does the WIN32 API pad align struct fields and if so, how? If both are padded or both are not padded, I'm fine, otherwise my code is bugged. That is not my main question. That insert is there only to mention that my code is not necessarily working as it is written. My main question is this one:

What is the best way to declare a pinvoke struct with an array in it? I wonder if both will be fine, which one is preferred and/or if there is a better way of declaring that struct because both way I used add artifacts that make it long to write and/or harder to understand.

4
  • Not sure where the C++ is coming from. The structure definition is C, and the padding rules are WinAPI. Commented Feb 16, 2015 at 17:55
  • @MSalters, Thanks, I corrected C++ but I think it will be easier to understand OS instead of WinAPI. I agree there is a better way to say that but, for the moment, that's the best I have found. Commented Feb 16, 2015 at 17:57
  • I've edited it again to say what you really should ask ;) Commented Feb 16, 2015 at 18:01
  • The problem is that many "OS" do not say anything about it. Even the notion of an int size escapes them. Microsoft for instance doesn't care, the WINAPI talks about DWORD not int. Commented Feb 16, 2015 at 18:11

2 Answers 2

1

Declare it like this:

[StructLayout(LayoutKind.Sequential)]
public struct NUMA_NODE_RELATIONSHIP
{
    public uint NodeNumber;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
    public byte[] Reserved;
    public GROUP_AFFINITY GroupMask;
}

There's no need for explicit layout. It makes more sense to let the marshaler lay out the struct using the same rules as the C++ compiler. Especially as GROUP_AFFINITY has alignment of 4 or 8 bytes depending on the machine pointer size. Obviously the above relies on you getting GROUP_AFFINITY right.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lots. That's exactly what I was hoping to receive! You are definitely the king of the pinvoke !!! By the way... have you ever wrote the c# wrapper to GetLogicalProcessorInformationEx ?
Google doesn't seem to think I have. I've done so from unmanaged code. And got it wrong. Quite a tricky one.
Thanks! Just FYI... I'm currently using your code for GetLogicalProcessorInformation to help me out to try writing GetLogicalProcessorInformationEx ;-) !
1

The WinAPI aligns most small types at their natural size, so WORD at 16 bits and DWORD at 32 bits. BYTE, being one byte, isn't aligned. Padding is inserted where needed to achieve the proper alignment for the next member.

What makes it tricky here is the aligment of GROUP_AFFINITY. I think it's 2 bytes because all its members are WORD.

The second reason I think that it's 2 byte aligned is because Microsoft tends to name its padding (!). If there was 2 bytes of unnamed padding after Reserved, Microsoft would have made it BYTE reserved[22] instead.

And yes, .Net field offsets are in bytes too.

4 Comments

Thanks. Very useful information. My main question is not this one. It's about the proper way to declare the struct in C#. I wonder if both will be fine, which one is preferred and/or if there is a better way of declaring that struct because both way I used add artifacts that make it long to write and/or harder to understand.
You can't say FieldOffset[22] in C, which is why the WinAPI uses those Reserved bytes. There's no need to carry that limitation or that field name to C#.
Yes you are right. I agree. But I think it is not necessarily bad to let it there just to make it clear that we thought about it. A quick comparison with the documentation could accelerate the reading. But some could argue that it clutter code... I remove it.
@Msalters the alignment of GROUP_AFFINITY is either 4 or 8 depending on machine pointer size.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.