/// <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); } }