编组 SIZE_T* 的正确方法?
Posted
技术标签:
【中文标题】编组 SIZE_T* 的正确方法?【英文标题】:Correct way to marshal SIZE_T*? 【发布时间】:2010-11-21 12:35:04 【问题描述】:我有以下 C++ 函数定义,我试图通过 PInvoke 从托管代码中调用它:
bool FooBar(SIZE_T* arg1);
我的托管声明如下所示:
[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern bool FooBar(ref uint arg1);
你们中的一些人可能会注意到我最终遇到的相同错误。这不是 64 位可移植的。 SIZE_T 的大小可变(32-64 位),指向它的指针也是如此。在托管大小上,指针正确转换为 64 位,但 uint 没有,您最终可能会在 arg1 的高位中出现垃圾。这是一个特别持久的错误,因为垃圾通常只是零:(
我得到的唯一解决方案是以下托管声明:
[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern bool FooBar(ref IntPtr arg1);
这当然可行,因为 IntPtr 可以正确地改变它的大小。在我的代码中,我只是将 IntPtr 视为一个整数,并且它可以工作,尽管它看起来像一个丑陋的黑客。在我看来,应该有某种方法来正确指定这一点,也许使用 UnmanagedType.SysUInt,但我无法提出任何其他可行的解决方案。
【问题讨论】:
【参考方案1】:使用IntPtr
和/或UIntPtr
是 正确地使用 - 专门为此目的而存在的类型!我不明白你为什么认为它是“丑陋的黑客”。我也不确定您提出的替代方案是什么 - 允许将值映射到 uint
的任何类型的属性本质上都是错误的,因为无论架构如何,C# uint
都保证为 32 位,并且所以在 64 位平台上,要正确编组它,它必须修剪一半,丢失数据,并可能使结果无用。
【讨论】:
我认为它是一个丑陋的黑客,因为 IntPtr 应该代表一个指向某物的指针。但是,我只是将其直接视为整数。 SysUInt 特别声明它是 uint 的可变大小表示。正是我想要的,除了我不知道如何将它正确编组为 SysUInt 指针,而不是 SysUInt。 您正在使用指向 SIZE_T 的指针作为参数,Soonil。 IntPtr 看起来很合适 你会注意到我没有使用 IntPtr,但是我使用的是 ref IntPtr。 @Soonil,没有什么可以说IntPtr
“应该代表指向某物的指针”。 MSDN 是这样描述它的:“IntPtr 类型被设计为一个整数,其大小是特定于平台的。也就是说,这种类型的实例在 32 位硬件和操作系统上应该是 32 位,而 64 位在 64 位硬件和操作系统上”。因此,它只是一个与平台字长匹配的整数类型。 size_t
和 ptrdiff_t
也是如此(通常,无论如何),所以映射是完全正常的。
附带说明,在 MSIL/CLR 级别(这是您可以获得的最低级别)上,int
被称为int32
,而IntPtr
被称为native int
。如果是 BCL 中类型的“Ptr”前缀让您感到困惑,那么您不应将其视为“指针的整数”。相反,它的意思是“与指针大小相同的整数”。【参考方案2】:
UIntPtr 是要使用的正确类型。
size_t 是一个无符号的指针大小的整数,这正是 UIntPtr 的含义。我同意,名称中的“ptr”可能有点令人困惑。它实际上并不意味着“这是一个指针”,它的意思是“这是一个 pointer-sized 整数”。所以你的声明是:
[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern bool FooBar(ref UIntPtr arg1);
【讨论】:
嗯,很好 :) 不会是第一个命名错误的 API,呵呵。 实际上,这是这种类型的通用名称。 Win32 API 具有类型定义INT_PTR
和 UINT_PTR
。 ISO C99 在<stddef.h>
中有intptr_t
和uintptr_t
。因此,虽然它可能是一个用词不当,但现在它是一个传统的。以上是关于编组 SIZE_T* 的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章