很多人不知道这一点(这就是为什么你得到这么多答案说你不知道的原因),但是 .NET 中内置了一些东西来做类似的事情:SafeHandle。
事实上,其派生类之一的 .NET 2.0 页面有一个使用AllocHGlobal
. 当SafeUnmanagedMemoryHandle
调用 的终结器时,它会自动为您调用FreeHGlobal
。(如果您想要确定性清理而不是仅仅等待终结器解决它,您将需要调用Close()
或Dispose()
显式)。
您只需对代码进行一些更改:
internal static SafeUnmanagedMemoryHandle StructToPtr(object obj)
{
var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
Marshal.StructureToPtr(obj, ptr, false);
return new SafeUnmanagedMemoryHandle(ptr, true);
}
[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_RenderCopy")]
internal static extern int RenderCopy(IntPtr renderer, IntPtr texture, SafeUnmanagedMemoryHandle srcrect, SafeUnmanagedMemoryHandle dstrect);
一旦你这样做了,你的原始Copy
示例将完全按照你的预期工作。
public int Copy(Texture texture, Rect srcrect, Rect dstrect)
{
return SDL.RenderCopy(_ptr, texture._ptr, Util.StructToPtr(srcrect), Util.StructToPtr(dstrect));
}
当两个指针超出范围并最终确定时,它们将在之后被清理。我不知道如何_ptr
使用或者是否Texture
是您控制的类,但这些也可能被切换到SafeHandle
s 。
更新:如果您想了解有关如何正确处理非托管资源的更多信息(并获得IDisposable
比 MSDN 给出的示例更好的实现模式的示例),我强烈推荐文章“ IDisposable:您的母亲从未说过的事情关于资源重新分配的问题”,Stephen Cleary着。他在文章中深入探讨了如何正确编写自己的 SafeHandles。
附录
这是示例的副本,以防链接失效:
using System;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace SafeHandleExamples
{
class Example
{
public static void Main()
{
IntPtr ptr = Marshal.AllocHGlobal(10);
Console.WriteLine("Ten bytes of unmanaged memory allocated.");
SafeUnmanagedMemoryHandle memHandle = new SafeUnmanagedMemoryHandle(ptr, true);
if (memHandle.IsInvalid)
{
Console.WriteLine("SafeUnmanagedMemoryHandle is invalid!.");
}
else
{
Console.WriteLine("SafeUnmanagedMemoryHandle class initialized to unmanaged memory.");
}
Console.ReadLine();
}
}
// Demand unmanaged code permission to use this class.
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
sealed class SafeUnmanagedMemoryHandle : SafeHandleZeroOrMinusOneIsInvalid
{
// Set ownsHandle to true for the default constructor.
internal SafeUnmanagedMemoryHandle() : base(true) { }
// Set the handle and set ownsHandle to true.
internal SafeUnmanagedMemoryHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(preexistingHandle);
}
// Perform any specific actions to release the
// handle in the ReleaseHandle method.
// Often, you need to use Pinvoke to make
// a call into the Win32 API to release the
// handle. In this case, however, we can use
// the Marshal class to release the unmanaged
// memory.
override protected bool ReleaseHandle()
{
// "handle" is the internal
// value for the IntPtr handle.
// If the handle was set,
// free it. Return success.
if (handle != IntPtr.Zero)
{
// Free the handle.
Marshal.FreeHGlobal(handle);
// Set the handle to zero.
handle = IntPtr.Zero;
// Return success.
return true;
}
// Return false.
return false;
}
}
}