Marshal.StructureToPtr
Marshal.StructureToPtr 是什么?
Marshal.StructureToPtr 是 .NET 中 System.Runtime.InteropServices.Marshal 类的一个静态方法。它的作用是将托管内存中的值类型(如 struct 或某些 class)的数据复制到非托管内存块中,以便与 C/C++ 等非托管代码进行交互。
简单说:它把你 C# 中的结构体“写入”一块由指针指向的原始内存。
方法签名
public static void StructureToPtr(object? structure, IntPtr ptr, bool fDeleteOld);
参数说明
| 参数 | 含义 |
|---|---|
structure |
要封送的托管对象(必须是值类型或固定布局的引用类型) |
ptr |
指向非托管内存的指针(必须已分配足够大小) |
fDeleteOld |
是否在复制前自动释放 ptr 指向的原有内容(如果该内存之前存有其他托管结构) |
典型使用场景
1. 将 C# 结构体传给 C++ DLL 函数
假设 C++ 有一个函数接收一个结构体指针:
// C++ DLL struct Point { int x; int y; }; void SetPoint(Point* pt);
C# 中这样调用:
[StructLayout(LayoutKind.Sequential)] struct Point { public int x; public int y; } [DllImport("MyDll.dll")] static extern void SetPoint(IntPtr pt); // 使用 Point myPoint = new Point { x = 10, y = 20 }; IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<Point>()); Marshal.StructureToPtr(myPoint, ptr, false); SetPoint(ptr); Marshal.FreeHGlobal(ptr); // 别忘了释放
2. 手动构建非托管数组
需要把一个结构体数组传给 C++ 时,可以循环调用 StructureToPtr 写入连续内存。
配对使用:Marshal.PtrToStructure
StructureToPtr 的逆操作是将非托管内存中的数据读回托管结构体:
Point newPoint = Marshal.PtrToStructure<Point>(ptr);
两者结合可实现托管 ↔ 非托管的双向数据传递。
重要注意事项
-
必须手动管理内存:
StructureToPtr不会自动分配内存,你需要用Marshal.AllocHGlobal或Marshal.AllocCoTaskMem预先分配,并在使用完后用FreeHGlobal/FreeCoTaskMem释放,否则内存泄漏。 -
fDeleteOld的语义:-
true:方法会先调用Marshal.DestroyStructure清理ptr指向的旧内容(适用于该内存之前存储过其他托管结构),然后再复制新数据。 -
false:直接覆盖内存,不清理旧内容。大多数简单场景用false即可。
-
-
要求
[StructLayout]特性:被封送的结构体必须标记[StructLayout(LayoutKind.Sequential)]或LayoutKind.Explicit,否则内存布局不确定,C++ 无法正确读取。 -
引用类型成员:如果结构体包含字符串或数组等引用类型,
StructureToPtr会进行默认的封送处理(如将string转为char*),但涉及复杂嵌套时推荐使用MarshalAs特性或手动处理。 -
性能:频繁调用小结构体
StructureToPtr通常可接受,但大量大尺寸数据反复封送会有开销,可考虑使用Span<byte>或固定缓冲区优化。
一句话总结
Marshal.StructureToPtr 是 .NET 提供给开发者的“手动挡”工具,让你精确控制托管数据进入非托管内存的过程,是实现 P/Invoke 和手动内存交互的基础 API。

浙公网安备 33010602011771号