static object ComInterfaceToObjectInternal_NoManagedUnboxing( IntPtr pComItf, RuntimeTypeHandle interfaceType, RuntimeTypeHandle classTypeInSignature, CreateComObjectFlags flags) { if (pComItf == default(IntPtr)) { return(null); } // // Is this a CCW? // ComCallableObject ccw; if (ComCallableObject.TryGetCCW(pComItf, out ccw)) { return(ccw.TargetObject); } // // This pointer is not a CCW, but we need to do one additional check here for aggregation // In case the COM pointer is a interface implementation from native, but the outer object is a // managed object // IntPtr pComIdentityIUnknown = McgMarshal.ComQueryInterfaceNoThrow(pComItf, ref Interop.COM.IID_IUnknown); if (pComIdentityIUnknown == default(IntPtr)) { throw new InvalidCastException(); } try { // // Check whether the identity COM pointer to see if it is a aggregating CCW // if (ComCallableObject.TryGetCCW(pComIdentityIUnknown, out ccw)) { return(ccw.TargetObject); } // // Nope, not a CCW - let's go down our RCW creation code path // return(ComInterfaceToComObjectInternal( pComItf, pComIdentityIUnknown, interfaceType, classTypeInSignature, ContextCookie.Default, flags )); } finally { McgMarshal.ComRelease(pComIdentityIUnknown); } }
/// <summary> /// Returns whether the IUnknown* is a free-threaded COM object /// </summary> /// <param name="pUnknown"></param> internal static unsafe bool IsFreeThreaded(IntPtr pUnknown) { // // Does it support IAgileObject? // IntPtr pAgileObject = McgMarshal.ComQueryInterfaceNoThrow(pUnknown, ref Interop.COM.IID_IAgileObject); if (pAgileObject != default(IntPtr)) { // Anything that implements IAgileObject is considered to be free-threaded // NOTE: This doesn't necessarily mean that the object is free-threaded - it only means // we BELIEVE it is free-threaded McgMarshal.ComRelease_StdCall(pAgileObject); return(true); } IntPtr pMarshal = McgMarshal.ComQueryInterfaceNoThrow(pUnknown, ref Interop.COM.IID_IMarshal); if (pMarshal == default(IntPtr)) { return(false); } try { // // Check the un-marshaler // Interop.COM.__IMarshal *pIMarshalNativePtr = (Interop.COM.__IMarshal *)(void *) pMarshal; fixed(Guid *pGuid = &Interop.COM.IID_IUnknown) { Guid clsid; int hr = CalliIntrinsics.StdCall__int( pIMarshalNativePtr->vtbl->pfnGetUnmarshalClass, new IntPtr(pIMarshalNativePtr), pGuid, default(IntPtr), (uint)Interop.COM.MSHCTX.MSHCTX_INPROC, default(IntPtr), (uint)Interop.COM.MSHLFLAGS.MSHLFLAGS_NORMAL, &clsid); if (hr >= 0 && InteropExtensions.GuidEquals(ref clsid, ref Interop.COM.CLSID_InProcFreeMarshaler)) { // The un-marshaller is indeed the unmarshaler for the FTM so this object // is free threaded. return(true); } } return(false); } finally { McgMarshal.ComRelease_StdCall(pMarshal); } }
public static int QueryInterface(IntPtr pUnk, ref Guid iid, out IntPtr ppv) { int hr = 0; ppv = McgMarshal.ComQueryInterfaceNoThrow(pUnk, ref iid, out hr); #if CORECLR if (ppv == default(IntPtr)) { return(Marshal.QueryInterface(pUnk, ref iid, out ppv)); } #endif return(hr); }
/// <summary> /// Returns the existing RCW or create a new RCW from the COM interface pointer /// NOTE: Don't use this overload if you already have the identity IUnknown /// </summary> /// <param name="expectedContext"> /// The current context of this thread. If it is passed and is not Default, we'll check whether the /// returned RCW from cache matches this expected context. If it is not a match (from a different /// context, and is not free threaded), we'll go ahead ignoring the cached entry, and create a new /// RCW instead - which will always end up in the current context /// We'll skip the check if current == ContextCookie.Default. /// </param> internal static object ComInterfaceToComObject( IntPtr pComItf, RuntimeTypeHandle interfaceType, RuntimeTypeHandle classTypeInSigature, ContextCookie expectedContext, CreateComObjectFlags flags ) { Debug.Assert(expectedContext.IsDefault || expectedContext.IsCurrent); // // Get identity IUnknown for lookup // IntPtr pComIdentityIUnknown = McgMarshal.ComQueryInterfaceNoThrow(pComItf, ref Interop.COM.IID_IUnknown); if (pComIdentityIUnknown == default(IntPtr)) { throw new InvalidCastException(); } try { object obj = ComInterfaceToComObjectInternal( pComItf, pComIdentityIUnknown, interfaceType, classTypeInSigature, expectedContext, flags ); return(obj); } finally { McgMarshal.ComRelease(pComIdentityIUnknown); } }
/// <summary> /// This method returns a new Exception object given the HR value. /// /// 1. We check whether we have our own LanguageException associated with this hr. If so we simply use it since it helps preserve the stacktrace, message and type. /// This is done using GetLanguageException API on ILanguageExceptionErrorInfo from IRestrictedErrorInfo. Since ILanguageExceptionErrorInfo is available only on Windows Blue /// we can only do this WindowsBlue and above. In desktop CLR we could use GetErroInfo and check whether we have our IErroInfo and retrieve our own exception. /// For Win8 in .NET Native we simply create the exception using the RestrictedErrorInfo and hence only able to give the exception with restrictedErrorMsg. /// 2. In case we do not have the languageException we simply check RestrictedErrorInfo for errorMsg and create an exception using /// <errorMsg>\r\n<restrictedErrorMsg>. This is done for only windows blue. To be backward compatible we only use errorMsg for creating exception in win8. /// 3. PS - This class puts all the logic in try, catch block to ensure that none of the exception helpers /// throw exception themselves. /// </summary> /// <param name="hr"></param> /// <param name="isWinRTScenario"></param> internal static Exception GetExceptionForHRInternalNoThrow(int hr, bool isWinRTScenario, bool isClassicCOM) { Exception ex; IntPtr pRestrictedErrorInfo = IntPtr.Zero; try { if (TryGetRestrictedErrorInfo(out pRestrictedErrorInfo)) { // This is to check whether we need to give post win8 behavior or not. if (isWinRTScenario) { // Check whether the given IRestrictedErrorInfo object supports ILanguageExceptionErrorInfo IntPtr pLanguageExceptionErrorInfo = McgMarshal.ComQueryInterfaceNoThrow(pRestrictedErrorInfo, ref Interop.COM.IID_ILanguageExceptionErrorInfo); if (pLanguageExceptionErrorInfo != IntPtr.Zero) { // We have an LanguageExceptionErrorInfo. IntPtr pUnk; __com_ILanguageExceptionErrorInfo *pComLanguageExceptionErrorInfo = (__com_ILanguageExceptionErrorInfo *)pLanguageExceptionErrorInfo; int result = CalliIntrinsics.StdCall <int>(pComLanguageExceptionErrorInfo->pVtable->pfnGetLanguageException, pLanguageExceptionErrorInfo, out pUnk); McgMarshal.ComSafeRelease(pLanguageExceptionErrorInfo); if (result >= 0 && pUnk != IntPtr.Zero) { try { // Check whether the given pUnk is a managed exception. ComCallableObject ccw; if (ComCallableObject.TryGetCCW(pUnk, out ccw)) { return(ccw.TargetObject as Exception); } } finally { McgMarshal.ComSafeRelease(pUnk); } } } } String message = null, errorInfoReference = null; string errMsg, errCapSid, resErrMsg; int errHr; object restrictedErrorInfo = null; bool hasErrorInfo = false; if (RestrictedErrorInfoHelper.GetErrorDetails(pRestrictedErrorInfo, out errMsg, out errHr, out resErrMsg, out errCapSid) && errHr == hr) { // RestrictedErrorInfo details can be used since the pRestrictedErrorInfo has the same hr value as the hr returned by the native code. // We are in windows blue or above and hence the exceptionMsg is errMsg + "\r\n" + resErrMsg message = String.IsNullOrEmpty(resErrMsg) ? errMsg : errMsg + "\r\n" + resErrMsg; RestrictedErrorInfoHelper.GetReference(pRestrictedErrorInfo, out errorInfoReference); restrictedErrorInfo = McgMarshal.ComInterfaceToObject(pRestrictedErrorInfo, InternalTypes.IRestrictedErrorInfo); hasErrorInfo = true; } if (hr == Interop.COM.RO_E_CLOSED && isWinRTScenario) { hr = Interop.COM.COR_E_OBJECTDISPOSED; } // Now we simply need to set the description and the resDescription by adding an internal method. ex = GetMappingExceptionForHR(hr, message, isClassicCOM, hasErrorInfo); if (restrictedErrorInfo != null) { InteropExtensions.AddExceptionDataForRestrictedErrorInfo(ex, resErrMsg, errorInfoReference, errCapSid, restrictedErrorInfo); } return(ex); } } catch (Exception) { // We can't do any thing here and hence we swallow the exception and get the corresponding hr. } finally { McgMarshal.ComSafeRelease(pRestrictedErrorInfo); } // We could not find any restrictedErrorInfo associated with this object and hence we simply use the hr to create the exception. return(GetMappingExceptionForHR(hr, null, isClassicCOM, hasErrorInfo: false)); }
/// <summary> /// Returns the existing RCW or create a new RCW from the COM interface pointer /// NOTE: This does not do any unboxing at all. /// </summary> /// <param name="expectedContext"> /// The current context of this thread. If it is passed and is not Default, we'll check whether the /// returned RCW from cache matches this expected context. If it is not a match (from a different /// context, and is not free threaded), we'll go ahead ignoring the cached entry, and create a new /// RCW instead - which will always end up in the current context /// We'll skip the check if current == ContextCookie.Default. /// </param> private static object ComInterfaceToComObjectInternal_NoCache( IntPtr pComItf, IntPtr pComIdentityIUnknown, RuntimeTypeHandle interfaceType, RuntimeTypeHandle classTypeInSignature, ContextCookie expectedContext, CreateComObjectFlags flags, out string className ) { className = null; // // Lookup RCW in global RCW cache based on the identity IUnknown // __ComObject comObject = ComObjectCache.Lookup(pComIdentityIUnknown); if (comObject != null) { bool useThisComObject = true; if (!expectedContext.IsDefault) { // // Make sure the returned RCW matches the context we specify (if any) // if (!comObject.IsFreeThreaded && !comObject.ContextCookie.Equals(expectedContext)) { // // This is a mismatch. // We only care about context for WinRT factory RCWs (which is the only place we are // passing in the context right now). // When we get back a WinRT factory RCW created in a different context. This means the // factory is a singleton, and the returned IActivationFactory could be either one of // the following: // 1) A raw pointer, and it acts like a free threaded object // 2) A proxy that is used across different contexts. It might maintain a list of contexts // that it is marshaled to, and will fail to be called if it is not marshaled to this // context yet. // // In this case, it is unsafe to use this RCW in this context and we should proceed // to create a duplicated one instead. It might make sense to have a context-sensitive // RCW cache but I don't think this case will be common enough to justify it // // @TODO: Check for DCOM proxy as well useThisComObject = false; } } if (useThisComObject) { // // We found one - AddRef and return // comObject.AddRef(); return(comObject); } } string winrtClassName = null; bool isSealed = false; if (!classTypeInSignature.IsNull()) { isSealed = classTypeInSignature.IsSealed(); } // // Only look at runtime class name if the class type in signature is not sealed // NOTE: In the case of System.Uri, we are not pass the class type, only the interface // if (!isSealed && (flags & CreateComObjectFlags.SkipTypeResolutionAndUnboxing) == 0) { IntPtr pInspectable; bool needRelease = false; if (interfaceType.IsSupportIInspectable()) { // // Use the interface pointer as IInspectable as we know it is indeed a WinRT interface that // derives from IInspectable // pInspectable = pComItf; } else if ((flags & CreateComObjectFlags.IsWinRTObject) != 0) { // // Otherwise, if someone tells us that this is a WinRT object, but we don't have a // IInspectable interface at hand, we'll QI for it // pInspectable = McgMarshal.ComQueryInterfaceNoThrow(pComItf, ref Interop.COM.IID_IInspectable); needRelease = true; } else { pInspectable = default(IntPtr); } try { if (pInspectable != default(IntPtr)) { className = McgComHelpers.GetRuntimeClassName(pInspectable); winrtClassName = className; } } finally { if (needRelease && pInspectable != default(IntPtr)) { McgMarshal.ComRelease(pInspectable); pInspectable = default(IntPtr); } } } // // 1. Prefer using the class returned from GetRuntimeClassName // 2. Otherwise use the class (if there) in the signature // 3. Out of options - create __ComObject // RuntimeTypeHandle classTypeToCreateRCW = default(RuntimeTypeHandle); RuntimeTypeHandle interfaceTypeFromName = default(RuntimeTypeHandle); if (!String.IsNullOrEmpty(className)) { if (!McgModuleManager.TryGetClassTypeFromName(className, out classTypeToCreateRCW)) { // // If we can't find the class name in our map, try interface as well // Such as IVector<Int32> // This apparently won't work if we haven't seen the interface type in MCG // McgModuleManager.TryGetInterfaceTypeFromName(className, out interfaceTypeFromName); } } if (classTypeToCreateRCW.IsNull()) { classTypeToCreateRCW = classTypeInSignature; } // Use identity IUnknown to create the new RCW // @TODO: Transfer the ownership of ref count to the RCW if (classTypeToCreateRCW.IsNull()) { // // Create a weakly typed RCW because we have no information about this particular RCW // @TODO - what if this RCW is not seen by MCG but actually exists in WinMD and therefore we // are missing GCPressure and ComMarshallingType information for this object? // comObject = new __ComObject(pComIdentityIUnknown, default(RuntimeTypeHandle)); } else { // // Create a strongly typed RCW based on RuntimeTypeHandle // comObject = CreateComObjectInternal(classTypeToCreateRCW, pComIdentityIUnknown); // Use identity IUnknown to create the new RCW } #if DEBUG // // Remember the runtime class name for debugging purpose // This way you can tell what the class name is, even when we failed to create a strongly typed // RCW for it // comObject.m_runtimeClassName = className; #endif // // Make sure we QI for that interface // if (!interfaceType.IsNull()) { comObject.QueryInterface_NoAddRef_Internal(interfaceType, /* cacheOnly= */ false, /* throwOnQueryInterfaceFailure= */ false); } return(comObject); }