`MEMORY_BASIC_INFORMATION` is not a valid portable definition
nike4613 opened this issue · comments
The MEMORY_BASIC_INFORMATION
definition is not portable between 32- and 64-bit. The PartitionId
field is only present in Win64, and causes the layout to be incorrect on 32-bit, where that field does not exist, and there is no padding there.
The C definition:
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
#if defined (_WIN64)
WORD PartitionId;
#endif
SIZE_T RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
The *32
and *64
variants also shed light on this problem:
typedef struct _MEMORY_BASIC_INFORMATION32 {
DWORD BaseAddress;
DWORD AllocationBase;
DWORD AllocationProtect;
DWORD RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION32, *PMEMORY_BASIC_INFORMATION32;
typedef struct DECLSPEC_ALIGN(16) _MEMORY_BASIC_INFORMATION64 {
ULONGLONG BaseAddress;
ULONGLONG AllocationBase;
DWORD AllocationProtect;
DWORD __alignment1;
ULONGLONG RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
DWORD __alignment2;
} MEMORY_BASIC_INFORMATION64, *PMEMORY_BASIC_INFORMATION64;
Notice how neither of these variants have PartitionId
, though MEMORY_BASIC_INFORMATION64
has an explicit alignment field in its place. For 32-bit targets, MEMORY_BASIC_INFORMATION
should be equivalent to MEMORY_BASIC_INFORMATION32
, and on 64-bit targets, it should be equivalent to MEMORY_BASIC_INFORMATION64
. The current definition is not.
A correct definition would be this:
public unsafe partial struct MEMORY_BASIC_INFORMATION
{
[NativeTypeName("PVOID")]
public void* BaseAddress;
[NativeTypeName("PVOID")]
public void* AllocationBase;
[NativeTypeName("DWORD")]
public uint AllocationProtect;
// The automatic padding to align RegionSize fills all the space PartitionId would take up,
// but ensures that this definition is fully portable.
[NativeTypeName("SIZE_T")]
public nuint RegionSize;
[NativeTypeName("DWORD")]
public uint State;
[NativeTypeName("DWORD")]
public uint Protect;
[NativeTypeName("DWORD")]
public uint Type;
}
This is one of the few types where it is impossible to define something valid in C# without building per architecture.
The closest thing I could do here is to provide nested structs users must use per platform instead. I can't provide say MEMORY_BASIC_INFORMATION32
and MEMORY_BASIC_INFORMATION64
because the SDK defines those themselves and they're used for a different purpose
Does TerraFX.Interop.Windows still support 32-bit?
If not then it might be worth adding a static constructor like so,
public unsafe partial struct MEMORY_BASIC_INFORMATION
{
static MEMORY_BASIC_INFORMATION()
{
if (sizeof(IntPtr) == 4) throw new PlatformNotSupportedException("Only 64-bit is supported 🥲");
}
}
Does TerraFX.Interop.Windows still support 32-bit?
It doesn't "not support" 32-bit.
For the vast majority of types exposed they will seamlessly work regardless of target platform. There are a small subset of types, such as MEMORY_BASIC_INFORMATION
, which do differ in definition between 32-bit and 64-bit such that problems may creep in. I've tried to handle most of the cases I was aware of, but apparently missed this one in particular