3

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:

  1. Look up the API reference such as MapViewOfFile
  2. Look up the windows data type definition
  3. 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;
#endif

LONG

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?

Steven P
  • 1,956
  • 1
  • 16
  • 17
  • I understand you have used [MapViewOfFile](http://msdn.microsoft.com/en-us/library/windows/desktop/aa366761.aspx) as an example but FYI since .NET 4.0 you can use the [MemoryMappedFile](http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx) class – Shaun Wilde Feb 16 '12 at 04:59
  • Thanks Shaun - I did see the MemoryMappedFile implementations in .NET 4.0, but in this case, I don't have the luxury of controlling the client environment and unfortunately 4.0 is not on the cards. – Steven P Feb 16 '12 at 11:18
  • no worries, I use it on [OpenCover](http://github.com/sawilde/OpenCover) to share memory between .net and native c++, it has made my code so much simpler. – Shaun Wilde Feb 16 '12 at 20:18

1 Answers1

4

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, and DWORD and QWORD become U?Int32 and U?Int64, respectively.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Let's say I call some made up function GetSystemMemory(__out DWORD totalMemory) and it returns 0xFFFFFFFF. If I take that value and simply pass it to another Win32 function, it won't make any difference being Int32 or UInt32, as the raw bits will still be 0xFFFFFFFF. HOWEVER, if I tried to interpret that value in the CLR, it WILL make sense as UInt32 but not as Int32. Therefore, I would say DWORD should always be marshalled as UInt32 - the value can both be marshalled and interpreted in both contexts and have the same meaning? I want to avoid 'general rules' and do it scientifically... – Steven P Feb 16 '12 at 03:49
  • @StevenP: It depends what meaning the native type had intended. That sounds like it should be in `Int32` with a value of `-1`. – SLaks Feb 16 '12 at 04:03
  • Thanks for your answer. Perhaps 0xFFFFFFFF was a confusing example. I meant 0xFFFFFFFF as a valid value to be 4GB. So, if the function returned 4GB and I use Int32, it will make no sense in managed land, but it will in win32. So why would I ever marshal it as Int32 instead of UInt32 if UInt32 will be able to both marshal the value and represent its meaning? In this case, 4GB. – Steven P Feb 16 '12 at 04:17
  • @StevenP: If the value should be 4GB, use `UInt32`. If it should be `-1`, use `Int32`. It depends on the semantics of the native function. – SLaks Feb 16 '12 at 04:18
  • I have had one problem, on windows the size of long is 4 bytes, whereas on other platforms (linux, etc) the size is 4 bytes on x86 and 8 bytes on x64. This can be a problem if a native library you are using has a function definition that takes a long, and is cross platform. If you assume it's an int, then it will fail on x64 linux. If you assume it's an IntPtr, then it will fail on x64 windows. – Programmdude Apr 08 '14 at 16:16
  • Also, if it's 32 bit on 32 bit machines and 64 bit on 64 bit machines (such as ptrdiff_t), you have to use IntPtr. – Joshua Feb 16 '12 at 04:51
  • On more sane platforms than windows, that's called int. – Joshua Feb 16 '12 at 16:46