All problems in computer science can be solved by another level of indirection:
public class SafeMemory : SafeBuffer
{
private GCHandle referenceHandle;
private SafeMemory[] reference;
public SafeMemory()
{
this.reference = new SafeMemory[1];
this.referenceHandle = GCHandle.Alloc(this.reference);
}
~SafeMemory()
{
this.referenceHandle.Free();
}
public void Start()
{
this.reference[0] = this;
StartUnmanagedAsyncOperation(this, this.referenceHandle);
}
static void AsyncOperationCompleted(GCHandle referenceHandle)
{
((SafeMemory[])referenceHandle.Target)[0] = null;
}
}
(SafeBuffer implementation omitted for brevity.)
The user code creates and holds a reference to a SafeMemory instance. The SafeMemory instance and a GCHandle hold a reference to an 1-element SafeMemory array. The GCHandle is released when the user code no longer holds the reference to the SafeMemory instance.
While an unmanaged asynchronous operation is in progress, the array is made to hold a reference to the SafeMemory instance. This means the GCHandle holds a reference to the array and the array holds a reference to the SafeMemory instance, which prevents the GCHandle from being released even if the user code stops holding the reference to the SafeMemory instance.