If I allocated some memory using AllocHGlobal, do I need to free it using FreeHGlobal?

I wrote a helper method,

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

Which takes a struct and returns me IntPtr . I use it as such:

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

The problem is that I only need IntPtr in a split second so that I can pass it to the C DLL,

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

I really do not want to worry about his release; otherwise my 1 line function grows to 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; } 

Is there a better way to do this? Will C # be able to clear any memory it allocated?

If not, is there a way that I can bind the SDL.RenderCopy call in some using statements instead, so that I don't have to execute all this temporary variable + explicitly release the non-meaning?

+7
c # interop
source share
4 answers

Yes, C # will not automatically free memory allocated by Marshal.AllocHGlobal . This memory must be freed when Marshal.FreeHGlobal called, otherwise it will leak.

You can create something smart to wrap IntPtr

 class StructWrapper : IDisposable { public IntPtr Ptr { get; private set; } public StructWrapper(object obj) { if (Ptr != null) { Ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj)); Marshal.StructureToPtr(obj, Ptr, false); } else { Ptr = IntPtr.Zero; } } ~StructWrapper() { if (Ptr != IntPtr.Zero) { Marshal.FreeHGlobal(Ptr); Ptr = IntPtr.Zero; } } public void Dispose() { Marshal.FreeHGlobal(Ptr); Ptr = IntPtr.Zero; GC.SuppressFinalize(this); } public static implicit operator IntPtr(StructWrapper w) { return w.Ptr; } } 

Using this shell, you can manually free memory by wrapping the object in a using statement or by allowing it to be freed when the finalizer starts.

+20
source share

Many people do not know this (and why you have so many answers that you cannot), but for .NET there is something that can be done for such things: SafeHandle .

In fact, the .NET 2.0 page for one of its derived classes has an example using AllocHGlobal . When the SafeUnmanagedMemoryHandle finalizer is SafeUnmanagedMemoryHandle , it automatically calls FreeHGlobal for you. (If you want deterministic cleanup instead of just waiting for the finalizer to finish, you need to either explicitly call Close() or Dispose() ).

It just requires a few code changes on your part:

 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); 

Once you do this, your original Copy example will work exactly as you expected.

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

When two pointers go out of scope and exit, they will be cleared after that. I don't know how _ptr used, or if Texture is a class that you control, but they can also be switched to SafeHandle .


UPDATE:. If you want to learn more about how to handle unmanaged resources (and get an example of a better IDisposable implementation template better than the example provided by MSDN) I highly recommend the article " IDisposable: What Your Mother Never Told You About Releasing Resources " by Stephen Cleary . He has an in-depth understanding of how to write his own SafeHandles in an article.


application

Here is a copy of the example if the link is ever dead:

 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; } } } 
+14
source share

Yes, you need to free it, and the way you got it, since your 6-line program is pretty efficient. You make his compromise when you go beyond the garbage collector.

+1
source share

Unfortunately, there is no built-in automatic way to do this. If you call AllocHGlobal , you must explicitly free it with FreeHGlobal (if you are not able to with potentially massive memory leaks).

This memory must be released using the Marshal.FreeHGlobal method.

What I did in the past completes the allocation of AllocHGlobal in the IDisposable wrapper class, where Dispose() calls FreeHGlobal in the pointer. That way, I could put them in a using statement.

0
source share

All Articles