【问题标题】:Is there a definitive guide to cross platform (x86 and x64) PInvoke and windows data types?是否有跨平台(x86 和 x64)PInvoke 和 windows 数据类型的权威指南?
【发布时间】:2012-03-07 11:33:45
【问题描述】:

我正在验证 x64 兼容性的一些代码。以前我使用过 PInvoke.net,但我发现了一些关于 x64 的可疑声明。所以现在,我:

  1. 查找API参考,例如MapViewOfFile
  2. 查找windows data type 定义
  3. 找到对应的 .NET 类型。

这是第 3 步,我想要一个明确的参考

举个例子:

LPVOID WINAPI MapViewOfFile(
  __in  HANDLE hFileMappingObject,
  __in  DWORD dwDesiredAccess,
  __in  DWORD dwFileOffsetHigh,
  __in  DWORD dwFileOffsetLow,
  __in  SIZE_T dwNumberOfBytesToMap
);

返回值为LPVOID,定义为:

LPVOID

指向任何类型的指针。

该类型在 WinDef.h 中声明如下:

typedef void *LPVOID;

好的...所以我猜那是IntPtrUIntPtr。这个article 有一个表并建议LPVOID 应该映射到IntPtr 或UIntPtr。好的。

接下来,处理。

手柄

对象的句柄。

此类型在 WinNT.h 中声明如下:

typedef PVOID 句柄;

好的,HANDLE 是一个 PVOID。

PVOID

指向任何类型的指针。

此类型在 WinNT.h 中声明如下:

typedef void *PVOID;

嗯,听起来像IntPtr

接下来,双字

DWORD

一个 32 位无符号整数。范围是 0 到 4294967295(十进制)。

该类型在 WinDef.h 中声明如下:

typedef unsigned long DWORD;

好的,无符号长 0 到 4294967295,所以这是一个 uinthere 它建议 Int32 或 UInt32。 Int32 将无法存储超过 2,147,483,648 的任何值。所以那张桌子很可疑。

最后,我们有 SIZE_T,它被定义为 ULONG_PTR,它可以是 32 位或 64 位有符号长度,具体取决于平台(定义如下)。这个article(和follow up)得出的结论是你应该使用IntPtr,因为它将处理可变大小。

SIZE_T

指针可以指向的最大字节数。用于一个 计数必须跨越指针的整个范围。

此类型在 BaseTsd.h 中声明如下:

typedef ULONG_PTR SIZE_T;

ULONG_PTR

一个未签名的 LONG_PTR。

此类型在 BaseTsd.h 中声明如下:

#if defined(_WIN64)
 typedef unsigned __int64 ULONG_PTR;
#else
 typedef unsigned long ULONG_PTR;
#endif

一个 32 位有符号整数。范围是 –2147483648 到 2147483647 十进制。

此类型在 WinNT.h 中声明如下:

typedef long LONG;

INT64

一个 64 位有符号整数。范围是 –9223372036854775808 到 9223372036854775807 十进制。

此类型在 BaseTsd.h 中声明如下:

typedef signed __int64 INT64;

因此,虽然我可以查找每个 windows 数据类型的定义,然后根据大小、符号以及它是否适用于 x86 和 x64 来找到对应的 .NET 数据类型,但这并不理想。

是否有明确的参考(不是 pinvoke.net),其中包含适用于 x64 的最新映射表?

【问题讨论】:

  • 我知道您以 MapViewOfFile 为例,但仅供参考,从 .NET 4.0 开始,您可以使用 MemoryMappedFile
  • 谢谢 Shaun - 我确实看到了 .NET 4.0 中的 MemoryMappedFile 实现,但在这种情况下,我没有控制客户端环境的奢侈,不幸的是 4.0 不在卡片上。跨度>
  • 别担心,我在OpenCover 上使用它在.net 和本机c++ 之间共享内存,它使我的代码变得如此简单。

标签: c# c++ winapi 64-bit pinvoke


【解决方案1】:

将本机数据类型映射到托管类型时,重要的是大小和一致性。

有符号和无符号类型的选择仅在解释托管值时很重要。
它们都被编组为原始位。

在大多数情况下,您只是将值从一种 API 方法传递给另一种;在这些情况下,类型是有符号还是无符号并不重要,只要大小合适即可。

因此,一般规则是任何指针大小的值都变为IntPtr,而DWORDQWORD 分别变为U?Int32U?Int64

【讨论】:

  • 假设我调用了一些组合函数 GetSystemMemory(__out DWORD totalMemory),它返回 0xFFFFFFFF。如果我采用该值并将其简单地传递给另一个 Win32 函数,则 Int32 或 UInt32 不会有任何区别,因为原始位仍将是 0xFFFFFFFF。但是,如果我尝试在 CLR 中解释该值,则它会作为 UInt32 而不是 Int32 有意义。因此,我会说 DWORD 应该始终被编组为 UInt32 - 该值可以在两种上下文中进行编组和解释并且具有相同的含义?我想避免“一般规则”并科学地做......
  • @StevenP:这取决于原生类型的含义。听起来它应该在Int32 中,值为-1
  • 感谢您的回答。也许 0xFFFFFFFF 是一个令人困惑的例子。我的意思是 0xFFFFFFFF 作为 4GB 的有效值。因此,如果函数返回 4GB 并且我使用 Int32,则在托管土地上将毫无意义,但在 win32 中会。那么,如果 UInt32 能够编组值并表示其含义,我为什么要将它编组为 Int32 而不是 UInt32 呢?在这种情况下,4GB。
  • @StevenP:如果该值应为 4GB,请使用 UInt32。如果应该是-1,请使用Int32。这取决于本机函数的语义。
  • 另外,如果在32位机器上是32位,在64位机器上是64位(如ptrdiff_t),则必须使用IntPtr。
猜你喜欢
  • 2012-02-02
  • 2011-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
  • 2010-10-08
  • 1970-01-01
相关资源
最近更新 更多