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);

两者结合可实现托管 ↔ 非托管的双向数据传递。


重要注意事项

  1. 必须手动管理内存StructureToPtr 不会自动分配内存,你需要用 Marshal.AllocHGlobal 或 Marshal.AllocCoTaskMem 预先分配,并在使用完后用 FreeHGlobal / FreeCoTaskMem 释放,否则内存泄漏。

  2. fDeleteOld 的语义

    • true:方法会先调用 Marshal.DestroyStructure 清理 ptr 指向的旧内容(适用于该内存之前存储过其他托管结构),然后再复制新数据。

    • false:直接覆盖内存,不清理旧内容。大多数简单场景用 false 即可。

  3. 要求 [StructLayout] 特性:被封送的结构体必须标记 [StructLayout(LayoutKind.Sequential)] 或 LayoutKind.Explicit,否则内存布局不确定,C++ 无法正确读取。

  4. 引用类型成员:如果结构体包含字符串或数组等引用类型,StructureToPtr 会进行默认的封送处理(如将 string 转为 char*),但涉及复杂嵌套时推荐使用 MarshalAs 特性或手动处理。

  5. 性能:频繁调用小结构体 StructureToPtr 通常可接受,但大量大尺寸数据反复封送会有开销,可考虑使用 Span<byte> 或固定缓冲区优化。


一句话总结

Marshal.StructureToPtr 是 .NET 提供给开发者的“手动挡”工具,让你精确控制托管数据进入非托管内存的过程,是实现 P/Invoke 和手动内存交互的基础 API。

posted @ 2026-05-29 15:46  家煜宝宝  阅读(17)  评论(0)    收藏  举报