// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // ---------------------------------------------------------------------------------- // Interop library code // // Marshalling helpers used by MCG generated stub // McgMarshal covers full marshalling surface area and is entrypoint for all marshalling support. // In long term: // 1. MCG generated code should call McgMarshal to do marshalling // 2. Public Marhshal API should call McgMarshal to do marshalling // NOTE: // These source code are being published to InternalAPIs and consumed by RH builds // Use PublishInteropAPI.bat to keep the InternalAPI copies in sync // ---------------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices.WindowsRuntime; using System.Runtime.InteropServices; using System.Threading; using System.Text; using System.Runtime; using System.Runtime.CompilerServices; using Internal.NativeFormat; #if !CORECLR using Internal.Runtime.Augments; #endif #if RHTESTCL using OutputClass = System.Console; #else using OutputClass = System.Diagnostics.Debug; #endif namespace System.Runtime.InteropServices { /// /// Expose functionality from System.Private.CoreLib and forwards calls to InteropExtensions in System.Private.CoreLib /// [CLSCompliant(false)] public static partial class McgMarshal { public static void SaveLastWin32Error() { PInvokeMarshal.SaveLastWin32Error(); } public static void ClearLastWin32Error() { PInvokeMarshal.ClearLastWin32Error(); } public static bool GuidEquals(ref Guid left, ref Guid right) { return InteropExtensions.GuidEquals(ref left, ref right); } public static bool ComparerEquals(T left, T right) { return InteropExtensions.ComparerEquals(left, right); } public static T CreateClass() where T : class { return InteropExtensions.UncheckedCast(InteropExtensions.RuntimeNewObject(typeof(T).TypeHandle)); } public static bool IsEnum(object obj) { #if RHTESTCL return false; #else return InteropExtensions.IsEnum(obj.GetTypeHandle()); #endif } /// /// Return true if the type is __COM or derived from __COM. False otherwise /// public static bool IsComObject(Type type) { #if RHTESTCL return false; #else return type == typeof(__ComObject) || type.GetTypeInfo().IsSubclassOf(typeof(__ComObject)); #endif } /// /// Return true if the object is a RCW. False otherwise /// internal static bool IsComObject(object obj) { return (obj is __ComObject); } public static T FastCast(object value) where T : class { // We have an assert here, to verify that a "real" cast would have succeeded. // However, casting on weakly-typed RCWs modifies their state, by doing a QI and caching // the result. This often makes things work which otherwise wouldn't work (especially variance). Debug.Assert(value == null || value is T); return InteropExtensions.UncheckedCast(value); } /// /// Converts a managed DateTime to native OLE datetime /// Used by MCG marshalling code /// public static double ToNativeOleDate(DateTime dateTime) { return InteropExtensions.ToNativeOleDate(dateTime); } /// /// Converts native OLE datetime to managed DateTime /// Used by MCG marshalling code /// public static DateTime FromNativeOleDate(double nativeOleDate) { return InteropExtensions.FromNativeOleDate(nativeOleDate); } /// /// Used in Marshalling code /// Call safeHandle.InitializeHandle to set the internal _handle field /// public static void InitializeHandle(SafeHandle safeHandle, IntPtr win32Handle) { InteropExtensions.InitializeHandle(safeHandle, win32Handle); } /// /// Check if obj's type is the same as represented by normalized handle /// [MethodImpl(MethodImplOptions.NoInlining)] public static bool IsOfType(object obj, RuntimeTypeHandle handle) { return obj.IsOfType(handle); } #if ENABLE_MIN_WINRT public static unsafe void SetExceptionErrorCode(Exception exception, int errorCode) { InteropExtensions.SetExceptionErrorCode(exception, errorCode); } /// /// Used in Marshalling code /// Gets the handle of the CriticalHandle /// public static IntPtr GetHandle(CriticalHandle criticalHandle) { return InteropExtensions.GetCriticalHandle(criticalHandle); } /// /// Used in Marshalling code /// Sets the handle of the CriticalHandle /// public static void SetHandle(CriticalHandle criticalHandle, IntPtr handle) { InteropExtensions.SetCriticalHandle(criticalHandle, handle); } #endif } /// /// McgMarshal helpers exposed to be used by MCG /// public static partial class McgMarshal { #region Type marshalling public static Type TypeNameToType(HSTRING nativeTypeName, int nativeTypeKind) { #if ENABLE_WINRT return McgTypeHelpers.TypeNameToType(nativeTypeName, nativeTypeKind); #else throw new NotSupportedException("TypeNameToType"); #endif } internal static Type TypeNameToType(string nativeTypeName, int nativeTypeKind) { #if ENABLE_WINRT return McgTypeHelpers.TypeNameToType(nativeTypeName, nativeTypeKind, checkTypeKind: false); #else throw new NotSupportedException("TypeNameToType"); #endif } public static unsafe void TypeToTypeName( Type type, out HSTRING nativeTypeName, out int nativeTypeKind) { #if ENABLE_WINRT McgTypeHelpers.TypeToTypeName(type, out nativeTypeName, out nativeTypeKind); #else throw new NotSupportedException("TypeToTypeName"); #endif } /// /// Fetch type name /// /// type /// type name internal static string TypeToTypeName(RuntimeTypeHandle typeHandle, out int nativeTypeKind) { #if ENABLE_WINRT TypeKind typekind; string typeName; McgTypeHelpers.TypeToTypeName(typeHandle, out typeName, out typekind); nativeTypeKind = (int)typekind; return typeName; #else throw new NotSupportedException("TypeToTypeName"); #endif } #endregion #region String marshalling [CLSCompliant(false)] public static unsafe void StringBuilderToUnicodeString(System.Text.StringBuilder stringBuilder, ushort* destination) { PInvokeMarshal.StringBuilderToUnicodeString(stringBuilder, destination); } [CLSCompliant(false)] public static unsafe void UnicodeStringToStringBuilder(ushort* newBuffer, System.Text.StringBuilder stringBuilder) { PInvokeMarshal.UnicodeStringToStringBuilder(newBuffer, stringBuilder); } #if !RHTESTCL [CLSCompliant(false)] public static unsafe void StringBuilderToAnsiString(System.Text.StringBuilder stringBuilder, byte* pNative, bool bestFit, bool throwOnUnmappableChar) { PInvokeMarshal.StringBuilderToAnsiString(stringBuilder, pNative, bestFit, throwOnUnmappableChar); } [CLSCompliant(false)] public static unsafe void AnsiStringToStringBuilder(byte* newBuffer, System.Text.StringBuilder stringBuilder) { PInvokeMarshal.AnsiStringToStringBuilder(newBuffer, stringBuilder); } /// /// Convert ANSI string to unicode string, with option to free native memory. Calls generated by MCG /// /// Input assumed to be zero terminated. Generates String.Empty for zero length string. /// This version is more efficient than ConvertToUnicode in src\Interop\System\Runtime\InteropServices\Marshal.cs in that it can skip calling /// MultiByteToWideChar for ASCII string, and it does not need another char[] buffer [CLSCompliant(false)] public static unsafe string AnsiStringToString(byte* pchBuffer) { return PInvokeMarshal.AnsiStringToString(pchBuffer); } /// /// Convert UNICODE string to ANSI string. /// /// This version is more efficient than StringToHGlobalAnsi in Interop\System\Runtime\InteropServices\Marshal.cs in that /// it could allocate single byte per character, instead of SystemMaxDBCSCharSize per char, and it can skip calling WideCharToMultiByte for ASCII string [CLSCompliant(false)] public static unsafe byte* StringToAnsiString(string str, bool bestFit, bool throwOnUnmappableChar) { return PInvokeMarshal.StringToAnsiString(str, bestFit, throwOnUnmappableChar); } /// /// Convert UNICODE wide char array to ANSI ByVal byte array. /// /// /// * This version works with array instead string, it means that there will be NO NULL to terminate the array. /// * The buffer to store the byte array must be allocated by the caller and must fit managedArray.Length. /// /// UNICODE wide char array /// Allocated buffer where the ansi characters must be placed. Could NOT be null. Buffer size must fit char[].Length. [CLSCompliant(false)] public static unsafe void ByValWideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, int expectedCharCount, bool bestFit, bool throwOnUnmappableChar) { PInvokeMarshal.ByValWideCharArrayToAnsiCharArray(managedArray, pNative, expectedCharCount, bestFit, throwOnUnmappableChar); } [CLSCompliant(false)] public static unsafe void ByValAnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray) { PInvokeMarshal.ByValAnsiCharArrayToWideCharArray(pNative, managedArray); } [CLSCompliant(false)] public static unsafe void WideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, bool bestFit, bool throwOnUnmappableChar) { PInvokeMarshal.WideCharArrayToAnsiCharArray(managedArray, pNative, bestFit, throwOnUnmappableChar); } /// /// Convert ANSI ByVal byte array to UNICODE wide char array, best fit /// /// /// * This version works with array instead to string, it means that the len must be provided and there will be NO NULL to /// terminate the array. /// * The buffer to the UNICODE wide char array must be allocated by the caller. /// /// Pointer to the ANSI byte array. Could NOT be null. /// Maximum buffer size. /// Wide char array that has already been allocated. [CLSCompliant(false)] public static unsafe void AnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray) { PInvokeMarshal.AnsiCharArrayToWideCharArray(pNative, managedArray); } /// /// Convert a single UNICODE wide char to a single ANSI byte. /// /// single UNICODE wide char value public static unsafe byte WideCharToAnsiChar(char managedValue, bool bestFit, bool throwOnUnmappableChar) { return PInvokeMarshal.WideCharToAnsiChar(managedValue, bestFit, throwOnUnmappableChar); } /// /// Convert a single ANSI byte value to a single UNICODE wide char value, best fit. /// /// Single ANSI byte value. public static unsafe char AnsiCharToWideChar(byte nativeValue) { return PInvokeMarshal.AnsiCharToWideChar(nativeValue); } /// /// Convert UNICODE string to ANSI ByVal string. /// /// This version is more efficient than StringToHGlobalAnsi in Interop\System\Runtime\InteropServices\Marshal.cs in that /// it could allocate single byte per character, instead of SystemMaxDBCSCharSize per char, and it can skip calling WideCharToMultiByte for ASCII string /// Unicode string. /// Allocated buffer where the ansi string must be placed. Could NOT be null. Buffer size must fit str.Length. [CLSCompliant(false)] public static unsafe void StringToByValAnsiString(string str, byte* pNative, int charCount, bool bestFit, bool throwOnUnmappableChar) { PInvokeMarshal.StringToByValAnsiString(str, pNative, charCount, bestFit, throwOnUnmappableChar); } /// /// Convert ANSI string to unicode string, with option to free native memory. Calls generated by MCG /// /// Input assumed to be zero terminated. Generates String.Empty for zero length string. /// This version is more efficient than ConvertToUnicode in src\Interop\System\Runtime\InteropServices\Marshal.cs in that it can skip calling /// MultiByteToWideChar for ASCII string, and it does not need another char[] buffer [CLSCompliant(false)] public static unsafe string ByValAnsiStringToString(byte* pchBuffer, int charCount) { return PInvokeMarshal.ByValAnsiStringToString(pchBuffer, charCount); } /// /// CoTaskMemAlloc + ZeroMemory /// @TODO - we can probably optimize the zero memory part later /// public unsafe static void* CoTaskMemAllocAndZeroMemory(IntPtr size) { void *ptr = (void*)PInvokeMarshal.CoTaskMemAlloc(new UIntPtr((void*)size)); if (ptr == null) return ptr; byte *pByte = (byte*)ptr; long lSize = size.ToInt64(); while (lSize > 0) { lSize--; (*pByte++) = 0; } return ptr; } /// /// Free allocated memory. The allocated memory should be allocated by CoTaskMemAlloc /// public static void SafeCoTaskMemFree(IntPtr allocatedMemory) { if (allocatedMemory != IntPtr.Zero) PInvokeMarshal.CoTaskMemFree(allocatedMemory); } /// /// Free allocated memory. The allocated memory should be allocated by CoTaskMemAlloc /// public static unsafe void SafeCoTaskMemFree(void* pv) { if (pv != null) PInvokeMarshal.CoTaskMemFree(new IntPtr(pv)); } /// /// Allocate a buffer with enough size to store the unicode characters saved in source /// Buffer is allocated with CoTaskMemAlloc /// public unsafe static void *AllocUnicodeBuffer(string source) { if (source == null) return null; int byteLen = checked((source.Length + 1) * 2); char* pBuf = (char*)PInvokeMarshal.CoTaskMemAlloc(new UIntPtr((uint)byteLen)); if (pBuf == null) throw new System.OutOfMemoryException(); return pBuf; } /// /// Copy unicode characters in source into dest, and terminating with null /// public unsafe static void CopyUnicodeString(string source, void* _dest) { if (source == null) return; char* dest = (char *)_dest; fixed (char* pSource = source) { int len = source.Length; char* src = pSource; // Copy characters one by one, including the null terminator for (int i = 0; i <= len; ++i) { *(dest++) = *(src++); } } } /// /// Convert String to BSTR /// public unsafe static ushort* ConvertStringToBSTR( ushort* ptrToFirstCharInBSTR, string strManaged) { if (strManaged == null) return null; if (ptrToFirstCharInBSTR == null) { // If caller don't provided buffer, allocate the buffer and create string using SysAllocStringLen fixed (char* ch = strManaged) { return (ushort*) ExternalInterop.SysAllocStringLen(ch, (uint)strManaged.Length); } } else { // If caller provided a buffer, construct the BSTR manually. // set length *((int*)ptrToFirstCharInBSTR - 1) = checked(strManaged.Length * 2); // copy characters from the managed string fixed (char* ch = strManaged) { InteropExtensions.Memcpy( (System.IntPtr)ptrToFirstCharInBSTR, (System.IntPtr)ch, (strManaged.Length + 1) * 2); } return ptrToFirstCharInBSTR; } } /// /// Convert BSTR to String /// public unsafe static string ConvertBSTRToString(ushort* bstr) { if (bstr == null) return null; return new string((char*)bstr, 0, (int)ExternalInterop.SysStringLen(bstr)); } /// /// Free Allocated BSTR /// public static unsafe void SysFreeString(void* pBSTR) { SysFreeString(new IntPtr(pBSTR)); } /// /// Free Allocated BSTR /// public unsafe static void SysFreeString(IntPtr pBSTR) { ExternalInterop.SysFreeString(pBSTR); } #endif #if ENABLE_MIN_WINRT [MethodImplAttribute(MethodImplOptions.NoInlining)] public static unsafe HSTRING StringToHString(string sourceString) { if (sourceString == null) throw new ArgumentNullException(nameof(sourceString), SR.Null_HString); return StringToHStringInternal(sourceString); } [MethodImplAttribute(MethodImplOptions.NoInlining)] public static unsafe HSTRING StringToHStringForField(string sourceString) { #if !RHTESTCL if (sourceString == null) throw new MarshalDirectiveException(SR.BadMarshalField_Null_HString); #endif return StringToHStringInternal(sourceString); } private static unsafe HSTRING StringToHStringInternal(string sourceString) { HSTRING ret; int hr = StringToHStringNoNullCheck(sourceString, &ret); if (hr < 0) throw Marshal.GetExceptionForHR(hr); return ret; } [MethodImplAttribute(MethodImplOptions.NoInlining)] internal static unsafe int StringToHStringNoNullCheck(string sourceString, HSTRING* hstring) { fixed (char* pChars = sourceString) { int hr = ExternalInterop.WindowsCreateString(pChars, (uint)sourceString.Length, (void*)hstring); return hr; } } #endif //ENABLE_MIN_WINRT #endregion #region COM marshalling /// /// Explicit AddRef for RCWs /// You can't call IFoo.AddRef anymore as IFoo no longer derive from IUnknown /// You need to call McgMarshal.AddRef(); /// /// /// Used by prefast MCG plugin (mcgimportpft) only /// [CLSCompliant(false)] public static int AddRef(__ComObject obj) { return obj.AddRef(); } /// /// Explicit Release for RCWs /// You can't call IFoo.Release anymore as IFoo no longer derive from IUnknown /// You need to call McgMarshal.Release(); /// /// /// Used by prefast MCG plugin (mcgimportpft) only /// [CLSCompliant(false)] public static int Release(__ComObject obj) { return obj.Release(); } [MethodImpl(MethodImplOptions.NoInlining)] public static unsafe int ComAddRef(IntPtr pComItf) { return CalliIntrinsics.StdCall__AddRef(((__com_IUnknown*)(void*)pComItf)->pVtable-> pfnAddRef, pComItf); } [MethodImpl(MethodImplOptions.NoInlining)] internal static unsafe int ComRelease_StdCall(IntPtr pComItf) { return CalliIntrinsics.StdCall__Release(((__com_IUnknown*)(void*)pComItf)->pVtable-> pfnRelease, pComItf); } /// /// Inline version of ComRelease /// [MethodImpl(MethodImplOptions.NoInlining)] //reduces MCG-generated code size public static unsafe int ComRelease(IntPtr pComItf) { IntPtr pRelease = ((__com_IUnknown*)(void*)pComItf)->pVtable->pfnRelease; // Check if the COM object is implemented by PN Interop code, for which we can call directly if (pRelease == AddrOfIntrinsics.AddrOf(__vtable_IUnknown.Release)) { return __interface_ccw.DirectRelease(pComItf); } // Normal slow path, do not inline return ComRelease_StdCall(pComItf); } [MethodImpl(MethodImplOptions.NoInlining)] public static unsafe int ComSafeRelease(IntPtr pComItf) { if (pComItf != default(IntPtr)) { return ComRelease(pComItf); } return 0; } public static int FinalReleaseComObject(object o) { if (o == null) throw new ArgumentNullException(nameof(o)); __ComObject co = null; // Make sure the obj is an __ComObject. try { co = (__ComObject)o; } catch (InvalidCastException) { throw new ArgumentException(SR.Argument_ObjNotComObject, nameof(o)); } co.FinalReleaseSelf(); return 0; } /// /// Returns the cached WinRT factory RCW under the current context /// [CLSCompliant(false)] public static unsafe __ComObject GetActivationFactory(string className, RuntimeTypeHandle factoryIntf) { #if ENABLE_MIN_WINRT return FactoryCache.Get().GetActivationFactory(className, factoryIntf); #else throw new PlatformNotSupportedException("GetActivationFactory"); #endif } /// /// Used by CCW infrastructure code to return the target object from this pointer /// /// The target object pointed by this pointer public static object ThisPointerToTargetObject(IntPtr pUnk) { return ComCallableObject.GetTarget(pUnk); } [CLSCompliant(false)] public static object ComInterfaceToObject_NoUnboxing( IntPtr pComItf, RuntimeTypeHandle interfaceType) { return McgComHelpers.ComInterfaceToObjectInternal( pComItf, interfaceType, default(RuntimeTypeHandle), McgComHelpers.CreateComObjectFlags.SkipTypeResolutionAndUnboxing ); } /// /// Shared CCW Interface To Object /// /// /// /// /// [CLSCompliant(false)] public static object ComInterfaceToObject( System.IntPtr pComItf, RuntimeTypeHandle interfaceType, RuntimeTypeHandle classTypeInSignature) { #if ENABLE_MIN_WINRT if (interfaceType.Equals(typeof(object).TypeHandle)) { return McgMarshal.IInspectableToObject(pComItf); } if (interfaceType.Equals(typeof(System.String).TypeHandle)) { return McgMarshal.HStringToString(pComItf); } if (interfaceType.IsComClass()) { RuntimeTypeHandle defaultInterface = interfaceType.GetDefaultInterface(); Debug.Assert(!defaultInterface.IsNull()); return ComInterfaceToObjectInternal(pComItf, defaultInterface, interfaceType); } #endif return ComInterfaceToObjectInternal( pComItf, interfaceType, classTypeInSignature ); } [CLSCompliant(false)] public static object ComInterfaceToObject( IntPtr pComItf, RuntimeTypeHandle interfaceType) { return ComInterfaceToObject(pComItf, interfaceType, default(RuntimeTypeHandle)); } private static object ComInterfaceToObjectInternal( IntPtr pComItf, RuntimeTypeHandle interfaceType, RuntimeTypeHandle classTypeInSignature) { object result = McgComHelpers.ComInterfaceToObjectInternal(pComItf, interfaceType, classTypeInSignature, McgComHelpers.CreateComObjectFlags.None); // // Make sure the type we returned is actually of the right type // NOTE: Don't pass null to IsInstanceOfClass as it'll return false // if (!classTypeInSignature.IsNull() && result != null) { if (!InteropExtensions.IsInstanceOfClass(result, classTypeInSignature)) throw new InvalidCastException(); } return result; } public static unsafe IntPtr ComQueryInterfaceNoThrow(IntPtr pComItf, ref Guid iid) { int hr = 0; return ComQueryInterfaceNoThrow(pComItf, ref iid, out hr); } public static unsafe IntPtr ComQueryInterfaceNoThrow(IntPtr pComItf, ref Guid iid, out int hr) { IntPtr pComIUnk; hr = ComQueryInterfaceWithHR(pComItf, ref iid, out pComIUnk); return pComIUnk; } internal static unsafe int ComQueryInterfaceWithHR(IntPtr pComItf, ref Guid iid, out IntPtr ppv) { IntPtr pComIUnk; int hr; fixed (Guid* unsafe_iid = &iid) { hr = CalliIntrinsics.StdCall__QueryInterface(((__com_IUnknown*)(void*)pComItf)->pVtable-> pfnQueryInterface, pComItf, new IntPtr(unsafe_iid), new IntPtr(&pComIUnk)); } if (hr != 0) { ppv = default(IntPtr); } else { ppv = pComIUnk; } return hr; } /// /// Helper function to copy vTable to native heap on CoreCLR. /// /// Vtbl type /// static v-table field , always a valid pointer /// Pointer to Vtable on native heap on CoreCLR , on N it's an alias for pVtbl public static unsafe IntPtr GetCCWVTableCopy(void* pVtbl, ref IntPtr pNativeVtbl, int size) { if (pNativeVtbl == default(IntPtr)) { #if CORECLR // On CoreCLR copy vTable to native heap , on N VTable is frozen. IntPtr pv = Marshal.AllocHGlobal(size); int* pSrc = (int*)pVtbl; int* pDest = (int*)pv.ToPointer(); int pSize = sizeof(int); // this should never happen , if a CCW is discarded we never get here. Debug.Assert(size >= pSize); for (int i = 0; i < size; i += pSize) { *pDest++ = *pSrc++; } if (Interlocked.CompareExchange(ref pNativeVtbl, pv, default(IntPtr)) != default(IntPtr)) { // Another thread sneaked-in and updated pNativeVtbl , just use the update from other thread Marshal.FreeHGlobal(pv); } #else // .NET NATIVE // Wrap it in an IntPtr pNativeVtbl = (IntPtr)pVtbl; #endif // CORECLR } return pNativeVtbl; } [CLSCompliant(false)] public static IntPtr ObjectToComInterface( object obj, RuntimeTypeHandle typeHnd) { #if ENABLE_MIN_WINRT if (typeHnd.Equals(typeof(object).TypeHandle)) { return McgMarshal.ObjectToIInspectable(obj); } if (typeHnd.Equals(typeof(System.String).TypeHandle)) { return McgMarshal.StringToHString((string)obj).handle; } if (typeHnd.IsComClass()) { // This code path should be executed only for WinRT classes typeHnd = typeHnd.GetDefaultInterface(); Debug.Assert(!typeHnd.IsNull()); } #endif return McgComHelpers.ObjectToComInterfaceInternal( obj, typeHnd ); } public static IntPtr ObjectToIInspectable(Object obj) { #if ENABLE_MIN_WINRT return ObjectToComInterface(obj, InternalTypes.IInspectable); #else throw new PlatformNotSupportedException("ObjectToIInspectable"); #endif } // This is not a safe function to use for any funtion pointers that do not point // at a static function. This is due to the behavior of shared generics, // where instance function entry points may share the exact same address // but static functions are always represented in delegates with customized // stubs. private static bool DelegateTargetMethodEquals(Delegate del, IntPtr pfn) { RuntimeTypeHandle thDummy; return del.GetFunctionPointer(out thDummy) == pfn; } [MethodImpl(MethodImplOptions.NoInlining)] public static IntPtr DelegateToComInterface(Delegate del, RuntimeTypeHandle typeHnd) { if (del == null) return default(IntPtr); IntPtr stubFunctionAddr = typeHnd.GetDelegateInvokeStub(); object targetObj; // // If the delegate points to the forward stub for the native delegate, // then we want the RCW associated with the native interface. Otherwise, // this is a managed delegate, and we want the CCW associated with it. // if (DelegateTargetMethodEquals(del, stubFunctionAddr)) targetObj = del.Target; else targetObj = del; return McgMarshal.ObjectToComInterface(targetObj, typeHnd); } [MethodImpl(MethodImplOptions.NoInlining)] public static Delegate ComInterfaceToDelegate(IntPtr pComItf, RuntimeTypeHandle typeHnd) { if (pComItf == default(IntPtr)) return null; object obj = ComInterfaceToObject(pComItf, typeHnd, /* classIndexInSignature */ default(RuntimeTypeHandle)); // // If the object we got back was a managed delegate, then we're good. Otherwise, // the object is an RCW for a native delegate, so we need to wrap it with a managed // delegate that invokes the correct stub. // Delegate del = obj as Delegate; if (del == null) { Debug.Assert(obj is __ComObject); IntPtr stubFunctionAddr = typeHnd.GetDelegateInvokeStub(); del = InteropExtensions.CreateDelegate( typeHnd, stubFunctionAddr, obj, /*isStatic:*/ true, /*isVirtual:*/ false, /*isOpen:*/ false); } return del; } /// /// Marshal array of objects /// [MethodImplAttribute(MethodImplOptions.NoInlining)] unsafe public static void ObjectArrayToComInterfaceArray(uint len, System.IntPtr* dst, object[] src, RuntimeTypeHandle typeHnd) { for (uint i = 0; i < len; i++) { dst[i] = McgMarshal.ObjectToComInterface(src[i], typeHnd); } } /// /// Allocate native memory, and then marshal array of objects /// [MethodImplAttribute(MethodImplOptions.NoInlining)] unsafe public static System.IntPtr* ObjectArrayToComInterfaceArrayAlloc(object[] src, RuntimeTypeHandle typeHnd, out uint len) { System.IntPtr* dst = null; len = 0; if (src != null) { len = (uint)src.Length; dst = (System.IntPtr*)PInvokeMarshal.CoTaskMemAlloc((System.UIntPtr)(len * (sizeof(System.IntPtr)))); for (uint i = 0; i < len; i++) { dst[i] = McgMarshal.ObjectToComInterface(src[i], typeHnd); } } return dst; } /// /// Get outer IInspectable for managed object deriving from native scenario /// At this point the inner is not created yet - you need the outer first and pass it to the factory /// to create the inner /// [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static IntPtr GetOuterIInspectableForManagedObject(__ComObject managedObject) { ComCallableObject ccw = null; try { // // Create the CCW over the RCW // Note that they are actually both the same object // Base class = inner // Derived class = outer // ccw = new ComCallableObject( managedObject, // The target object = managedObject managedObject // The inner RCW (as __ComObject) = managedObject ); // // Retrieve the outer IInspectable // Pass skipInterfaceCheck = true to avoid redundant checks // return ccw.GetComInterfaceForType_NoCheck(InternalTypes.IInspectable, ref Interop.COM.IID_IInspectable); } finally { // // Free the extra ref count initialized by __native_ccw.Init (to protect the CCW from being collected) // if (ccw != null) ccw.Release(); } } [CLSCompliant(false)] public static unsafe IntPtr ManagedObjectToComInterface(Object obj, RuntimeTypeHandle interfaceType) { return McgComHelpers.ManagedObjectToComInterface(obj, interfaceType); } public static unsafe object IInspectableToObject(IntPtr pComItf) { #if ENABLE_WINRT return ComInterfaceToObject(pComItf, InternalTypes.IInspectable); #else throw new PlatformNotSupportedException("IInspectableToObject"); #endif } public static unsafe IntPtr CoCreateInstanceEx(Guid clsid, string server) { #if ENABLE_WINRT Interop.COM.MULTI_QI results; IntPtr pResults = new IntPtr(&results); fixed (Guid* pIID = &Interop.COM.IID_IUnknown) { Guid* pClsid = &clsid; results.pIID = new IntPtr(pIID); results.pItf = IntPtr.Zero; results.hr = 0; int hr; // if server name is specified, do remote server activation if (!String.IsNullOrEmpty(server)) { Interop.COM.COSERVERINFO serverInfo; fixed (char* pName = server) { serverInfo.Name = new IntPtr(pName); IntPtr pServerInfo = new IntPtr(&serverInfo); hr = ExternalInterop.CoCreateInstanceFromApp(pClsid, IntPtr.Zero, (int)Interop.COM.CLSCTX.CLSCTX_REMOTE_SERVER, pServerInfo, 1, pResults); } } else { hr = ExternalInterop.CoCreateInstanceFromApp(pClsid, IntPtr.Zero, (int)Interop.COM.CLSCTX.CLSCTX_SERVER, IntPtr.Zero, 1, pResults); } if (hr < 0) { throw McgMarshal.GetExceptionForHR(hr, /*isWinRTScenario = */ false); } if (results.hr < 0) { throw McgMarshal.GetExceptionForHR(results.hr, /* isWinRTScenario = */ false); } return results.pItf; } #else throw new PlatformNotSupportedException("CoCreateInstanceEx"); #endif } public static unsafe IntPtr CoCreateInstanceEx(Guid clsid) { return CoCreateInstanceEx(clsid, string.Empty); } #endregion #region Testing /// /// Internal-only method to allow testing of apartment teardown code /// public static void ReleaseRCWsInCurrentApartment() { ContextEntry.RemoveCurrentContext(); } /// /// Used by detecting leaks /// Used in prefast MCG only /// public static int GetTotalComObjectCount() { return ComObjectCache.s_comObjectMap.Count; } /// /// Used by detecting and dumping leaks /// Used in prefast MCG only /// public static IEnumerable<__ComObject> GetAllComObjects() { List<__ComObject> list = new List<__ComObject>(); for (int i = 0; i < ComObjectCache.s_comObjectMap.GetMaxCount(); ++i) { IntPtr pHandle = default(IntPtr); if (ComObjectCache.s_comObjectMap.GetValue(i, ref pHandle) && (pHandle != default(IntPtr))) { GCHandle handle = GCHandle.FromIntPtr(pHandle); list.Add(InteropExtensions.UncheckedCast<__ComObject>(handle.Target)); } } return list; } #endregion /// /// This method propagate the exception being thrown. /// 1. On Windows8+, WinRT scenarios we do the following. /// a. Check whether the exception has any IRestrictedErrorInfo associated with it. /// If so, it means that this exception was actually caused by a native exception in which case we do simply use the same /// message and stacktrace. /// b. If not, this is actually a managed exception and in this case we RoOriginateLanguageException with the msg, hresult and the IErrorInfo /// associated with the managed exception. This helps us to retrieve the same exception in case it comes back to native. /// 2. On win8 and for classic COM scenarios. /// a. This method should not be called /// /// [MethodImpl(MethodImplOptions.NoInlining)] public static bool PropagateException(Exception ex) { #if ENABLE_WINRT return ExceptionHelpers.PropagateException(ex); #else // TODO : ExceptionHelpers should be platform specific , move it to // seperate source files return true; #endif } /// /// This method returns HR for the exception being thrown. /// 1. On Windows8+, WinRT scenarios /// The work to propagate the exception should have already performed in the exception filter /// by calling PropagateException() /// 2. On win8 and for classic COM scenarios. /// a. We create IErrorInfo for the given Exception object and SetErrorInfo with the given IErrorInfo. /// /// [MethodImpl(MethodImplOptions.NoInlining)] public static int GetHRForExceptionWinRT(Exception ex) { #if ENABLE_WINRT return ExceptionHelpers.GetHRForExceptionWithErrorPropagationNoThrow(ex, true); #else // TODO : ExceptionHelpers should be platform specific , move it to // seperate source files return 0; //return Marshal.GetHRForException(ex); #endif } [MethodImpl(MethodImplOptions.NoInlining)] public static int GetHRForException(Exception ex) { #if ENABLE_WINRT return ExceptionHelpers.GetHRForExceptionWithErrorPropagationNoThrow(ex, false); #else return ex.HResult; #endif } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowOnExternalCallFailed(int hr, System.RuntimeTypeHandle typeHnd) { bool isWinRTScenario #if ENABLE_WINRT = typeHnd.IsSupportIInspectable(); #else = false; #endif throw McgMarshal.GetExceptionForHR(hr, isWinRTScenario); } /// /// This method returns a new Exception object given the HR value. /// /// /// public static Exception GetExceptionForHR(int hr, bool isWinRTScenario) { #if ENABLE_WINRT return ExceptionHelpers.GetExceptionForHRInternalNoThrow(hr, isWinRTScenario, !isWinRTScenario); #elif CORECLR return Marshal.GetExceptionForHR(hr); #else // TODO: Map HR to exeption even without COM interop support? return new COMException(hr); #endif } #region Shared templates #if ENABLE_MIN_WINRT public static void CleanupNative(IntPtr pObject) { if (typeof(T) == typeof(string)) { global::System.Runtime.InteropServices.McgMarshal.FreeHString(pObject); } else { global::System.Runtime.InteropServices.McgMarshal.ComSafeRelease(pObject); } } #endif #endregion #if ENABLE_MIN_WINRT [MethodImpl(MethodImplOptions.NoInlining)] public static unsafe IntPtr ActivateInstance(string typeName) { __ComObject target = McgMarshal.GetActivationFactory( typeName, InternalTypes.IActivationFactoryInternal ); IntPtr pIActivationFactoryInternalItf = target.QueryInterface_NoAddRef_Internal( InternalTypes.IActivationFactoryInternal, /* cacheOnly= */ false, /* throwOnQueryInterfaceFailure= */ true ); __com_IActivationFactoryInternal* pIActivationFactoryInternal = (__com_IActivationFactoryInternal*)pIActivationFactoryInternalItf; IntPtr pResult = default(IntPtr); int hr = CalliIntrinsics.StdCall__int( pIActivationFactoryInternal->pVtable->pfnActivateInstance, pIActivationFactoryInternal, &pResult ); GC.KeepAlive(target); if (hr < 0) { throw McgMarshal.GetExceptionForHR(hr, /* isWinRTScenario = */ true); } return pResult; } #endif [MethodImpl(MethodImplOptions.NoInlining)] public static IntPtr GetInterface( __ComObject obj, RuntimeTypeHandle typeHnd) { return obj.QueryInterface_NoAddRef_Internal( typeHnd); } [MethodImplAttribute(MethodImplOptions.NoInlining)] public static object GetDynamicAdapter(__ComObject obj, RuntimeTypeHandle requestedType, RuntimeTypeHandle existingType) { return obj.GetDynamicAdapter(requestedType, existingType); } [MethodImplAttribute(MethodImplOptions.NoInlining)] public static object GetDynamicAdapter(__ComObject obj, RuntimeTypeHandle requestedType) { return obj.GetDynamicAdapter(requestedType, default(RuntimeTypeHandle)); } #region "PInvoke Delegate" public static IntPtr GetStubForPInvokeDelegate(RuntimeTypeHandle delegateType, Delegate dele) { #if CORECLR throw new NotSupportedException(); #else return PInvokeMarshal.GetFunctionPointerForDelegate(dele); #endif } /// /// Retrieve the corresponding P/invoke instance from the stub /// public static Delegate GetPInvokeDelegateForStub(IntPtr pStub, RuntimeTypeHandle delegateType) { #if CORECLR if (pStub == IntPtr.Zero) return null; McgPInvokeDelegateData pInvokeDelegateData; if (!McgModuleManager.GetPInvokeDelegateData(delegateType, out pInvokeDelegateData)) { return null; } return CalliIntrinsics.Call__Delegate( pInvokeDelegateData.ForwardDelegateCreationStub, pStub ); #else return PInvokeMarshal.GetDelegateForFunctionPointer(pStub, delegateType); #endif } /// /// Retrieves the function pointer for the current open static delegate that is being called /// public static IntPtr GetCurrentCalleeOpenStaticDelegateFunctionPointer() { #if !RHTESTCL && PROJECTN return PInvokeMarshal.GetCurrentCalleeOpenStaticDelegateFunctionPointer(); #else throw new NotSupportedException(); #endif } /// /// Retrieves the current delegate that is being called /// public static T GetCurrentCalleeDelegate() where T : class // constraint can't be System.Delegate { #if !RHTESTCL && PROJECTN return PInvokeMarshal.GetCurrentCalleeDelegate(); #else throw new NotSupportedException(); #endif } #endregion } /// /// McgMarshal helpers exposed to be used by MCG /// public static partial class McgMarshal { public static object UnboxIfBoxed(object target) { return UnboxIfBoxed(target, null); } public static object UnboxIfBoxed(object target, string className) { // // If it is a managed wrapper, unbox it // object unboxedObj = McgComHelpers.UnboxManagedWrapperIfBoxed(target); if (unboxedObj != target) return unboxedObj; if (className == null) className = System.Runtime.InteropServices.McgComHelpers.GetRuntimeClassName(target); if (!String.IsNullOrEmpty(className)) { IntPtr unboxingStub; if (McgModuleManager.TryGetUnboxingStub(className, out unboxingStub)) { object ret = CalliIntrinsics.Call(unboxingStub, target); if (ret != null) return ret; } #if ENABLE_WINRT else if(McgModuleManager.UseDynamicInterop) { BoxingInterfaceKind boxingInterfaceKind; RuntimeTypeHandle[] genericTypeArgument; if (DynamicInteropBoxingHelpers.TryGetBoxingArgumentTypeHandleFromString(className, out boxingInterfaceKind, out genericTypeArgument)) { Debug.Assert(target is __ComObject); return DynamicInteropBoxingHelpers.Unboxing(boxingInterfaceKind, genericTypeArgument, target); } } #endif } return null; } internal static object BoxIfBoxable(object target) { return BoxIfBoxable(target, default(RuntimeTypeHandle)); } /// /// Given a boxed value type, return a wrapper supports the IReference interface /// /// /// You might want to specify how to box this. For example, any object[] derived array could /// potentially boxed as object[] if everything else fails /// internal static object BoxIfBoxable(object target, RuntimeTypeHandle typeHandleOverride) { RuntimeTypeHandle expectedTypeHandle = typeHandleOverride; if (expectedTypeHandle.Equals(default(RuntimeTypeHandle))) expectedTypeHandle = target.GetTypeHandle(); RuntimeTypeHandle boxingWrapperType; IntPtr boxingStub; int boxingPropertyType; if (McgModuleManager.TryGetBoxingWrapperType(expectedTypeHandle, target, out boxingWrapperType, out boxingPropertyType, out boxingStub)) { if (!boxingWrapperType.IsInvalid()) { // // IReference / IReferenceArray / IKeyValuePair // All these scenarios require a managed wrapper // // Allocate the object object refImplType = InteropExtensions.RuntimeNewObject(boxingWrapperType); if (boxingPropertyType >= 0) { Debug.Assert(refImplType is BoxedValue); BoxedValue boxed = InteropExtensions.UncheckedCast(refImplType); // Call ReferenceImpl.Initialize(obj, type); boxed.Initialize(target, boxingPropertyType); } else { Debug.Assert(refImplType is BoxedKeyValuePair); BoxedKeyValuePair boxed = InteropExtensions.UncheckedCast(refImplType); // IKeyValuePair<,>, call CLRIKeyValuePairImpl.Initialize(object obj); // IKeyValuePair<,>[], call CLRIKeyValuePairArrayImpl.Initialize(object obj); refImplType = boxed.Initialize(target); } return refImplType; } else { // // General boxing for projected types, such as System.Uri // return CalliIntrinsics.Call(boxingStub, target); } } return null; } } }