diff options
-rw-r--r-- | src/System.Private.CoreLib/src/System/RtType.cs | 199 | ||||
-rw-r--r-- | src/vm/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/vm/callsiteinspect.cpp | 516 | ||||
-rw-r--r-- | src/vm/callsiteinspect.h | 45 | ||||
-rw-r--r-- | src/vm/clrtocomcall.cpp | 316 | ||||
-rw-r--r-- | src/vm/metasig.h | 1 | ||||
-rw-r--r-- | src/vm/mlinfo.h | 1 | ||||
-rw-r--r-- | src/vm/mscorlib.h | 37 | ||||
-rw-r--r-- | src/vm/stdinterfaces.cpp | 21 | ||||
-rw-r--r-- | src/vm/stdinterfaces.h | 8 |
10 files changed, 1088 insertions, 58 deletions
diff --git a/src/System.Private.CoreLib/src/System/RtType.cs b/src/System.Private.CoreLib/src/System/RtType.cs index 0e5cf8104e..1612be8ba3 100644 --- a/src/System.Private.CoreLib/src/System/RtType.cs +++ b/src/System.Private.CoreLib/src/System/RtType.cs @@ -3966,8 +3966,6 @@ namespace System return members; } -#if FEATURE_COMINTEROP -#endif [DebuggerStepThroughAttribute] [Diagnostics.DebuggerHidden] public override object InvokeMember( @@ -4009,7 +4007,6 @@ namespace System } #endregion - #region COM Interop #if FEATURE_COMINTEROP && FEATURE_USE_LCID if (target != null && target.GetType().IsCOMObject) { @@ -4048,7 +4045,6 @@ namespace System } } #endif // FEATURE_COMINTEROP && FEATURE_USE_LCID - #endregion #region Check that any named parameters are not null if (namedParams != null && Array.IndexOf(namedParams, null) != -1) @@ -4883,8 +4879,199 @@ namespace System #endregion - #region COM - #endregion +#if FEATURE_COMINTEROP + private Object ForwardCallToInvokeMember( + String memberName, + BindingFlags flags, + Object target, + Object[] aArgs, // in/out - only byref values are in a valid state upon return + bool[] aArgsIsByRef, + int[] aArgsWrapperTypes, // _maybe_null_ + Type[] aArgsTypes, + Type retType) + { + Debug.Assert( + aArgs.Length == aArgsIsByRef.Length + && aArgs.Length == aArgsTypes.Length + && (aArgsWrapperTypes == null || aArgs.Length == aArgsWrapperTypes.Length), "Input arrays should all be of the same length"); + + int cArgs = aArgs.Length; + + // Handle arguments that are passed as ByRef and those + // arguments that need to be wrapped. + ParameterModifier[] aParamMod = null; + if (cArgs > 0) + { + ParameterModifier paramMod = new ParameterModifier(cArgs); + for (int i = 0; i < cArgs; i++) + { + paramMod[i] = aArgsIsByRef[i]; + } + + aParamMod = new ParameterModifier[] { paramMod }; + if (aArgsWrapperTypes != null) + { + WrapArgsForInvokeCall(aArgs, aArgsWrapperTypes); + } + } + + // For target invocation exceptions, the exception is wrapped. + flags |= BindingFlags.DoNotWrapExceptions; + Object ret = InvokeMember(memberName, flags, null, target, aArgs, aParamMod, null, null); + + // Convert each ByRef argument that is _not_ of the proper type to + // the parameter type. + for (int i = 0; i < cArgs; i++) + { + // Determine if the parameter is ByRef. + if (aParamMod[0][i] && aArgs[i] != null) + { + Type argType = aArgsTypes[i]; + if (!Object.ReferenceEquals(argType, aArgs[i].GetType())) + { + aArgs[i] = ForwardCallBinder.ChangeType(aArgs[i], argType, null); + } + } + } + + // If the return type is _not_ of the proper type, then convert it. + if (ret != null) + { + if (!Object.ReferenceEquals(retType, ret.GetType())) + { + ret = ForwardCallBinder.ChangeType(ret, retType, null); + } + } + + return ret; + } + + private void WrapArgsForInvokeCall(Object[] aArgs, int[] aArgsWrapperTypes) + { + int cArgs = aArgs.Length; + for (int i = 0; i < cArgs; i++) + { + if (aArgsWrapperTypes[i] == 0) + { + continue; + } + + if (((DispatchWrapperType)aArgsWrapperTypes[i]).HasFlag(DispatchWrapperType.SafeArray)) + { + Type wrapperType = null; + bool isString = false; + + // Determine the type of wrapper to use. + switch ((DispatchWrapperType)aArgsWrapperTypes[i] & ~DispatchWrapperType.SafeArray) + { + case DispatchWrapperType.Unknown: + wrapperType = typeof(UnknownWrapper); + break; + case DispatchWrapperType.Dispatch: + wrapperType = typeof(DispatchWrapper); + break; + case DispatchWrapperType.Error: + wrapperType = typeof(ErrorWrapper); + break; + case DispatchWrapperType.Currency: + wrapperType = typeof(CurrencyWrapper); + break; + case DispatchWrapperType.BStr: + wrapperType = typeof(BStrWrapper); + isString = true; + break; + default: + Debug.Assert(false, "[RuntimeType.WrapArgsForInvokeCall]Invalid safe array wrapper type specified."); + break; + } + + // Allocate the new array of wrappers. + Array oldArray = (Array)aArgs[i]; + int numElems = oldArray.Length; + Object[] newArray = (Object[])Array.UnsafeCreateInstance(wrapperType, numElems); + + // Retrieve the ConstructorInfo for the wrapper type. + ConstructorInfo wrapperCons; + if (isString) + { + wrapperCons = wrapperType.GetConstructor(new Type[] {typeof(String)}); + } + else + { + wrapperCons = wrapperType.GetConstructor(new Type[] {typeof(Object)}); + } + + // Wrap each of the elements of the array. + for (int currElem = 0; currElem < numElems; currElem++) + { + if(isString) + { + newArray[currElem] = wrapperCons.Invoke(new Object[] {(String)oldArray.GetValue(currElem)}); + } + else + { + newArray[currElem] = wrapperCons.Invoke(new Object[] {oldArray.GetValue(currElem)}); + } + } + + // Update the argument. + aArgs[i] = newArray; + } + else + { + // Determine the wrapper to use and then wrap the argument. + switch ((DispatchWrapperType)aArgsWrapperTypes[i]) + { + case DispatchWrapperType.Unknown: + aArgs[i] = new UnknownWrapper(aArgs[i]); + break; + case DispatchWrapperType.Dispatch: + aArgs[i] = new DispatchWrapper(aArgs[i]); + break; + case DispatchWrapperType.Error: + aArgs[i] = new ErrorWrapper(aArgs[i]); + break; + case DispatchWrapperType.Currency: + aArgs[i] = new CurrencyWrapper(aArgs[i]); + break; + case DispatchWrapperType.BStr: + aArgs[i] = new BStrWrapper((String)aArgs[i]); + break; + default: + Debug.Assert(false, "[RuntimeType.WrapArgsForInvokeCall]Invalid wrapper type specified."); + break; + } + } + } + } + + private static OleAutBinder s_ForwardCallBinder; + private OleAutBinder ForwardCallBinder + { + get + { + // Synchronization is not required. + if (s_ForwardCallBinder == null) + s_ForwardCallBinder = new OleAutBinder(); + + return s_ForwardCallBinder; + } + } + + [Flags] + private enum DispatchWrapperType : int + { + // This enum must stay in sync with the DispatchWrapperType enum defined in MLInfo.h + Unknown = 0x00000001, + Dispatch = 0x00000002, + // Record = 0x00000004, + Error = 0x00000008, + Currency = 0x00000010, + BStr = 0x00000020, + SafeArray = 0x00010000 + } + +#endif // FEATURE_COMINTEROP } #region Library diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index 54c73cce44..842fe31f0a 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -286,6 +286,7 @@ set(VM_SOURCES_WKS cachelinealloc.cpp callcounter.cpp callhelpers.cpp + callsiteinspect.cpp ceemain.cpp clrconfignative.cpp clrex.cpp @@ -402,6 +403,7 @@ set(VM_HEADERS_WKS cachelinealloc.h callcounter.h callhelpers.h + callsiteinspect.h ceemain.h clrconfignative.h clrex.h diff --git a/src/vm/callsiteinspect.cpp b/src/vm/callsiteinspect.cpp new file mode 100644 index 0000000000..953714d910 --- /dev/null +++ b/src/vm/callsiteinspect.cpp @@ -0,0 +1,516 @@ +// 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. + +#include "common.h" +#include "object.h" +#include "callsiteinspect.h" + +namespace +{ + // Given a frame and value, get a reference to the object + OBJECTREF GetOBJECTREFFromStack( + _In_ FramedMethodFrame *frame, + _In_ PVOID val, + _In_ const CorElementType eType, + _In_ TypeHandle ty, + _In_ BOOL fIsByRef) + { + CONTRACT(OBJECTREF) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(frame)); + PRECONDITION(CheckPointer(val)); + } + CONTRACT_END; + + // Value types like Nullable<T> have special unboxing semantics + if (eType == ELEMENT_TYPE_VALUETYPE) + { + // box the value class + _ASSERTE(ty.GetMethodTable()->IsValueType() || ty.GetMethodTable()->IsEnum()); + + MethodTable* pMT = ty.GetMethodTable(); + + // What happens when the type contains a stack pointer? + _ASSERTE(!pMT->IsByRefLike()); + + PVOID* pVal = (PVOID *)val; + if (!fIsByRef) + { + val = StackElemEndianessFixup(val, pMT->GetNumInstanceFieldBytes()); + pVal = &val; + } + + RETURN (pMT->FastBox(pVal)); + } + + switch (CorTypeInfo::GetGCType(eType)) + { + case TYPE_GC_NONE: + { + if (ELEMENT_TYPE_PTR == eType) + COMPlusThrow(kNotSupportedException); + + MethodTable *pMT = MscorlibBinder::GetElementType(eType); + + OBJECTREF pObj = pMT->Allocate(); + if (fIsByRef) + { + val = *((PVOID *)val); + } + else + { + val = StackElemEndianessFixup(val, CorTypeInfo::Size(eType)); + } + + void *pDest = pObj->UnBox(); + +#ifdef COM_STUBS_SEPARATE_FP_LOCATIONS + if (!fIsByRef + && (ELEMENT_TYPE_R4 == eType || ELEMENT_TYPE_R8 == eType) + && frame != nullptr + && !TransitionBlock::IsStackArgumentOffset(static_cast<int>((TADDR) val - frame->GetTransitionBlock()))) + { + if (ELEMENT_TYPE_R4 == eType) + *(UINT32*)pDest = (UINT32)FPSpillToR4(val); + else + *(UINT64*)pDest = (UINT64)FPSpillToR8(val); + } + else +#endif // COM_STUBS_SEPARATE_FP_LOCATIONS + { + memcpyNoGCRefs(pDest, val, CorTypeInfo::Size(eType)); + } + + RETURN (pObj); + } + + case TYPE_GC_REF: + if (fIsByRef) + val = *((PVOID *)val); + RETURN (ObjectToOBJECTREF(*(Object **)val)); + + default: + COMPlusThrow(kInvalidOperationException, W("InvalidOperation_TypeCannotBeBoxed")); + } + } + + struct ArgDetails + { + int Offset; + BOOL IsByRef; + CorElementType ElementType; + TypeHandle Type; + }; + + ArgDetails GetArgDetails( + _In_ FramedMethodFrame *frame, + _In_ ArgIterator &pArgIter) + { + CONTRACT(ArgDetails) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(frame)); + } + CONTRACT_END; + + ArgDetails details{}; + details.Offset = pArgIter.GetNextOffset(); + details.ElementType = pArgIter.GetArgType(); + +#ifdef COM_STUBS_SEPARATE_FP_LOCATIONS + // BUGBUG do we need to handle this? + if ((ELEMENT_TYPE_R4 == details.ElementType || ELEMENT_TYPE_R8 == details.ElementType) + && TransitionBlock::IsArgumentRegisterOffset(details.Offset)) + { + int iFPArg = TransitionBlock::GetArgumentIndexFromOffset(details.Offset); + details.Offset = static_cast<int>(frame->GetFPArgOffset(iFPArg)); + } +#endif // COM_STUBS_SEPARATE_FP_LOCATIONS + + // Get the TypeHandle for the argument's type. + MetaSig *pSig = pArgIter.GetSig(); + details.Type = pSig->GetLastTypeHandleThrowing(); + + if (details.ElementType == ELEMENT_TYPE_BYREF) + { + details.IsByRef = TRUE; + // If this is a by-ref arg, GetOBJECTREFFromStack() will dereference "addr" to + // get the real argument address. Dereferencing now will open a gc hole if "addr" + // points into the gc heap, and we trigger gc between here and the point where + // we return the arguments. + + TypeHandle tycopy; + details.ElementType = pSig->GetByRefType(&tycopy); + if (details.ElementType == ELEMENT_TYPE_VALUETYPE) + details.Type = tycopy; + } +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + else if (details.ElementType == ELEMENT_TYPE_VALUETYPE) + { + details.IsByRef = ArgIterator::IsArgPassedByRef(details.Type); + } +#endif // ENREGISTERED_PARAMTYPE_MAXSIZE + + RETURN (details); + } + + INT64 CopyOBJECTREFToStack( + _In_ OBJECTREF *src, + _In_opt_ PVOID pvDest, + _In_ CorElementType typ, + _In_ TypeHandle ty, + _In_ MetaSig *pSig, + _In_ BOOL fCopyClassContents) + { + // Use local to ensure proper alignment + INT64 ret = 0; + + CONTRACT(INT64) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pvDest, NULL_OK)); + PRECONDITION(CheckPointer(pSig)); + PRECONDITION(typ != ELEMENT_TYPE_VOID); + } + CONTRACT_END; + + if (fCopyClassContents) + { + // We have to copy the contents of a value class to pvDest + + // write unboxed version back to memory provided by the caller + if (pvDest) + { + if (ty.IsNull()) + ty = pSig->GetRetTypeHandleThrowing(); + + _ASSERTE((*src) != NULL || Nullable::IsNullableType(ty)); +#ifdef PLATFORM_UNIX + // Unboxing on non-Windows ABIs must be special cased + COMPlusThrowHR(COR_E_NOTSUPPORTED); +#else + ty.GetMethodTable()->UnBoxIntoUnchecked(pvDest, (*src)); +#endif + + // return the object so it can be stored in the frame and + // propagated to the root set + *(OBJECTREF*)&ret = (*src); + } + } + else if (CorTypeInfo::IsObjRef(typ)) + { + // We have a real OBJECTREF + + // Check if it is an OBJECTREF (from the GC heap) + if (pvDest) + SetObjectReferenceUnchecked((OBJECTREF *)pvDest, *src); + + *(OBJECTREF*)&ret = (*src); + } + else + { + // We have something that does not have a return buffer associated. + + // Note: this assert includes ELEMENT_TYPE_VALUETYPE because for enums, + // ArgIterator::HasRetBuffArg() returns 'false'. This occurs because the + // normalized type for enums is ELEMENT_TYPE_I4 even though + // MetaSig::GetReturnType() returns ELEMENT_TYPE_VALUETYPE. + // Almost all ELEMENT_TYPE_VALUETYPEs will go through the copy class + // contents codepath above. + // Also, CorTypeInfo::IsPrimitiveType() does not check for IntPtr, UIntPtr + // hence we have ELEMENT_TYPE_I and ELEMENT_TYPE_U. + _ASSERTE( + CorTypeInfo::IsPrimitiveType(typ) + || (typ == ELEMENT_TYPE_VALUETYPE) + || (typ == ELEMENT_TYPE_I) + || (typ == ELEMENT_TYPE_U) + || (typ == ELEMENT_TYPE_FNPTR)); + + // For a "ref int" arg, if a nasty sink replaces the boxed int with + // a null OBJECTREF, this is where we check. We need to be uniform + // in our policy w.r.t. this (throw vs ignore). + // The branch above throws. + if ((*src) != NULL) + { + PVOID srcData = (*src)->GetData(); + int cbsize = gElementTypeInfo[typ].m_cbSize; + decltype(ret) retBuff; + + // ElementTypeInfo.m_cbSize can be less than zero for cases that need + // special handling (e.g. value types) to be sure of the size (see siginfo.cpp). + // The type handle has the actual byte count, so we look there for such cases. + if (cbsize < 0) + { + if (ty.IsNull()) + ty = pSig->GetRetTypeHandleThrowing(); + + _ASSERTE(!ty.IsNull()); + cbsize = ty.GetSize(); + + // Assert the value class fits in the buffer + _ASSERTE(cbsize <= (int) sizeof(retBuff)); + + // Unbox value into a local buffer, this covers the Nullable<T> case. + ty.GetMethodTable()->UnBoxIntoUnchecked(&retBuff, *src); + + srcData = &retBuff; + } + + if (pvDest) + memcpyNoGCRefs(pvDest, srcData, cbsize); + + // need to sign-extend signed types + bool fEndianessFixup = false; + switch (typ) + { + case ELEMENT_TYPE_I1: + ret = *(INT8*)srcData; + fEndianessFixup = true; + break; + case ELEMENT_TYPE_I2: + ret = *(INT16*)srcData; + fEndianessFixup = true; + break; + case ELEMENT_TYPE_I4: + ret = *(INT32*)srcData; + fEndianessFixup = true; + break; + default: + memcpyNoGCRefs(StackElemEndianessFixup(&ret, cbsize), srcData, cbsize); + break; + } + +#if !defined(_WIN64) && BIGENDIAN + if (fEndianessFixup) + ret <<= 32; +#endif + } + } + + RETURN (ret); + } +} + +void CallsiteInspect::GetCallsiteArgs( + _In_ CallsiteDetails &callsite, + _Outptr_ PTRARRAYREF *args, + _Outptr_ BOOLARRAYREF *argsIsByRef, + _Outptr_ PTRARRAYREF *argsTypes) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(args)); + PRECONDITION(CheckPointer(argsIsByRef)); + PRECONDITION(CheckPointer(argsTypes)); + } + CONTRACTL_END; + + struct _gc + { + PTRARRAYREF Args; + PTRARRAYREF ArgsTypes; + BOOLARRAYREF ArgsIsByRef; + OBJECTREF CurrArgType; + OBJECTREF CurrArg; + } gc; + ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + { + // Ensure the sig is in a known state + callsite.MetaSig.Reset(); + + // scan the sig for the argument count + INT32 numArgs = callsite.MetaSig.NumFixedArgs(); + if (callsite.IsDelegate) + numArgs -= 2; // Delegates have 2 implicit additional arguments + + // Allocate all needed arrays for callsite arg details + gc.Args = (PTRARRAYREF)AllocateObjectArray(numArgs, g_pObjectClass); + MethodTable *typeMT = MscorlibBinder::GetClass(CLASS__TYPE); + gc.ArgsTypes = (PTRARRAYREF)AllocateObjectArray(numArgs, typeMT); + gc.ArgsIsByRef = (BOOLARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_BOOLEAN, numArgs); + + ArgIterator iter{ &callsite.MetaSig }; + for (int index = 0; index < numArgs; index++) + { + ArgDetails details = GetArgDetails(callsite.Frame, iter); + PVOID addr = (LPBYTE)callsite.Frame->GetTransitionBlock() + details.Offset; + + // How do we handle pointer types? + _ASSERTE(details.ElementType != ELEMENT_TYPE_PTR); + + gc.CurrArg = GetOBJECTREFFromStack( + callsite.Frame, + addr, + details.ElementType, + details.Type, + details.IsByRef); + + // Store argument + gc.Args->SetAt(index, gc.CurrArg); + + // Record the argument's type + gc.CurrArgType = details.Type.GetManagedClassObject(); + _ASSERTE(gc.CurrArgType != NULL); + gc.ArgsTypes->SetAt(index, gc.CurrArgType); + + // Record if the argument is ByRef + *((UCHAR*)gc.ArgsIsByRef->GetDataPtr() + index) = (!!details.IsByRef); + } + } + GCPROTECT_END(); + + // Return details + *args = gc.Args; + *argsTypes = gc.ArgsTypes; + *argsIsByRef = gc.ArgsIsByRef; +} + +void CallsiteInspect::PropagateOutParametersBackToCallsite( + _In_ PTRARRAYREF outArgs, + _In_ OBJECTREF retVal, + _In_ CallsiteDetails &callsite) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + struct _gc + { + OBJECTREF RetVal; + PTRARRAYREF OutArgs; + OBJECTREF CurrArg; + } gc; + ZeroMemory(&gc, sizeof(gc)); + gc.OutArgs = outArgs; + gc.RetVal = retVal; + GCPROTECT_BEGIN(gc); + { + FramedMethodFrame *frame = callsite.Frame; + const INT32 flags = callsite.Flags; + MetaSig *pSig = &callsite.MetaSig; + pSig->Reset(); // Ensure the sig is in a known state + + // Construct an ArgIterator from the sig + ArgIterator argit{ pSig }; + + // Propagate the return value only if the call is not a constructor call + // and the return type is non-void + if ((flags & CallsiteDetails::Ctor) == 0 + && pSig->GetReturnType() != ELEMENT_TYPE_VOID) + { + if (argit.HasRetBuffArg()) + { + // Copy from RetVal into the retBuff. + INT64 retVal = CopyOBJECTREFToStack( + &gc.RetVal, + *(void**)(frame->GetTransitionBlock() + argit.GetRetBuffArgOffset()), + pSig->GetReturnType(), + TypeHandle{}, + pSig, + TRUE /* should copy */); + + // Copy the return value + *(ARG_SLOT *)(frame->GetReturnValuePtr()) = retVal; + } +#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE + else if (argit.HasNonStandardByvalReturn()) + { + // In these cases, put the pointer to the return buffer into + // the frame's return value slot. + CopyOBJECTREFToStack( + &gc.RetVal, + frame->GetReturnValuePtr(), + pSig->GetReturnType(), + TypeHandle(), + pSig, + TRUE /* should copy */); + } +#endif // ENREGISTERED_RETURNTYPE_MAXSIZE + else + { + // There is no separate return buffer, + // the retVal should fit in an INT64. + INT64 retVal = CopyOBJECTREFToStack( + &gc.RetVal, + nullptr, + pSig->GetReturnType(), + TypeHandle{}, + pSig, + FALSE /* should copy */); + + // Copy the return value + *(ARG_SLOT *)(frame->GetReturnValuePtr()) = retVal; + } + } + + // Refetch all the variables as GC could have happened + // after copying the return value. + UINT32 cOutArgs = (gc.OutArgs != NULL) ? gc.OutArgs->GetNumComponents() : 0; + if (cOutArgs > 0) + { + MetaSig syncSig{ callsite.MethodDesc }; + MetaSig *pSyncSig = nullptr; + + if (flags & CallsiteDetails::EndInvoke) + pSyncSig = &syncSig; + + PVOID *argAddr; + for (UINT32 i = 0; i < cOutArgs; ++i) + { + // Determine the address of the argument + if (pSyncSig) + { + CorElementType typ = pSyncSig->NextArg(); + if (typ == ELEMENT_TYPE_END) + break; + + if (typ != ELEMENT_TYPE_BYREF) + continue; + + argAddr = reinterpret_cast<PVOID *>(frame->GetTransitionBlock() + argit.GetNextOffset()); + } + else + { + int ofs = argit.GetNextOffset(); + if (ofs == TransitionBlock::InvalidOffset) + break; + + if (argit.GetArgType() != ELEMENT_TYPE_BYREF) + continue; + + argAddr = reinterpret_cast<PVOID *>(frame->GetTransitionBlock() + ofs); + } + + TypeHandle ty; + CorElementType brType = pSig->GetByRefType(&ty); + + gc.CurrArg = gc.OutArgs->GetAt(i); + CopyOBJECTREFToStack( + &gc.CurrArg, + *argAddr, + brType, + ty, + pSig, + ty.IsNull() ? FALSE : ty.IsValueType()); + } + } + } + GCPROTECT_END(); +} diff --git a/src/vm/callsiteinspect.h b/src/vm/callsiteinspect.h new file mode 100644 index 0000000000..8a2d7cca49 --- /dev/null +++ b/src/vm/callsiteinspect.h @@ -0,0 +1,45 @@ +// 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. + +struct CallsiteDetails +{ + // The signature of the current call + MetaSig MetaSig; + + // The current call frame + FramedMethodFrame *Frame; + + // The relevant method for the callsite + MethodDesc *MethodDesc; + + // Is the callsite for a delegate + // Note the relevant method may _not_ be a delegate + BOOL IsDelegate; + + // Flags for callsite + enum + { + None = 0x0, + BeginInvoke = 0x01, + EndInvoke = 0x02, + Ctor = 0x04, + }; + INT32 Flags; +}; + +namespace CallsiteInspect +{ + // Get all arguments and associated argument details at the supplied callsite + void GetCallsiteArgs( + _In_ CallsiteDetails &callsite, + _Outptr_ PTRARRAYREF *args, + _Outptr_ BOOLARRAYREF *argsIsByRef, + _Outptr_ PTRARRAYREF *argsTypes); + + // Properly propagate out parameters + void PropagateOutParametersBackToCallsite( + _In_ PTRARRAYREF outParams, + _In_ OBJECTREF retVal, + _In_ CallsiteDetails &callsite); +} diff --git a/src/vm/clrtocomcall.cpp b/src/vm/clrtocomcall.cpp index 372cf6f30a..e56c4b4159 100644 --- a/src/vm/clrtocomcall.cpp +++ b/src/vm/clrtocomcall.cpp @@ -16,6 +16,7 @@ #include "excep.h" #include "clrtocomcall.h" #include "siginfo.hpp" +#include "comdelegate.h" #include "comcallablewrapper.h" #include "runtimecallablewrapper.h" #include "dllimport.h" @@ -25,6 +26,7 @@ #include "reflectioninvocation.h" #include "mdaassistants.h" #include "sigbuilder.h" +#include "callsiteinspect.h" #define DISPATCH_INVOKE_SLOT 6 @@ -629,6 +631,314 @@ UINT32 CLRToCOMEventCallWorker(ComPlusMethodFrame* pFrame, ComPlusCallMethodDesc return 0; } +CallsiteDetails CreateCallsiteDetails(_In_ FramedMethodFrame *pFrame) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pFrame)); + } + CONTRACTL_END; + + MethodDesc *pMD = pFrame->GetFunction(); + _ASSERTE(!pMD->ContainsGenericVariables() && pMD->IsRuntimeMethodHandle()); + + const BOOL fIsDelegate = pMD->GetMethodTable()->IsDelegate(); + _ASSERTE(!fIsDelegate && pMD->IsRuntimeMethodHandle()); + + MethodDesc *pDelegateMD = nullptr; + INT32 callsiteFlags = CallsiteDetails::None; + if (fIsDelegate) + { + // Gather details on the delegate itself + DelegateEEClass* delegateCls = (DelegateEEClass*)pMD->GetMethodTable()->GetClass(); + _ASSERTE(pFrame->GetThis()->GetMethodTable()->IsDelegate()); + + if (pMD == delegateCls->m_pBeginInvokeMethod.GetValue()) + { + callsiteFlags |= CallsiteDetails::BeginInvoke; + } + else + { + _ASSERTE(pMD == delegateCls->m_pEndInvokeMethod.GetValue()); + callsiteFlags |= CallsiteDetails::EndInvoke; + } + + pDelegateMD = pMD; + + // Get at the underlying method desc for this frame + pMD = COMDelegate::GetMethodDesc(pFrame->GetThis()); + _ASSERTE(pDelegateMD != nullptr + && pMD != nullptr + && !pMD->ContainsGenericVariables() + && pMD->IsRuntimeMethodHandle()); + } + + if (pMD->IsCtor()) + callsiteFlags |= CallsiteDetails::Ctor; + + Signature signature; + Module *pModule; + SigTypeContext typeContext; + + if (fIsDelegate) + { + _ASSERTE(pDelegateMD != nullptr); + signature = pDelegateMD->GetSignature(); + pModule = pDelegateMD->GetModule(); + + // If the delegate is generic, pDelegateMD may not represent the exact instantiation so we recover it from 'this'. + SigTypeContext::InitTypeContext(pFrame->GetThis()->GetMethodTable()->GetInstantiation(), Instantiation{}, &typeContext); + } + else if (pMD->IsVarArg()) + { + VASigCookie *pVACookie = pFrame->GetVASigCookie(); + signature = pVACookie->signature; + pModule = pVACookie->pModule; + SigTypeContext::InitTypeContext(&typeContext); + } + else + { + // COM doesn't support generics so the type is obvious + TypeHandle actualType = TypeHandle{ pMD->GetMethodTable() }; + + signature = pMD->GetSignature(); + pModule = pMD->GetModule(); + SigTypeContext::InitTypeContext(pMD, actualType, &typeContext); + } + + _ASSERTE(!signature.IsEmpty() && pModule != nullptr); + + // Create details + return CallsiteDetails{ { signature, pModule, &typeContext }, pFrame, pMD, fIsDelegate }; +} + +UINT32 CLRToCOMLateBoundWorker( + _In_ ComPlusMethodFrame *pFrame, + _In_ ComPlusCallMethodDesc *pMD) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pFrame)); + PRECONDITION(CheckPointer(pMD)); + } + CONTRACTL_END; + + HRESULT hr; + + LOG((LF_STUBS, LL_INFO1000, "Calling CLRToCOMLateBoundWorker %s::%s \n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); + + // Retrieve the method table and the method desc of the call. + MethodTable *pItfMT = pMD->GetInterfaceMethodTable(); + ComPlusCallMethodDesc *pItfMD = pMD; + + // Make sure this is only called on IDispatch only interfaces. + _ASSERTE(pItfMT->GetComInterfaceType() == ifDispatch); + + // If this is a method impl MD then we need to retrieve the actual interface MD that + // this is a method impl for. + // REVISIT_TODO: Stop using ComSlot to convert method impls to interface MD + // _ASSERTE(pMD->m_pComPlusCallInfo->m_cachedComSlot == 7); + if (!pMD->GetMethodTable()->IsInterface()) + { + const unsigned cbExtraSlots = 7; + pItfMD = (ComPlusCallMethodDesc*)pItfMT->GetMethodDescForSlot(pMD->m_pComPlusCallInfo->m_cachedComSlot - cbExtraSlots); + CONSISTENCY_CHECK(pMD->GetInterfaceMD() == pItfMD); + } + + // Token of member to call + mdToken tkMember; + DWORD binderFlags = BINDER_AllLookup; + + // Property details + mdProperty propToken; + LPCUTF8 strMemberName; + ULONG uSemantic; + + // See if there is property information for this member. + hr = pItfMT->GetModule()->GetPropertyInfoForMethodDef(pItfMD->GetMemberDef(), &propToken, &strMemberName, &uSemantic); + if (hr != S_OK) + { + // Non-property method + strMemberName = pItfMD->GetName(); + tkMember = pItfMD->GetMemberDef(); + binderFlags |= BINDER_InvokeMethod; + } + else + { + // Property accessor + tkMember = propToken; + + // Determine which type of accessor we are dealing with. + switch (uSemantic) + { + case msGetter: + { + // INVOKE_PROPERTYGET + binderFlags |= BINDER_GetProperty; + break; + } + + case msSetter: + { + // INVOKE_PROPERTYPUT or INVOKE_PROPERTYPUTREF + ULONG cAssoc; + ASSOCIATE_RECORD* pAssoc; + + IMDInternalImport *pMDImport = pItfMT->GetMDImport(); + + // Retrieve all the associates. + HENUMInternalHolder henum{ pMDImport }; + henum.EnumAssociateInit(propToken); + + cAssoc = henum.EnumGetCount(); + _ASSERTE(cAssoc > 0); + + ULONG allocSize = cAssoc * sizeof(*pAssoc); + if (allocSize < cAssoc) + COMPlusThrowHR(COR_E_OVERFLOW); + + pAssoc = (ASSOCIATE_RECORD*)_alloca((size_t)allocSize); + IfFailThrow(pMDImport->GetAllAssociates(&henum, pAssoc, cAssoc)); + + // Check to see if there is both a set and an other. If this is the case + // then the setter is a INVOKE_PROPERTYPUTREF otherwise we will make it a + // INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF. + bool propHasOther = false; + for (ULONG i = 0; i < cAssoc; i++) + { + if (pAssoc[i].m_dwSemantics == msOther) + { + propHasOther = true; + break; + } + } + + if (propHasOther) + { + // There is both a INVOKE_PROPERTYPUT and a INVOKE_PROPERTYPUTREF for this + // property. Therefore be specific and make this invoke a INVOKE_PROPERTYPUTREF. + binderFlags |= BINDER_PutRefDispProperty; + } + else + { + // Only a setter so make the invoke a set which maps to + // INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF. + binderFlags = BINDER_SetProperty; + } + break; + } + + case msOther: + { + // INVOKE_PROPERTYPUT + binderFlags |= BINDER_PutDispProperty; + break; + } + + default: + { + _ASSERTE(!"Invalid method semantic!"); + } + } + } + + // If the method has a void return type, then set the IgnoreReturn binding flag. + if (pItfMD->IsVoid()) + binderFlags |= BINDER_IgnoreReturn; + + UINT32 fpRetSize = 0; + + struct + { + OBJECTREF MemberName; + OBJECTREF ItfTypeObj; + PTRARRAYREF Args; + BOOLARRAYREF ArgsIsByRef; + PTRARRAYREF ArgsTypes; + OBJECTREF ArgsWrapperTypes; + OBJECTREF RetValType; + OBJECTREF RetVal; + } gc; + ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + { + // Retrieve the exposed type object for the interface. + gc.ItfTypeObj = pItfMT->GetManagedClassObject(); + + // Retrieve the name of the target member. If the member + // has a DISPID then use that to optimize the invoke. + DISPID dispId = DISPID_UNKNOWN; + hr = pItfMD->GetMDImport()->GetDispIdOfMemberDef(tkMember, (ULONG*)&dispId); + if (hr == S_OK) + { + WCHAR strTmp[ARRAYSIZE(DISPID_NAME_FORMAT_STRING W("4294967295"))]; + _snwprintf_s(strTmp, COUNTOF(strTmp), _TRUNCATE, DISPID_NAME_FORMAT_STRING, dispId); + gc.MemberName = StringObject::NewString(strTmp); + } + else + { + gc.MemberName = StringObject::NewString(strMemberName); + } + + CallsiteDetails callsite = CreateCallsiteDetails(pFrame); + + // Arguments + CallsiteInspect::GetCallsiteArgs(callsite, &gc.Args, &gc.ArgsIsByRef, &gc.ArgsTypes); + + // If call requires object wrapping, set up the array of wrapper types. + if (pMD->RequiresArgumentWrapping()) + gc.ArgsWrapperTypes = SetUpWrapperInfo(pItfMD); + + // Return type + TypeHandle retValHandle = callsite.MetaSig.GetRetTypeHandleThrowing(); + gc.RetValType = retValHandle.GetManagedClassObject(); + + // the return value is written into the Frame's neginfo, so we don't + // need to return it directly. We can just have the stub do that work. + // However, the stub needs to know what type of FP return this is, if + // any, so we return the return size info as the return value. + if (callsite.MetaSig.HasFPReturn()) + { + callsite.MetaSig.Reset(); + ArgIterator argit{ &callsite.MetaSig }; + fpRetSize = argit.GetFPReturnSize(); + _ASSERTE(fpRetSize > 0); + } + + // Create a call site for the invoke + MethodDescCallSite forwardCallToInvoke(METHOD__CLASS__FORWARD_CALL_TO_INVOKE, &gc.ItfTypeObj); + + // Prepare the arguments that will be passed to the method. + ARG_SLOT invokeArgs[] = + { + ObjToArgSlot(gc.ItfTypeObj), + ObjToArgSlot(gc.MemberName), + (ARG_SLOT)binderFlags, + ObjToArgSlot(pFrame->GetThis()), + ObjToArgSlot(gc.Args), + ObjToArgSlot(gc.ArgsIsByRef), + ObjToArgSlot(gc.ArgsWrapperTypes), + ObjToArgSlot(gc.ArgsTypes), + ObjToArgSlot(gc.RetValType) + }; + + // Invoke the method + gc.RetVal = forwardCallToInvoke.CallWithValueTypes_RetOBJECTREF(invokeArgs); + + // Ensure all outs and return values are moved back to the current callsite + CallsiteInspect::PropagateOutParametersBackToCallsite(gc.Args, gc.RetVal, callsite); + } + GCPROTECT_END(); + + return fpRetSize; +} // calls that propagate from CLR to COM @@ -681,6 +991,12 @@ UINT32 STDCALL CLRToCOMWorker(TransitionBlock * pTransitionBlock, ComPlusCallMet { returnValue = CLRToCOMEventCallWorker(pFrame, pMD); } + else if (pItfMT->GetComInterfaceType() == ifDispatch) + { + // If the interface is a Dispatch only interface then convert the early bound + // call to a late bound call. + returnValue = CLRToCOMLateBoundWorker(pFrame, pMD); + } else { LOG((LF_STUBS, LL_INFO1000, "Calling CLRToCOMWorker %s::%s \n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); diff --git a/src/vm/metasig.h b/src/vm/metasig.h index b3a4139a7c..e826526976 100644 --- a/src/vm/metasig.h +++ b/src/vm/metasig.h @@ -455,6 +455,7 @@ DEFINE_METASIG(IM(IntPtr_UInt_IntPtr_IntPtr_RetVoid, I K I I, v)) DEFINE_METASIG(IM(Obj_Bool_RetVoid, j F, v)) #ifdef FEATURE_COMINTEROP DEFINE_METASIG(SM(Obj_RetStr, j, s)) +DEFINE_METASIG_T(IM(Str_BindingFlags_Obj_ArrObj_ArrBool_ArrInt_ArrType_Type_RetObj, s g(BINDING_FLAGS) j a(j) a(F) a(i) a(C(TYPE)) C(TYPE), j)) #endif // FEATURE_COMINTEROP DEFINE_METASIG_T(IM(Obj_Obj_BindingFlags_Binder_CultureInfo_RetVoid, j j g(BINDING_FLAGS) C(BINDER) C(CULTURE_INFO), v)) DEFINE_METASIG_T(IM(Obj_Obj_BindingFlags_Binder_ArrObj_CultureInfo_RetVoid, j j g(BINDING_FLAGS) C(BINDER) a(j) C(CULTURE_INFO), v)) diff --git a/src/vm/mlinfo.h b/src/vm/mlinfo.h index b27dcc01c3..b31e34f138 100644 --- a/src/vm/mlinfo.h +++ b/src/vm/mlinfo.h @@ -31,6 +31,7 @@ enum DispatchWrapperType { DispatchWrapperType_Unknown = 0x00000001, DispatchWrapperType_Dispatch = 0x00000002, + //DispatchWrapperType_Record = 0x00000004, DispatchWrapperType_Error = 0x00000008, DispatchWrapperType_Currency = 0x00000010, DispatchWrapperType_BStr = 0x00000020, diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h index 2813fc6e04..ab00e94577 100644 --- a/src/vm/mscorlib.h +++ b/src/vm/mscorlib.h @@ -191,10 +191,6 @@ DEFINE_METHOD(BINDER, CHANGE_TYPE, ChangeType, DEFINE_CLASS(BINDING_FLAGS, Reflection, BindingFlags) -#ifdef FEATURE_COMINTEROP -DEFINE_CLASS(BSTR_WRAPPER, Interop, BStrWrapper) -#endif // FEATURE_COMINTEROP - DEFINE_CLASS_U(System, RuntimeType, ReflectClassBaseObject) DEFINE_FIELD_U(m_cache, ReflectClassBaseObject, m_cache) DEFINE_FIELD_U(m_handle, ReflectClassBaseObject, m_typeHandle) @@ -210,6 +206,19 @@ DEFINE_METHOD(CLASS, GET_FIELD_INFO, GetFieldInfo, DEFINE_METHOD(CLASS, GET_PROPERTY_INFO, GetPropertyInfo, SM_RuntimeType_Int_RetPropertyInfo) #ifdef FEATURE_COMINTEROP +DEFINE_METHOD(CLASS, FORWARD_CALL_TO_INVOKE, ForwardCallToInvokeMember, IM_Str_BindingFlags_Obj_ArrObj_ArrBool_ArrInt_ArrType_Type_RetObj) +#endif // FEATURE_COMINTEROP + +#ifdef FEATURE_COMINTEROP +DEFINE_CLASS(BSTR_WRAPPER, Interop, BStrWrapper) +DEFINE_CLASS(CURRENCY_WRAPPER, Interop, CurrencyWrapper) +DEFINE_CLASS(DISPATCH_WRAPPER, Interop, DispatchWrapper) +DEFINE_CLASS(ERROR_WRAPPER, Interop, ErrorWrapper) +DEFINE_CLASS(UNKNOWN_WRAPPER, Interop, UnknownWrapper) +DEFINE_CLASS(VARIANT_WRAPPER, Interop, VariantWrapper) +#endif // FEATURE_COMINTEROP + +#ifdef FEATURE_COMINTEROP DEFINE_CLASS_U(System, __ComObject, ComObject) DEFINE_FIELD_U(m_ObjectToDataMap, ComObject, m_ObjectToDataMap) DEFINE_CLASS(COM_OBJECT, System, __ComObject) @@ -296,10 +305,6 @@ DEFINE_PROPERTY(CULTURE_INFO, PARENT, Parent, DEFINE_CLASS(CURRENCY, System, Currency) DEFINE_METHOD(CURRENCY, DECIMAL_CTOR, .ctor, IM_Dec_RetVoid) -#ifdef FEATURE_COMINTEROP -DEFINE_CLASS(CURRENCY_WRAPPER, Interop, CurrencyWrapper) -#endif - DEFINE_CLASS(DATE_TIME, System, DateTime) DEFINE_METHOD(DATE_TIME, LONG_CTOR, .ctor, IM_Long_RetVoid) @@ -322,10 +327,6 @@ DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) -#ifdef FEATURE_COMINTEROP -DEFINE_CLASS(DISPATCH_WRAPPER, Interop, DispatchWrapper) -#endif // FEATURE_COMINTEROP - DEFINE_CLASS(DYNAMICMETHOD, ReflectionEmit, DynamicMethod) DEFINE_CLASS(DYNAMICRESOLVER, ReflectionEmit, DynamicResolver) @@ -344,10 +345,6 @@ DEFINE_CLASS(ENVIRONMENT, System, Environment) DEFINE_METHOD(ENVIRONMENT, GET_RESOURCE_STRING_LOCAL, GetResourceStringLocal, SM_Str_RetStr) DEFINE_METHOD(ENVIRONMENT, SET_COMMAND_LINE_ARGS, SetCommandLineArgs, SM_ArrStr_RetVoid) -#ifdef FEATURE_COMINTEROP -DEFINE_CLASS(ERROR_WRAPPER, Interop, ErrorWrapper) -#endif - DEFINE_CLASS(EVENT, Reflection, RuntimeEventInfo) DEFINE_CLASS(EVENT_ARGS, System, EventArgs) @@ -960,18 +957,10 @@ DEFINE_CLASS(LAZY, System, Lazy`1) DEFINE_CLASS(LAZY_INITIALIZER, Threading, LazyInitializer) -#ifdef FEATURE_COMINTEROP -DEFINE_CLASS(UNKNOWN_WRAPPER, Interop, UnknownWrapper) -#endif - DEFINE_CLASS(VALUE_TYPE, System, ValueType) DEFINE_METHOD(VALUE_TYPE, GET_HASH_CODE, GetHashCode, IM_RetInt) DEFINE_METHOD(VALUE_TYPE, EQUALS, Equals, IM_Obj_RetBool) -#ifdef FEATURE_COMINTEROP -DEFINE_CLASS(VARIANT_WRAPPER, Interop, VariantWrapper) -#endif // FEATURE_COMINTEROP - DEFINE_CLASS(GC, System, GC) DEFINE_METHOD(GC, KEEP_ALIVE, KeepAlive, SM_Obj_RetVoid) DEFINE_METHOD(GC, COLLECT, Collect, SM_RetVoid) diff --git a/src/vm/stdinterfaces.cpp b/src/vm/stdinterfaces.cpp index 5726df9958..005748ce2d 100644 --- a/src/vm/stdinterfaces.cpp +++ b/src/vm/stdinterfaces.cpp @@ -583,25 +583,6 @@ ClassInfo_GetClassInfo(IUnknown* pUnk, ITypeInfo** ppTI) return hr; } -//------------------------------------------------------------------------------------- -// Helper to get the ITypeLib* for a Assembly. -HRESULT GetITypeLibForAssembly(Assembly *pAssembly, ITypeLib **ppTLB, int bAutoCreate, int flags) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - PRECONDITION(CheckPointer(pAssembly)); - PRECONDITION(CheckPointer(ppTLB)); - } - CONTRACTL_END; - - //@CORESYSTODO: what to do? - return E_FAIL; -} // HRESULT GetITypeLibForAssembly() - - //------------------------------------------------------------------------------------------ // Helper to get the ITypeInfo* for a type. HRESULT GetITypeLibForEEClass(MethodTable *pClass, ITypeLib **ppTLB, int bAutoCreate, int flags) @@ -614,7 +595,7 @@ HRESULT GetITypeLibForEEClass(MethodTable *pClass, ITypeLib **ppTLB, int bAutoCr } CONTRACTL_END; - return GetITypeLibForAssembly(pClass->GetAssembly(), ppTLB, bAutoCreate, flags); + return COR_E_NOTSUPPORTED; } // HRESULT GetITypeLibForEEClass() diff --git a/src/vm/stdinterfaces.h b/src/vm/stdinterfaces.h index 57981fa868..3ef26d1563 100644 --- a/src/vm/stdinterfaces.h +++ b/src/vm/stdinterfaces.h @@ -539,18 +539,10 @@ HRESULT TryGetGuid(MethodTable* pClass, GUID* pGUID, BOOL b); //------------------------------------------------------------------------------------------ // Helpers to get the ITypeInfo* for a type. -HRESULT ExportTypeLibFromLoadedAssemblyNoThrow(Assembly *pAssembly, LPCWSTR szTlb, ITypeLib **ppTlb, ITypeLibExporterNotifySink *pINotify, int flags); -void ExportTypeLibFromLoadedAssembly(Assembly *pAssembly, LPCWSTR szTlb, ITypeLib **ppTlb, ITypeLibExporterNotifySink *pINotify, int flags); -HRESULT GetITypeLibForEEClass(MethodTable *pMT, ITypeLib **ppTLB, int bAutoCreate, int flags); HRESULT GetITypeInfoForEEClass(MethodTable *pMT, ITypeInfo **ppTI, int bClassInfo=false, int bAutoCreate=true, int flags=0); -HRESULT GetTypeLibIdForRegisteredEEClass(MethodTable *pMT, GUID *pGuid); HRESULT GetDefaultInterfaceForCoclass(ITypeInfo *pTI, ITypeInfo **ppTIDef); //------------------------------------------------------------------------------------- -// Helper to get the ITypeLib* for a Assembly. -HRESULT GetITypeLibForAssembly(Assembly *pAssembly, ITypeLib **ppTLB, int bAutoCreate, int flags); - -//------------------------------------------------------------------------------------- // Helper to get the GUID of the typelib that is created from an assembly. HRESULT GetTypeLibGuidForAssembly(Assembly *pAssembly, GUID *pGuid); |