https://docs.microsoft.com/zh-cn/dotnet/standard/native-interop/best-practices
Windows PVOID,这是一个 C void*,可以作为 IntPtr 或 UIntPtr 进行封送,但在可能的情况下更倾向于 void*。
结构
托管结构是在堆栈上创建的,在返回方法之前不会将其删除。 按照定义,它们是“固定的”(不会被 GC 移动)。 如果本机代码不会在当前方法末尾之外使用指针,则也可以使用不安全代码块中的地址。
Blittable 结构的性能更好,因为它们可以由封送层直接使用。 尝试使结构为 blittable(例如,避免 bool)。 有关详细信息,请参阅 Blittable 类型部分。
如果结构为 blittable,请使用 sizeof() 而不是 Marshal.SizeOf<MyStruct>(),以获得更好的性能。 如上所述,可以通过尝试创建固定的 GCHandle 来验证该类型是否为 blittable。 如果该类型不是字符串或被视为 blittable,则 GCHandle.Alloc 将引发 ArgumentException。
指向定义中的结构的指针必须通过 ref 传递或使用 unsafe 和 *。
✔️ 请尽可能将托管结构与官方平台文档或标题中使用的形状和名称匹配。
✔️ 请务必使用 C# sizeof() 而不是 blittable 结构的 Marshal.SizeOf<MyStruct>(),以提高性能。
INT_PTR Reserved1[2] 等数组必须封送到两个 IntPtr 字段(Reserved1a 和 Reserved1b)。当本机数组为基元类型时,可以使用 fixed 关键字更明确地进行编写。 例如,SYSTEM_PROCESS_INFORMATION 在本机标头中类似如下内容:
C复制
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
BYTE Reserved1[48];
UNICODE_STRING ImageName;
...
} SYSTEM_PROCESS_INFORMATION
可以在 C# 中编写如下内容:
C#复制
internal unsafe struct SYSTEM_PROCESS_INFORMATION
{
internal uint NextEntryOffset;
internal uint NumberOfThreads;
private fixed byte Reserved1[48];
internal Interop.UNICODE_STRING ImageName;
...
}
但是,固定的缓冲区有一些问题。 非 blittable 类型的固定缓冲区不会正确封送,因此,就地数组需要扩大到多个单独字段。 此外,在早于 3.0 的 .NET Framework 和 .NET Core 中,如果包含固定缓冲区字段的结构嵌套在非 blittable 结构中,则不会将固定缓冲区字段正确封送到本机代码。