14

ヘルパーメソッドを書きましたが、

internal static IntPtr StructToPtr(object obj)
{
    var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
    Marshal.StructureToPtr(obj, ptr, false);
    return ptr;
}

これは を受け取り、structを返しますIntPtr。私はそれを次のように使用します:

public int Copy(Texture texture, Rect srcrect, Rect dstrect)
{
    return SDL.RenderCopy(_ptr, texture._ptr, Util.StructToPtr(srcrect), Util.StructToPtr(dstrect));
}

問題は、それをIntPtrC DLL に渡すことができるように、ほんの一瞬だけ必要であるということです。

[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_RenderCopy")]
internal static extern int RenderCopy(IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect);

解放について心配する必要はありません。そうしないと、1 行の関数が 6 に増えます。

public int Copy(Texture texture, Rect? srcrect=null, Rect? dstrect=null)
{
    var srcptr = Util.StructToPtr(srcrect);
    var dstptr = Util.StructToPtr(dstrect);
    var result = SDL.RenderCopy(_ptr, texture._ptr, srcptr, dstptr);
    Marshal.FreeHGlobal(srcptr);
    Marshal.FreeHGlobal(dstptr);
    return result;
}

これを行うより良い方法はありますか?C# は、割り当てられたメモリを最終的にクリーンアップしますか?

そうでない場合、代わりSDL.RenderCopyにいくつかのusingステートメントで呼び出しをラップして、この一時変数 + 明示的な解放ナンセンスをすべて実行する必要がないようにする方法はありますか?

4

4 に答える 4

20

多くの人はこれを知りません (それが、できないという回答が非常に多い理由です) 。

実際、その派生クラスの 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;
        }
    }
}
于 2013-07-10T05:51:34.977 に答える
1

はい、解放する必要があります。6 行のプログラムとしてそれを取得した方法は非常に効率的です。ガベージコレクターの外に出るときに行うトレードオフです。

于 2013-07-10T04:15:01.053 に答える
0

残念ながら、これを行うための組み込みの自動方法はありません。を呼び出す場合はAllocHGlobal、明示的に解放する必要FreeHGlobalがあります (大規模なメモリ リークの可能性がある場合を除きます)。

このメモリは、Marshal.FreeHGlobalメソッドを使用して解放する必要があります。

私が過去に行ったことは、ポインターを呼び出すラッパークラスでAllocHGlobal割り当てをラップすることです。このようにして、私はそれらをステートメントに入れることができました。IDisposableDispose()FreeHGlobalusing

于 2013-07-10T04:15:34.173 に答える