多くの人はこれを知りません (それが、できないという回答が非常に多い理由です) 。
実際、その派生クラスの 1 つの.NET 2.0 ページには、.NETを使用した例があり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));
}
2 つのポインターが範囲外になり、ファイナライズされると、後でクリーンアップされます。_ptr
がどのように使用されるのか、またはあなたが制御するクラスであるかどうかはわかりませんTexture
が、それらも s に切り替えられる可能性がありSafeHandle
ます。
更新:管理されていないリソースを適切に処理する方法について詳しく知りたい場合 (およびIDisposable
、MSDN が提供する例よりも優れた実装方法のより良いパターンの例を取得したい場合) は、記事「IDisposable: What Your Mother Never Told 」を強くお勧めします。リソースの割り当て解除について」、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;
}
}
}