/// <summary> /// Retrieves a native image handle from the native API. /// </summary> /// <param name="nativeMethod">Native method to retrieve the image.</param> /// <param name="cachedImage">A cached instance of the Image that we return to callers. (Callers may dispose this image, although they shouldn't).</param> /// <remarks> /// If this is the first time calling the property, we construct a new wrapper. /// If the handle is for an Image we have already constructed a wrapper for, we return the existing wrapper. /// If the handle is for a different Image, we construct a new wrapper and dispose the old one. /// If existing wrapper has been disposed, we throw an exception. /// </remarks> private void UpdateImageWrapperAndDisposePrevious( Func <NativeMethods.k4a_capture_t, NativeMethods.k4a_image_t> nativeMethod, ref Image cachedImage) { // Lock must be held to ensure the Image in cache is not replaced while we are inspecting it // It is still possible for that Image to be accessed or Disposed while this lock is held lock (this) { if (this.disposedValue) { throw new ObjectDisposedException(nameof(Capture)); } // Get the image object from the native SDK // We now own a reference on this image and must eventually release it. NativeMethods.k4a_image_t nativeImageHandle = nativeMethod(this.handle); // This try block ensures that we close image (and release our reference) if we fail // to create a new Image. try { // If this Capture previously had an image if (cachedImage != null) { // Get the native pointer IntPtr imageHandleValue = nativeImageHandle.DangerousGetHandle(); // Get the handle value from the cached image. If it has been disposed, this // will throw an exception, which will be passed to the caller. // Take an extra reference on the cachedImage to ensure it doesn't get disposed // after getting the IntPtr from the handle. using (Image reference = cachedImage.Reference()) { if (reference.DangerousGetHandle().DangerousGetHandle() != imageHandleValue) { // The image has changed, invalidate the current image and construct new wrappers cachedImage.Dispose(); cachedImage = null; } } } if (cachedImage == null && !nativeImageHandle.IsInvalid) { // Construct a new wrapper and return it // The native function may have returned #pragma warning disable CA2000 // Dispose objects before losing scope cachedImage = new Image(nativeImageHandle); #pragma warning restore CA2000 // Dispose objects before losing scope // Since we have wrapped image, it is now owned by Image and we should no longer close it nativeImageHandle = null; } } finally { // Ensure the native handle is closed if we have a failure creating the Image object if (nativeImageHandle != null) { nativeImageHandle.Close(); } } } }
// This function retrieves a native image handle from the native API. // // If this is the first time calling the property, we construct a new wrapper // If the handle is for an Image we have already constructed a wrapper for, we return the existing wrapper // If the handle is for a different Image, or if the wrapper has already been disposed, we construct a new wrapper and dispose the old one private Image GetImageWrapperAndDisposePrevious(Func <NativeMethods.k4a_capture_t, NativeMethods.k4a_image_t> nativeMethod, ref Image cache) { // Lock must be held to ensure the Image in cache is not replaced while we are inspecting it // It is still possible for that Image to be accessed or Disposed while this lock is held lock (this) { if (this.disposedValue) { throw new ObjectDisposedException(nameof(Capture)); } NativeMethods.k4a_image_t image = nativeMethod(this.handle); // This try block ensures that we close image try { if (cache != null) { IntPtr imageHandle = image.DangerousGetHandle(); // Only attempt to access cache in this try block try { // If cache was disposed before we called the native accessor (by the caller or another thread), // the handle could have been reused and the values would incorrectly match. However, cache.DangerousGetHandle() // will throw an exception, and we will correctly construct a new image wrapper. if (cache.DangerousGetHandle().DangerousGetHandle() != imageHandle) { // The image has changed, invalidate the current image and construct a new wrapper cache.Dispose(); cache = null; } } catch (ObjectDisposedException) { // If cache has been disposed by the caller or another thread we will discard // it and construct a new wrapper cache = null; } } if (cache == null && !image.IsInvalid) { // Construct a new wrapper and return it // The native function may have returned cache = new Image(image); // Since we have wrapped image, it is now owned by the UnsafeImage object and we should no longer close it image = null; } } finally { // Ensure the native handle is closed if we have a failure creating the Image object if (image != null) { image.Close(); } } return(cache); } }