// 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