diff --git a/ComMarshallingHelpers.cs b/ComMarshallingHelpers.cs new file mode 100644 index 0000000..5cae1db --- /dev/null +++ b/ComMarshallingHelpers.cs @@ -0,0 +1,88 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace SharpGen.Runtime +{ + public static class ComMarshallingHelpers + { + /// + /// Instantiate a CppObject from a native pointer. + /// + /// The CppObject class that will be returned + /// The native pointer to a com object. + /// An instance of T binded to the native pointer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T FromPointer(IntPtr cppObjectPtr) where T : CppObject + { + return MarshallingHelpers.FromPointer(cppObjectPtr); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr ToCallbackPtr(ComObject comObject) + where TCallback : ICallbackable + { + if (comObject == null) + return IntPtr.Zero; + // do not create extra object in case of the same type + if (comObject.GetType() != typeof(TCallback)) + { + var interfacePtr = comObject.QueryInterfaceOrNull(typeof (TCallback).GetTypeInfo().GUID); + var newInterface = (TCallback)Activator.CreateInstance(typeof(TCallback), interfacePtr); + // New pointer given from QI has one extra reference, so we need to release it, but we also need to create a holder for this pointer to make proper AddRef/Release in finalizer + if (!(newInterface is ComObject newComObject)) + throw new InvalidOperationException("Created object is now COM object"); + newComObject.Release(); + return newComObject.NativePointer; + } + return comObject.NativePointer; + } + + /// + /// Return the unmanaged C++ pointer from a instance. + /// + /// The type of the callback. + /// The callback. + /// A pointer to the unmanaged C++ object of the callback + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr ToCallbackPtr(ICallbackable callback) + where TCallback : ICallbackable + { + // we have to make QI to normalize pointer when passing COM object into COM call + if (callback is ComObject comObject) + { + return ToCallbackPtr(comObject); + } + return MarshallingHelpers.ToCallbackPtr(callback); + } + + /// + /// Return the unmanaged C++ pointer from a instance. + /// + /// The type of the callback. + /// The object. + /// A pointer to the unmanaged C++ object of the callback + /// This method is meant as a fast-path for codegen to use to reduce the number of casts. + public static IntPtr ToCallbackPtr(CppObject obj) + where TCallback : ICallbackable + { + return ToCallbackPtr((ICallbackable) obj); + } + + /// + /// Makes additional transformation for the object which was unmarshalled from unmanaged code + /// + /// + /// Indicates that the object was obtained via 'Obj**' parameter + /// Transformed object + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T TransformObjectFromUnmanaged(T obj, bool isOutParam) where T : CppObject + { + // objects returned via Obj** has one extra AddRef, so we need to call Release to balance references + if (isOutParam && obj is ComObject comObject) + comObject.Release(); + return obj; + } + + } +} \ No newline at end of file diff --git a/ComObject.cs b/ComObject.cs index 99899a0..b8b7b52 100644 --- a/ComObject.cs +++ b/ComObject.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// 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 @@ -95,7 +95,7 @@ public virtual T QueryInterface() where T : ComObject IntPtr parentPtr; var result = this.QueryInterface(typeof(T).GetTypeInfo().GUID, out parentPtr); result.CheckError(); - return FromPointer(parentPtr); + return MarshallingHelpers.FromPointer(parentPtr); } /// @@ -168,7 +168,7 @@ public static T QueryInterfaceOrNull(IntPtr comPointer) where T : ComObject var guid = typeof(T).GetTypeInfo().GUID; IntPtr pointerT; var result = (Result)Marshal.QueryInterface(comPointer, ref guid, out pointerT); - return (result.Failure) ? null : FromPointer(pointerT); + return (result.Failure) ? null : ComMarshallingHelpers.FromPointer(pointerT); } /// @@ -181,7 +181,7 @@ public static T QueryInterfaceOrNull(IntPtr comPointer) where T : ComObject /// IUnknown::QueryInterface public virtual T QueryInterfaceOrNull() where T : ComObject { - return FromPointer(QueryInterfaceOrNull(typeof(T).GetTypeInfo().GUID)); + return ComMarshallingHelpers.FromPointer(QueryInterfaceOrNull(typeof(T).GetTypeInfo().GUID)); } /// @@ -196,6 +196,24 @@ protected void QueryInterfaceFrom(T fromObject) where T : ComObject NativePointer = parentPtr; } + // Called with old ptr + protected override unsafe void NativePointerUpdating() + { + // make Release when dropping the pointer + if (_nativePointer != null) + Release(); + base.NativePointerUpdating(); + } + + // Called with new ptr + protected override unsafe void NativePointerUpdated(IntPtr oldNativePointer) + { + // when taking new pointer need to make AddRef + if (_nativePointer != null) + AddRef(); + base.NativePointerUpdated(oldNativePointer); + } + /// /// Releases unmanaged and - optionally - managed resources /// diff --git a/SharpGen.Runtime.COM.csproj b/SharpGen.Runtime.COM.csproj index ace4edf..50ef33d 100644 --- a/SharpGen.Runtime.COM.csproj +++ b/SharpGen.Runtime.COM.csproj @@ -15,5 +15,7 @@ - + + +