P/Invoke 内存资源处理方案
在使用 P/Invoke 调用本地代码时,正确处理内存资源至关重要,以避免内存泄漏和资源浪费。以下是几种常见的处理方案:
1. 基本内存处理
分配与释放
csharp
[DllImport("kernel32.dll")]
static extern IntPtr HeapAlloc(IntPtr hHeap, uint dwFlags, UIntPtr dwBytes);[DllImport("kernel32.dll")]
static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem);// 使用示例
IntPtr ptr = HeapAlloc(IntPtr.Zero, 0, (UIntPtr)100);
// 使用内存...
HeapFree(IntPtr.Zero, 0, ptr);
使用 Marshal 类
csharp
// 分配内存
IntPtr ptr = Marshal.AllocHGlobal(100);try
{// 使用内存...
}
finally
{// 释放内存Marshal.FreeHGlobal(ptr);
}
2. 自动释放方案
使用 SafeHandle
csharp
public class SafeMemoryHandle : SafeHandleZeroOrMinusOneIsInvalid
{[DllImport("kernel32.dll")]private static extern bool CloseHandle(IntPtr handle);public SafeMemoryHandle() : base(true) { }protected override bool ReleaseHandle(){return CloseHandle(handle);}
}// 使用
using (var handle = new SafeMemoryHandle())
{// 自动释放
}
使用 IDisposable 模式
csharp
public class NativeResourceWrapper : IDisposable
{private IntPtr _nativeResource;public NativeResourceWrapper(){_nativeResource = Marshal.AllocHGlobal(100);}public void Dispose(){Dispose(true);GC.SuppressFinalize(this);}protected virtual void Dispose(bool disposing){if (_nativeResource != IntPtr.Zero){Marshal.FreeHGlobal(_nativeResource);_nativeResource = IntPtr.Zero;}}~NativeResourceWrapper(){Dispose(false);}
}
3. 复杂类型处理
结构体处理
csharp
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{public int Value;public float Number;
}// 分配结构体内存
MyStruct myStruct = new MyStruct();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyStruct)));try
{Marshal.StructureToPtr(myStruct, ptr, false);// 使用...
}
finally
{Marshal.DestroyStructure(ptr, typeof(MyStruct));Marshal.FreeHGlobal(ptr);
}
字符串处理
csharp
[DllImport("mylib.dll", CharSet = CharSet.Auto)]
static extern void SomeFunction(string value);// 或者手动处理
[DllImport("mylib.dll")]
static extern void SomeFunction(IntPtr value);// 使用
string str = "Hello";
IntPtr ptr = Marshal.StringToHGlobalAnsi(str);
try
{SomeFunction(ptr);
}
finally
{Marshal.FreeHGlobal(ptr);
}
4. 最佳实践
-
始终释放分配的内存 - 使用
try/finally或using语句确保资源释放 -
优先使用 SafeHandle - 提供更安全的资源管理方式
-
注意编码转换 - 字符串处理时注意 ANSI/Unicode 转换
-
考虑线程安全 - 如果资源可能被多线程访问,需要同步处理
-
记录资源所有权 - 明确哪些代码负责分配和释放资源
通过遵循这些方案,可以有效地管理 P/Invoke 调用中的内存资源,避免常见的内存泄漏问题。
