0

SafeHandle gives a very strong guarantee that the resource it manages will eventually be released, even if an asynchronous exception occurs. Even using and try finally cannot guarantee that.

I am thinking to use this guarantee to free a pinned GCHandle, however, there seems to be no ready to use implementation of the GCHandle managed by SafeHandle in C#.

So I created my own implementation that looks like this:

public class ArraySafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    private GCHandle _handle;

    public ArraySafeHandle(byte[] array) : base(true)
    {
        _handle = GCHandle.Alloc(array, GCHandleType.Pinned);
        SetHandle(_handle.AddrOfPinnedObject());
    }

    protected override bool ReleaseHandle()
    {
        _handle.Free();
        return true;
    }
}

This works, however, I am concerned that nobody is using GCHandle in this way. Also, setting the IntPtr in a SafeHandle manually does not seem to be a common way to create the SafeHandle (see, for example, this remark in the official docs).

What is the correct implementation? Is it even a working solution to wrap GCHandle in SafeHandle or it can fail at any time?

yaskovdev
  • 1,213
  • 1
  • 12
  • 22
  • 1
    "`SafeHandle` gives a very strong guarantee that the resource it manages will eventually be released, even if an asynchronous exception occurs. Even `using` and `try finally` cannot guarantee that." That's a completely false statement from beginning to end. `SafeHandle` is not any more safe than any other finalizable object, except where PInvoke is concerned (because the PInvoke marshaller holds the reference). You *must* use either `using` or `finally` to correctly release `SafeHandle`. So no, wrapping a `GCHandle` in a `SafeHandle` does not do anything except if you are using PInvoke. – Charlieface Sep 11 '22 at 02:49
  • 1
    I guess the real question is why you are using `GCHandle` in the first place, it's not normally necessary, except in some complex marshalling situations when high performance is required, because PInvoke pins blittable arrays anyway. – Charlieface Sep 11 '22 at 02:52
  • Thank you, @Charlieface! I have a function in a 3rd party C++ library that takes 2 pointers to 2 not necessary contiguous memory regions and fills them with data. The problem is that in my case the memory regions should be contiguous (so that I could work with result using only 1 array). It seems like the only way in C# to fill 1 array using the function that takes 2 arrays is [to use `IntPtr`s](https://github.com/yaskovdev/sandbox/blob/master/SplitSafeHandleQuestion/SplitSafeHandleQuestion/Program.cs#L26-L28). So I have to "convert" managed `byte[]` into `IntPtr`, here the `GCHandle` appears. – yaskovdev Sep 11 '22 at 11:38
  • Hmm, it seems like even if there was a good way to wrap `GCHandle` in `SafeHandle`, that wouldn't solve my original problem: apparently it is impossible to "split" `SafeHandle` into two... – yaskovdev Sep 11 '22 at 11:40

0 Answers0