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 调用中的内存资源,避免常见的内存泄漏问题。