Thanks to AI, you can really be productive in dealing with unions in C.
CM_PARTIAL_RESOURCE_DESCRIPTOR (wdm.h) - Windows drivers | Microsoft Learn
using System;
using System.Runtime.InteropServices;
//
// Windows types for clarity
//
using PHYSICAL_ADDRESS = System.Int64; // LARGE_INTEGER
using KAFFINITY = System.UIntPtr; // ULONG_PTR
namespace NativeInterop
{
/// <summary>
/// C# equivalent of CM_PARTIAL_RESOURCE_DESCRIPTOR (winnt.h).
/// </summary>
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct CM_PARTIAL_RESOURCE_DESCRIPTOR
{
// ---- common header -------------------------------------------------
[FieldOffset(0)] public byte Type;
[FieldOffset(1)] public byte ShareDisposition;
[FieldOffset(2)] public ushort Flags;
// ---- union: one of these is valid, chosen by <Type> ---------------
[FieldOffset(4)] public GENERIC_RESOURCE Generic;
[FieldOffset(4)] public PORT_RESOURCE Port;
[FieldOffset(4)] public MEMORY_RESOURCE Memory;
[FieldOffset(4)] public MEMORY40_RESOURCE Memory40;
[FieldOffset(4)] public MEMORY48_RESOURCE Memory48;
[FieldOffset(4)] public MEMORY64_RESOURCE Memory64;
[FieldOffset(4)] public INTERRUPT_RESOURCE Interrupt;
[FieldOffset(4)] public MESSAGE_INTERRUPT_RESOURCE MessageInterrupt;
[FieldOffset(4)] public DMA_RESOURCE Dma;
[FieldOffset(4)] public DMAV3_RESOURCE DmaV3;
[FieldOffset(4)] public DEVICE_PRIVATE_RESOURCE DevicePrivate;
[FieldOffset(4)] public BUSNUMBER_RESOURCE BusNumber;
[FieldOffset(4)] public DEVICESPECIFIC_RESOURCE DeviceSpecificData;
[FieldOffset(4)] public CONNECTION_RESOURCE Connection;
}
#region simple (start,length) pairs
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct GENERIC_RESOURCE { public PHYSICAL_ADDRESS Start; public uint Length; }
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PORT_RESOURCE { public PHYSICAL_ADDRESS Start; public uint Length; }
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MEMORY_RESOURCE { public PHYSICAL_ADDRESS Start; public uint Length; }
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MEMORY40_RESOURCE { public PHYSICAL_ADDRESS Start; public uint Length40; }
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MEMORY48_RESOURCE { public PHYSICAL_ADDRESS Start; public uint Length48; }
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MEMORY64_RESOURCE { public PHYSICAL_ADDRESS Start; public uint Length64; }
#endregion
#region interrupt resources
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct INTERRUPT_RESOURCE
{
public uint Level; // ULONG for both x86/x64 builds
public uint Vector;
public KAFFINITY Affinity;
}
// Message‑signalled interrupt has an inner union (Raw vs Translated)
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct MESSAGE_INTERRUPT_RESOURCE
{
[FieldOffset(0)] public MESSAGE_INTERRUPT_RAW Raw;
[FieldOffset(0)] public MESSAGE_INTERRUPT_TRANSLATED Translated;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MESSAGE_INTERRUPT_RAW
{
public ushort Group;
public ushort Reserved;
public ushort MessageCount;
public uint Vector;
public KAFFINITY Affinity;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MESSAGE_INTERRUPT_TRANSLATED
{
public uint Level;
public uint Vector;
public KAFFINITY Affinity;
}
#endregion
#region DMA
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DMA_RESOURCE
{
public uint Channel;
public uint Port;
public uint Reserved1;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DMAV3_RESOURCE
{
public uint Channel;
public uint RequestLine;
public byte TransferWidth;
public byte Reserved1;
public byte Reserved2;
public byte Reserved3;
}
#endregion
#region miscellaneous
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DEVICE_PRIVATE_RESOURCE
{
public uint Data0;
public uint Data1;
public uint Data2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BUSNUMBER_RESOURCE
{
public uint Start;
public uint Length;
public uint Reserved;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DEVICESPECIFIC_RESOURCE
{
public uint DataSize;
public uint Reserved1;
public uint Reserved2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CONNECTION_RESOURCE
{
public byte Class;
public byte Type;
public byte Reserved1;
public byte Reserved2;
public uint IdLowPart;
public uint IdHighPart;
}
#endregion
}
Declare the two structs in the union as C# structs in the usual way. Then declare a type for the union, using an explicit layout.
[StructLayout(LayoutKind.Explicit)]
public struct _WAITCHAIN_NODE_INFO_UNION
{
[FieldOffset(0)]
_WAITCHAIN_NODE_INFO_LOCK_OBJECT LockObject;
[FieldOffset(0)]
_WAITCHAIN_NODE_INFO_THREAD_OBJECT ThreadObject;
}
Then add the union to your struct:
[StructLayout(LayoutKind.Sequential)]
public struct WAITCHAIN_NODE_INFO
{
public WCT_OBJECT_TYPE ObjectType;
public WCT_OBJECT_STATUS ObjectStatus;
public _WAITCHAIN_NODE_INFO_UNION Union;
}
When you overlay objects like this, extra requirements are placed on the types involved. You cannot overlay a type containing a string or an array for instance. So the character array will have to be implemented as a value type, for instance a fixed array. This is inconvenient to operate with but MS did not define the types with C# in mind.