When Configuration.EnableReleaseOnFinalize is set to true, SharpDX.ComObject's finalizer releases its reference to the native object (and I imagine other SharpGen users might want to do something similar). Currently, it's possible for the finalizer to run and decrement the ref count to zero before the native method runs and acquires its own ref:
// Some managed method in my C# code
public void FillGeo(MyGeometryType geometry, D2D.Brush d2dbrush)
{
D2D.Geometry d2dgeometry = geometry.CreateD2DGeometry();
this.d2drendertarget.FillGeometry(d2dgeometry, d2dbrush);
}
// generated FillGeometry interop method in SharpDX (comments mine)
public unsafe void DrawGeometry(SharpDX.Direct2D1.Geometry geometry, SharpDX.Direct2D1.Brush brush,
System.Single strokeWidth, SharpDX.Direct2D1.StrokeStyle strokeStyle)
{
System.IntPtr geometry_ = System.IntPtr.Zero;
System.IntPtr brush_ = System.IntPtr.Zero;
System.IntPtr strokeStyle_ = System.IntPtr.Zero;
geometry_ = SharpDX.CppObject.ToCallbackPtr<SharpDX.Direct2D1.Geometry>(geometry);
// At this point, “geometry” is eligible for finalization, its last use was in the above line.
// If the finalizer runs now, it will decrement the native Geometry object's ref count to zero
brush_ = SharpDX.CppObject.ToCallbackPtr<SharpDX.Direct2D1.Brush>(brush);
strokeStyle_ = SharpDX.CppObject.ToCallbackPtr<SharpDX.Direct2D1.StrokeStyle>(strokeStyle);
// The native method called here will try to dereference "geometry_" but the native object it
// points at might have been destroyed (if the managed object's finalizer already ran)
SharpDX.Direct2D1.LocalInterop.CalliStdCallvoid(this._nativePointer, (void *)geometry_,
(void *)brush_, strokeWidth, (void *)strokeStyle_, (*(void ***)this._nativePointer)[22]);
}
This case can be trivially resolved with a using block in my code, but it seems like the point of Configuration.EnableReleaseOnFinalizer is that explicitly disposing is not required. I'm working around this issue with using blocks and GC.KeepAlive for the cases I've found in my codebase, but resolving this more generally would be great.
Could SharpGen generate GC.KeepAlive calls to delay finalization for objects whose native pointer is passed into a native method call? i.e. the generated method would look something like:
public unsafe void DrawGeometry(SharpDX.Direct2D1.Geometry geometry, SharpDX.Direct2D1.Brush brush, System.Single strokeWidth, SharpDX.Direct2D1.StrokeStyle strokeStyle)
{
System.IntPtr geometry_ = System.IntPtr.Zero;
System.IntPtr brush_ = System.IntPtr.Zero;
System.IntPtr strokeStyle_ = System.IntPtr.Zero;
geometry_ = SharpDX.CppObject.ToCallbackPtr<SharpDX.Direct2D1.Geometry>(geometry);
brush_ = SharpDX.CppObject.ToCallbackPtr<SharpDX.Direct2D1.Brush>(brush);
strokeStyle_ = SharpDX.CppObject.ToCallbackPtr<SharpDX.Direct2D1.StrokeStyle>(strokeStyle);
SharpDX.Direct2D1.LocalInterop.CalliStdCallvoid(this._nativePointer, (void *)geometry_,
(void *)brush_, strokeWidth, (void *)strokeStyle_, (*(void ***)this._nativePointer)[22]);
GC.KeepAlive(this);
GC.KeepAlive(geometry);
GC.KeepAlive(brush);
GC.KeepAlive(strokeStyle);
}
When Configuration.EnableReleaseOnFinalize is set to true, SharpDX.ComObject's finalizer releases its reference to the native object (and I imagine other SharpGen users might want to do something similar). Currently, it's possible for the finalizer to run and decrement the ref count to zero before the native method runs and acquires its own ref:
This case can be trivially resolved with a using block in my code, but it seems like the point of Configuration.EnableReleaseOnFinalizer is that explicitly disposing is not required. I'm working around this issue with using blocks and GC.KeepAlive for the cases I've found in my codebase, but resolving this more generally would be great.
Could SharpGen generate GC.KeepAlive calls to delay finalization for objects whose native pointer is passed into a native method call? i.e. the generated method would look something like: