// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Runtime.InteropServices; using SharpDX.Diagnostics; namespace SharpDX { /// /// Root IUnknown class to interop with COM object /// public partial class ComObject : CppObject, IUnknown { /// /// Logs a warning of a possible memory leak when is enabled. /// Default uses . /// public static Action LogMemoryLeakWarning = (warning) => System.Diagnostics.Debug.WriteLine(warning); /// /// Initializes a new instance of the class from a IUnknown object. /// /// Reference to a IUnknown object public ComObject(object iunknowObject) { NativePointer = Marshal.GetIUnknownForObject(iunknowObject); } /// /// Initializes a new instance of the class. /// protected ComObject() { } /// /// Query this instance for a particular COM GUID/interface support. /// /// GUID query interface /// output object associated with this GUID, IntPtr.Zero in interface is not supported /// If this object doesn't support the interface /// ms682521 /// IUnknown::QueryInterface /// IUnknown::QueryInterface public virtual void QueryInterface(Guid guid, out IntPtr outPtr) { var result = ((IUnknown) this).QueryInterface(ref guid, out outPtr); result.CheckError(); } /// /// Query instance for a particular COM GUID/interface support. /// /// GUID query interface /// If this object doesn't support the interface /// ms682521 /// IUnknown::QueryInterface /// IUnknown::QueryInterface public virtual IntPtr QueryInterfaceOrNull(Guid guid) { var pointer = IntPtr.Zero; ((IUnknown)this).QueryInterface(ref guid, out pointer); return pointer; } /// /// Compares 2 COM objects and return true if the native pointer is the same. /// /// The left. /// The right. /// true if the native pointer is the same, false otherwise public static bool EqualsComObject(T left, T right) where T : ComObject { if (Equals(left, right)) { return true; } if (left == null || right == null) { return false; } return (left.NativePointer == right.NativePointer); } /// /// Query this instance for a particular COM interface support. /// ///The type of the COM interface to query ///An instance of the queried interface /// If this object doesn't support the interface /// ms682521 /// IUnknown::QueryInterface /// IUnknown::QueryInterface public virtual T QueryInterface() where T : ComObject { IntPtr parentPtr; this.QueryInterface(Utilities.GetGuidFromType(typeof(T)), out parentPtr); return FromPointer(parentPtr); } /// /// Query this instance for a particular COM interface support. /// ///The type of the COM interface to query ///An instance of the queried interface /// If this object doesn't support the interface /// ms682521 /// IUnknown::QueryInterface /// IUnknown::QueryInterface internal virtual T QueryInterfaceUnsafe() { IntPtr parentPtr; this.QueryInterface(Utilities.GetGuidFromType(typeof(T)), out parentPtr); return FromPointerUnsafe(parentPtr); } /// /// Queries a managed object for a particular COM interface support (This method is a shortcut to ) /// ///The type of the COM interface to query /// The managed COM object. ///An instance of the queried interface /// ms682521 /// IUnknown::QueryInterface /// IUnknown::QueryInterface public static T As(object comObject) where T : ComObject { using (var tempObject = new ComObject(Marshal.GetIUnknownForObject(comObject))) { return tempObject.QueryInterface(); } } /// /// Queries a managed object for a particular COM interface support (This method is a shortcut to ) /// ///The type of the COM interface to query /// The managed COM object. ///An instance of the queried interface /// ms682521 /// IUnknown::QueryInterface /// IUnknown::QueryInterface public static T As(IntPtr iunknownPtr) where T : ComObject { using (var tempObject = new ComObject(iunknownPtr)) { return tempObject.QueryInterface(); } } internal static T AsUnsafe(IntPtr iunknownPtr) { using (var tempObject = new ComObject(iunknownPtr)) { return tempObject.QueryInterfaceUnsafe(); } } /// /// Queries a managed object for a particular COM interface support. /// ///The type of the COM interface to query /// The managed COM object. ///An instance of the queried interface /// ms682521 /// IUnknown::QueryInterface /// IUnknown::QueryInterface public static T QueryInterface(object comObject) where T : ComObject { using (var tempObject = new ComObject(Marshal.GetIUnknownForObject(comObject))) { return tempObject.QueryInterface(); } } /// /// Queries a managed object for a particular COM interface support. /// ///The type of the COM interface to query /// A pointer to a COM object. ///An instance of the queried interface /// ms682521 /// IUnknown::QueryInterface /// IUnknown::QueryInterface public static T QueryInterfaceOrNull(IntPtr comPointer) where T : ComObject { if (comPointer == IntPtr.Zero) { return null; } var guid = Utilities.GetGuidFromType(typeof(T)); IntPtr pointerT; var result = (Result)Marshal.QueryInterface(comPointer, ref guid, out pointerT); return (result.Failure) ? null : FromPointerUnsafe(pointerT); } /// /// Query Interface for a particular interface support. /// ///An instance of the queried interface or null if it is not supported /// /// ms682521 /// IUnknown::QueryInterface /// IUnknown::QueryInterface public virtual T QueryInterfaceOrNull() where T : ComObject { return FromPointer(QueryInterfaceOrNull(Utilities.GetGuidFromType(typeof(T)))); } /// /// Query Interface for a particular interface support and attach to the given instance. /// /// /// protected void QueryInterfaceFrom(T fromObject) where T : ComObject { IntPtr parentPtr; fromObject.QueryInterface(Utilities.GetGuidFromType(this.GetType()), out parentPtr); NativePointer = parentPtr; } Result IUnknown.QueryInterface(ref Guid guid, out IntPtr comObject) { return Marshal.QueryInterface(NativePointer, ref guid, out comObject); } int IUnknown.AddReference() { if (NativePointer == IntPtr.Zero) throw new InvalidOperationException("COM Object pointer is null"); return Marshal.AddRef(NativePointer); } int IUnknown.Release() { if (NativePointer == IntPtr.Zero) throw new InvalidOperationException("COM Object pointer is null"); return Marshal.Release(NativePointer); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. /// ms682317 /// IUnknown::Release /// IUnknown::Release protected unsafe override void Dispose(bool disposing) { // Only dispose non-zero object if (NativePointer != IntPtr.Zero) { // If object is disposed by the finalizer, emits a warning if(!disposing && Configuration.EnableTrackingReleaseOnFinalizer) { if(!Configuration.EnableReleaseOnFinalizer) { var objectReference = ObjectTracker.Find(this); LogMemoryLeakWarning?.Invoke(string.Format("Warning: Live ComObject [0x{0:X}], potential memory leak: {1}", NativePointer.ToInt64(), objectReference)); } } // Release the object if (disposing || Configuration.EnableReleaseOnFinalizer) ((IUnknown)this).Release(); // Untrack the object if (Configuration.EnableObjectTracking) ObjectTracker.UnTrack(this); // Set pointer to null (using protected members in order to avoid NativePointerUpdat* callbacks. _nativePointer = (void*)0; } base.Dispose(disposing); } protected override void NativePointerUpdating() { if (Configuration.EnableObjectTracking) ObjectTracker.UnTrack(this); } protected override void NativePointerUpdated(IntPtr oldNativePointer) { if (Configuration.EnableObjectTracking) ObjectTracker.Track(this); } } }