I am verifying some code for x64 compatability. Previously I’ve used PInvoke.net, but I’ve found a few suspect declarations in terms of x64. So now, I:
- Look up the API reference such as MapViewOfFile
- Look up the windows data type definition
- Find the corresponding .NET type.
It’s step 3 where I’d like a definitive reference
As an example:
LPVOID WINAPI MapViewOfFile(
__in HANDLE hFileMappingObject,
__in DWORD dwDesiredAccess,
__in DWORD dwFileOffsetHigh,
__in DWORD dwFileOffsetLow,
__in SIZE_T dwNumberOfBytesToMap
);
Return value is LPVOID, which is defined as:
LPVOID
A pointer to any type.
This type is declared in WinDef.h as follows:
typedef void *LPVOID;
OK… so I guess that’s IntPtr or UIntPtr. This article has a table and suggests LPVOID should map to IntPtr or UIntPtr. OK.
Next, HANDLE.
HANDLE
A handle to an object.
This type is declared in WinNT.h as follows:
typedef PVOID HANDLE;
OK, HANDLE is a PVOID.
PVOID
A pointer to any type.
This type is declared in WinNT.h as follows:
typedef void *PVOID;
Hmmmm, sounds like IntPtr
Next, DWORD
DWORD
A 32-bit unsigned integer. The range is 0 through 4294967295 decimal.
This type is declared in WinDef.h as follows:
typedef unsigned long DWORD;
OK, unsigned long 0 to 4294967295, so that’s a uint and yet here it suggests Int32 or UInt32. Int32 won’t be able to store any value over 2,147,483,648. So that table is very suspect.
Finally, we have SIZE_T, which is defined as a ULONG_PTR which can be 32 or 64 bit signed long depending on the platform (definitions below). This article (and follow up) conclude you should use IntPtr, since it will handle the variable sizes.
SIZE_T
The maximum number of bytes to which a pointer can point. Use for a
count that must span the full range of a pointer.This type is declared in BaseTsd.h as follows:
typedef ULONG_PTR SIZE_T;ULONG_PTR
An unsigned LONG_PTR.
This type is declared in BaseTsd.h as follows:
#if defined(_WIN64) typedef unsigned __int64 ULONG_PTR; #else typedef unsigned long ULONG_PTR; #endifLONG
A 32-bit signed integer. The range is –2147483648 through 2147483647
decimal.This type is declared in WinNT.h as follows:
typedef long LONG;INT64
A 64-bit signed integer. The range is –9223372036854775808 through
9223372036854775807 decimal.This type is declared in BaseTsd.h as follows:
typedef signed __int64 INT64;
So, while I can look up the definition of every windows data type and then find a corresponding .NET datatype in terms of size, sign, and whether it works on both x86 and x64, it’s not ideal.
Is there a definitive reference out there (not pinvoke.net) with a good mapping table that’s up to date for x64?
When mapping native datatypes to managed types, all that matters is size and consistency.
The choice of signed vs. unsigned types only matters when interpreting the managed value.
They’re both marshaled as raw bits.
In most cases, you will just be passing values from one API method to another; in these cases, it doesn’t matter whether the type is signed or unsigned, as long as it’s the right size.
Therefore, the general rule is that any pointer-sized value becomes
IntPtr, andDWORDandQWORDbecomeU?Int32andU?Int64, respectively.