Files

4100 lines
126 KiB
C++
Raw Permalink Normal View History

2016-01-27 11:41:24 -08:00
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
2015-01-30 14:14:42 -08:00
#include "common.h"
#include "vars.hpp"
#include "excep.h"
#include "interoputil.h"
#include "cachelinealloc.h"
#include "comutilnative.h"
#include "field.h"
#include "guidfromname.h"
#include "eeconfig.h"
#include "mlinfo.h"
#include "comdelegate.h"
#include "appdomain.hpp"
#include "prettyprintsig.h"
#include "util.hpp"
#include "interopconverter.h"
#include "wrappers.h"
#include "invokeutil.h"
#include "comcallablewrapper.h"
#include "../md/compiler/custattr.h"
#include "siginfo.hpp"
#include "finalizerthread.h"
#include "interoplibinterface.h"
2015-01-30 14:14:42 -08:00
#ifdef FEATURE_COMINTEROP
#include "cominterfacemarshaler.h"
#endif
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
#include "olecontexthelpers.h"
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
#ifdef FEATURE_COMINTEROP
#include "dispex.h"
#include "runtimecallablewrapper.h"
#include "comtoclrcall.h"
#include "clrtocomcall.h"
#include "comcache.h"
#include "commtmemberinfomap.h"
#include "olevariant.h"
#include "stdinterfaces.h"
#include "typeparse.h"
#include "interoputil.inl"
#include "typestring.h"
#define STANDARD_DISPID_PREFIX W("[DISPID")
#define STANDARD_DISPID_PREFIX_LENGTH 7
#define GET_ENUMERATOR_METHOD_NAME W("GetEnumerator")
#ifdef _DEBUG
2022-07-17 08:11:11 +03:00
VOID InitializeInteropLogging();
#endif
2015-01-30 14:14:42 -08:00
struct ByrefArgumentInfo
{
BOOL m_bByref;
VARIANT m_Val;
};
// Flag indicating if COM support has been initialized.
BOOL g_fComStarted = FALSE;
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
void AllocateComClassObject(ComClassFactory* pComClsFac, OBJECTREF* pComObj);
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
#endif // FEATURE_COMINTEROP
//------------------------------------------------------------------
// setup error info for exception object
//
#ifdef FEATURE_COMINTEROP
// HRESULT for CLR created IErrorInfo pointers are accessible
// from the enclosing simple wrapper
// This is in-proc only.
HRESULT GetHRFromCLRErrorInfo(IErrorInfo* pErr)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pErr));
PRECONDITION(IsInProcCCWTearOff(pErr));
PRECONDITION(IsSimpleTearOff(pErr));
}
CONTRACTL_END;
SimpleComCallWrapper* pSimpleWrap = SimpleComCallWrapper::GetWrapperFromIP(pErr);
return pSimpleWrap->IErrorInfo_hr();
}
2015-01-30 14:14:42 -08:00
HRESULT SetupErrorInfo(OBJECTREF pThrownObject)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
HRESULT hr = E_FAIL;
Exception* pException = NULL;
GCPROTECT_BEGIN(pThrownObject)
{
EX_TRY
{
// Calls to COM up ahead.
hr = EnsureComStartedNoThrow();
if (SUCCEEDED(hr) && pThrownObject != NULL)
{
IErrorInfo* pErr = NULL;
EX_TRY
{
// set the error info object for the exception that was thrown.
pErr = (IErrorInfo *)GetComIPFromObjectRef(&pThrownObject, IID_IErrorInfo);
2015-01-30 14:14:42 -08:00
{
GCX_PREEMP();
SetErrorInfo(0, pErr);
2015-01-30 14:14:42 -08:00
}
// Release the pErr in case it exists.
if (pErr)
{
hr = GetHRFromCLRErrorInfo(pErr);
ULONG cbRef = SafeRelease(pErr);
LogInteropRelease(pErr, cbRef, "IErrorInfo");
}
}
EX_CATCH
{
hr = GET_EXCEPTION()->GetHR();
}
EX_END_CATCH(SwallowAllExceptions);
}
}
EX_CATCH
{
if (SUCCEEDED(hr))
hr = E_FAIL;
}
EX_END_CATCH(SwallowAllExceptions);
}
GCPROTECT_END();
return hr;
}
//-------------------------------------------------------------------
// Used to populate ExceptionData with COM data
2015-01-30 14:14:42 -08:00
//-------------------------------------------------------------------
void FillExceptionData(
_Inout_ ExceptionData* pedata,
_In_ IErrorInfo* pErrInfo)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pedata));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
if (pErrInfo != NULL)
{
2021-03-25 14:26:44 -07:00
Thread* pThread = GetThreadNULLOk();
2015-01-30 14:14:42 -08:00
if (pThread != NULL)
{
GCX_PREEMP();
2015-01-30 14:14:42 -08:00
pErrInfo->GetSource (&pedata->bstrSource);
pErrInfo->GetDescription (&pedata->bstrDescription);
pErrInfo->GetHelpFile (&pedata->bstrHelpFile);
pErrInfo->GetHelpContext (&pedata->dwHelpContext );
pErrInfo->GetGUID(&pedata->guid);
ULONG cbRef = SafeRelease(pErrInfo); // release the IErrorInfo interface pointer
LogInteropRelease(pErrInfo, cbRef, "IErrorInfo");
}
}
}
#endif // FEATURE_COMINTEROP
2015-01-30 14:14:42 -08:00
//---------------------------------------------------------------------------
// If pImport has the DefaultDllImportSearchPathsAttribute,
// set the value of the attribute in pDlImportSearchPathFlags and return true.
2019-05-20 15:15:52 -07:00
BOOL GetDefaultDllImportSearchPathsAttributeValue(Module *pModule, mdToken token, DWORD * pDllImportSearchPathFlags)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
2019-05-20 15:15:52 -07:00
PRECONDITION(CheckPointer(pModule));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
BYTE* pData = NULL;
LONG cData = 0;
2019-05-20 15:15:52 -07:00
HRESULT hr = pModule->GetCustomAttribute(token,
WellKnownAttribute::DefaultDllImportSearchPaths,
(const VOID **)(&pData),
(ULONG *)&cData);
2015-01-30 14:14:42 -08:00
IfFailThrow(hr);
if(cData == 0 )
{
return FALSE;
}
CustomAttributeParser ca(pData, cData);
CaArg args[1];
args[0].InitEnum(SERIALIZATION_TYPE_U4, (ULONG)0);
ParseKnownCaArgs(ca, args, ARRAY_SIZE(args));
*pDllImportSearchPathFlags = args[0].val.u4;
2015-01-30 14:14:42 -08:00
return TRUE;
}
//---------------------------------------------------------------------------
// Returns the index of the LCID parameter if one exists and -1 otherwise.
int GetLCIDParameterIndex(MethodDesc *pMD)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pMD));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
int iLCIDParam = -1;
HRESULT hr;
const BYTE * pVal;
ULONG cbVal;
// Check to see if the method has the LCIDConversionAttribute.
hr = pMD->GetCustomAttribute(WellKnownAttribute::LCIDConversion, (const void**)&pVal, &cbVal);
if (hr == S_OK)
2015-01-30 14:14:42 -08:00
{
CustomAttributeParser caLCID(pVal, cbVal);
CaArg args[1];
args[0].Init(SERIALIZATION_TYPE_I4, 0);
IfFailGo(ParseKnownCaArgs(caLCID, args, ARRAY_SIZE(args)));
iLCIDParam = args[0].val.i4;
2015-01-30 14:14:42 -08:00
}
ErrExit:
return iLCIDParam;
}
//---------------------------------------------------------------------------
// Transforms an LCID into a CultureInfo.
void GetCultureInfoForLCID(LCID lcid, OBJECTREF *pCultureObj)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(CheckPointer(pCultureObj));
}
CONTRACTL_END;
OBJECTREF CultureObj = NULL;
GCPROTECT_BEGIN(CultureObj)
{
// Allocate a CultureInfo with the specified LCID.
CultureObj = AllocateObject(CoreLibBinder::GetClass(CLASS__CULTURE_INFO));
2015-01-30 14:14:42 -08:00
MethodDescCallSite cultureInfoCtor(METHOD__CULTURE_INFO__INT_CTOR, &CultureObj);
// Call the CultureInfo(int culture) constructor.
ARG_SLOT pNewArgs[] = {
ObjToArgSlot(CultureObj),
(ARG_SLOT)lcid
};
cultureInfoCtor.Call(pNewArgs);
// Set the returned culture object.
*pCultureObj = CultureObj;
}
GCPROTECT_END();
}
2015-01-30 14:14:42 -08:00
//---------------------------------------------------------------------------
// This method determines if a member is visible from COM.
BOOL IsMemberVisibleFromCom(MethodTable *pDeclaringMT, mdToken tk, mdMethodDef mdAssociate)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pDeclaringMT));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
HRESULT hr;
const BYTE * pVal;
ULONG cbVal;
DWORD dwFlags;
IMDInternalImport *pInternalImport = pDeclaringMT->GetMDImport();
2019-05-20 15:15:52 -07:00
Module *pModule = pDeclaringMT->GetModule();
2015-01-30 14:14:42 -08:00
// Check to see if the member is public.
switch (TypeFromToken(tk))
{
case mdtFieldDef:
_ASSERTE(IsNilToken(mdAssociate));
if (FAILED(pInternalImport->GetFieldDefProps(tk, &dwFlags)))
{
return FALSE;
}
if (!IsFdPublic(dwFlags))
return FALSE;
break;
case mdtMethodDef:
_ASSERTE(IsNilToken(mdAssociate));
if (FAILED(pInternalImport->GetMethodDefProps(tk, &dwFlags)))
{
return FALSE;
}
if (!IsMdPublic(dwFlags))
{
return FALSE;
}
{
// Generic Methods are not visible from COM
MDEnumHolder hEnumTyPars(pInternalImport);
if (FAILED(pInternalImport->EnumInit(mdtGenericParam, tk, &hEnumTyPars)))
return FALSE;
if (pInternalImport->EnumGetCount(&hEnumTyPars) != 0)
return FALSE;
}
break;
case mdtProperty:
_ASSERTE(!IsNilToken(mdAssociate));
if (FAILED(pInternalImport->GetMethodDefProps(mdAssociate, &dwFlags)))
{
return FALSE;
}
if (!IsMdPublic(dwFlags))
return FALSE;
// Check to see if the associate has the ComVisible attribute set
hr = pModule->GetCustomAttribute(mdAssociate, WellKnownAttribute::ComVisible, (const void**)&pVal, &cbVal);
if (hr == S_OK)
2015-01-30 14:14:42 -08:00
{
CustomAttributeParser cap(pVal, cbVal);
if (FAILED(cap.SkipProlog()))
return FALSE;
2015-01-30 14:14:42 -08:00
UINT8 u1;
if (FAILED(cap.GetU1(&u1)))
return FALSE;
2015-01-30 14:14:42 -08:00
return (BOOL)u1;
2015-01-30 14:14:42 -08:00
}
break;
default:
_ASSERTE(!"The type of the specified member is not handled by IsMemberVisibleFromCom");
break;
}
// Check to see if the member has the ComVisible attribute set (non-WinRT members only).
hr = pModule->GetCustomAttribute(tk, WellKnownAttribute::ComVisible, (const void**)&pVal, &cbVal);
if (hr == S_OK)
2015-01-30 14:14:42 -08:00
{
CustomAttributeParser cap(pVal, cbVal);
if (FAILED(cap.SkipProlog()))
return FALSE;
2015-01-30 14:14:42 -08:00
UINT8 u1;
if (FAILED(cap.GetU1(&u1)))
return FALSE;
2015-01-30 14:14:42 -08:00
return (BOOL)u1;
2015-01-30 14:14:42 -08:00
}
// The member is visible.
return TRUE;
}
// This method checks whether the given IErrorInfo is actually a managed CLR object.
BOOL IsManagedObject(IUnknown *pIUnknown)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(CheckPointer(pIUnknown));
}
CONTRACTL_END;
#if FEATURE_COMINTEROP
//Check based on IUnknown slots, i.e. we'll see whether the IP maps to a CCW.
if (MapIUnknownToWrapper(pIUnknown) != NULL)
{
// We found an existing CCW hence this is a managed exception.
return TRUE;
}
#endif
return FALSE;
}
2015-01-30 14:14:42 -08:00
ULONG GetStringizedMethodDef(MethodTable *pDeclaringMT, mdToken tkMb, CQuickArray<BYTE> &rDef, ULONG cbCur)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pDeclaringMT));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
IMDInternalImport *pMDImport = pDeclaringMT->GetMDImport();
CQuickBytes rSig;
MDEnumHolder ePm(pMDImport); // For enumerating params.
mdParamDef tkPm; // A param token.
DWORD dwFlags; // Param flags.
USHORT usSeq; // Sequence of a parameter.
ULONG cPm; // Count of params.
PCCOR_SIGNATURE pSig;
ULONG cbSig;
// Don't count invisible members.
if (!IsMemberVisibleFromCom(pDeclaringMT, tkMb, mdMethodDefNil))
return cbCur;
2015-01-30 14:14:42 -08:00
// accumulate the signatures.
IfFailThrow(pMDImport->GetSigOfMethodDef(tkMb, &cbSig, &pSig));
IfFailThrow(::PrettyPrintSigInternalLegacy(pSig, cbSig, "", &rSig, pMDImport));
2015-01-30 14:14:42 -08:00
// Get the parameter flags.
IfFailThrow(pMDImport->EnumInit(mdtParamDef, tkMb, &ePm));
cPm = pMDImport->EnumGetCount(&ePm);
2015-01-30 14:14:42 -08:00
// Resize for sig and params. Just use 1 byte of param.
rDef.ReSizeThrows(cbCur + rSig.Size() + cPm);
memcpy(rDef.Ptr() + cbCur, rSig.Ptr(), rSig.Size());
cbCur += (ULONG)(rSig.Size()-1);
2015-01-30 14:14:42 -08:00
// Enumerate through the params and get the flags.
while (pMDImport->EnumNext(&ePm, &tkPm))
{
LPCSTR szParamName_Ignore;
IfFailThrow(pMDImport->GetParamDefProps(tkPm, &usSeq, &dwFlags, &szParamName_Ignore));
if (usSeq == 0) // Skip return type flags.
continue;
rDef[cbCur++] = (BYTE)dwFlags;
}
// Return the number of bytes.
return cbCur;
} // void GetStringizedMethodDef()
ULONG GetStringizedFieldDef(MethodTable *pDeclaringMT, mdToken tkMb, CQuickArray<BYTE> &rDef, ULONG cbCur)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pDeclaringMT));
}
CONTRACTL_END;
CQuickBytes rSig;
PCCOR_SIGNATURE pSig;
ULONG cbSig;
// Don't count invisible members.
if (!IsMemberVisibleFromCom(pDeclaringMT, tkMb, mdMethodDefNil))
return cbCur;
2015-01-30 14:14:42 -08:00
IMDInternalImport *pMDImport = pDeclaringMT->GetMDImport();
// accumulate the signatures.
IfFailThrow(pMDImport->GetSigOfFieldDef(tkMb, &cbSig, &pSig));
IfFailThrow(::PrettyPrintSigInternalLegacy(pSig, cbSig, "", &rSig, pMDImport));
rDef.ReSizeThrows(cbCur + rSig.Size());
memcpy(rDef.Ptr() + cbCur, rSig.Ptr(), rSig.Size());
cbCur += (ULONG)(rSig.Size()-1);
// Return the number of bytes.
return cbCur;
} // void GetStringizedFieldDef()
//--------------------------------------------------------------------------------
// This method generates a stringized version of an interface that contains the
// name of the interface along with the signature of all the methods.
SIZE_T GetStringizedItfDef(TypeHandle InterfaceType, CQuickArray<BYTE> &rDef)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
MethodTable* pIntfMT = InterfaceType.GetMethodTable();
PREFIX_ASSUME(pIntfMT != NULL);
IMDInternalImport* pMDImport = pIntfMT->GetMDImport();
PREFIX_ASSUME(pMDImport != NULL);
LPCWSTR szName;
2015-01-30 14:14:42 -08:00
ULONG cchName;
MDEnumHolder eMb(pMDImport); // For enumerating methods and fields.
mdToken tkMb; // A method or field token.
SIZE_T cbCur;
// Make sure the specified type is an interface with a valid token.
_ASSERTE(!IsNilToken(pIntfMT->GetCl()) && pIntfMT->IsInterface());
// Get the name of the class.
DefineFullyQualifiedNameForClassW();
szName = GetFullyQualifiedNameForClassNestedAwareW(pIntfMT);
cchName = (ULONG)u16_strlen(szName);
2015-01-30 14:14:42 -08:00
// Start with the interface name.
cbCur = cchName * sizeof(WCHAR);
rDef.ReSizeThrows(cbCur + sizeof(WCHAR));
wcscpy_s(reinterpret_cast<LPWSTR>(rDef.Ptr()), rDef.Size()/sizeof(WCHAR), szName);
// Enumerate the methods...
IfFailThrow(pMDImport->EnumInit(mdtMethodDef, pIntfMT->GetCl(), &eMb));
while(pMDImport->EnumNext(&eMb, &tkMb))
{ // accumulate the signatures.
cbCur = GetStringizedMethodDef(pIntfMT, tkMb, rDef, (ULONG)cbCur);
}
pMDImport->EnumClose(&eMb);
// Enumerate the fields...
IfFailThrow(pMDImport->EnumInit(mdtFieldDef, pIntfMT->GetCl(), &eMb));
while(pMDImport->EnumNext(&eMb, &tkMb))
{ // accumulate the signatures.
cbCur = GetStringizedFieldDef(pIntfMT, tkMb, rDef, (ULONG)cbCur);
}
// Return the number of bytes.
return cbCur;
} // ULONG GetStringizedItfDef()
//--------------------------------------------------------------------------------
// Helper to get the stringized form of typelib guid.
HRESULT GetStringizedTypeLibGuidForAssembly(Assembly *pAssembly, CQuickArray<BYTE> &rDef, ULONG cbCur, ULONG *pcbFetched)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
2015-01-30 14:14:42 -08:00
MODE_ANY;
PRECONDITION(CheckPointer(pAssembly));
PRECONDITION(CheckPointer(pcbFetched));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
HRESULT hr = S_OK; // A result.
LPCUTF8 pszName = NULL; // Library name in UTF8.
ULONG cbName; // Length of name, UTF8 characters.
LPWSTR pName; // Pointer to library name.
ULONG cchName; // Length of name, wide chars.
LPWSTR pch=0; // Pointer into lib name.
const void *pSN=NULL; // Pointer to public key.
DWORD cbSN=0; // Size of public key.
USHORT usMajorVersion; // The major version number.
USHORT usMinorVersion; // The minor version number.
USHORT usBuildNumber; // The build number.
USHORT usRevisionNumber; // The revision number.
const BYTE *pbData = NULL; // Pointer to a custom attribute data.
ULONG cbData = 0; // Size of custom attribute data.
static char szTypeLibKeyName[] = {"TypeLib"};
2015-01-30 14:14:42 -08:00
// Get the name, and determine its length.
pszName = pAssembly->GetSimpleName();
cbName=(ULONG)strlen(pszName);
cchName = WszMultiByteToWideChar(CP_ACP,0, pszName,cbName+1, 0,0);
2015-01-30 14:14:42 -08:00
// See if there is a public key.
EX_TRY
{
pSN = pAssembly->GetPublicKey(&cbSN);
}
EX_CATCH
{
IfFailGo(COR_E_BADIMAGEFORMAT);
}
EX_END_CATCH(RethrowTerminalExceptions)
2015-01-30 14:14:42 -08:00
#ifdef FEATURE_COMINTEROP
// If the ComCompatibleVersionAttribute is set, then use the version
// number in the attribute when generating the GUID.
IfFailGo(pAssembly->GetCustomAttribute(TokenFromRid(1, mdtAssembly), WellKnownAttribute::ComCompatibleVersion, (const void**)&pbData, &cbData));
2015-01-30 14:14:42 -08:00
if (hr == S_OK && cbData >= (2 + 4 * sizeof(INT32)))
{
CustomAttributeParser cap(pbData, cbData);
IfFailRet(cap.SkipProlog());
// Retrieve the major and minor version from the attribute.
UINT32 u4;
IfFailRet(cap.GetU4(&u4));
usMajorVersion = GET_VERSION_USHORT_FROM_INT(u4);
IfFailRet(cap.GetU4(&u4));
usMinorVersion = GET_VERSION_USHORT_FROM_INT(u4);
IfFailRet(cap.GetU4(&u4));
usBuildNumber = GET_VERSION_USHORT_FROM_INT(u4);
IfFailRet(cap.GetU4(&u4));
usRevisionNumber = GET_VERSION_USHORT_FROM_INT(u4);
}
else
#endif // FEATURE_COMINTEROP
{
pAssembly->GetVersion(&usMajorVersion, &usMinorVersion, &usBuildNumber, &usRevisionNumber);
}
// Get the version information.
struct versioninfo
{
USHORT usMajorVersion; // Major Version.
2015-01-30 14:14:42 -08:00
USHORT usMinorVersion; // Minor Version.
USHORT usBuildNumber; // Build Number.
USHORT usRevisionNumber; // Revision Number.
} ver;
// <REVISIT_TODO> An issue here is that usMajor is used twice and usMinor not at all.
// We're not fixing that because everyone has a major version, so all the
// generated guids would change, which is breaking. To compensate, if
2015-01-30 14:14:42 -08:00
// the minor is non-zero, we add it separately, below.</REVISIT_TODO>
ver.usMajorVersion = usMajorVersion;
ver.usMinorVersion = usMajorVersion; // Don't fix this line!
ver.usBuildNumber = usBuildNumber;
ver.usRevisionNumber = usRevisionNumber;
2015-01-30 14:14:42 -08:00
// Resize the output buffer.
IfFailGo(rDef.ReSizeNoThrow(cbCur + cchName*sizeof(WCHAR) + sizeof(szTypeLibKeyName)-1 + cbSN + sizeof(ver)+sizeof(USHORT)));
2015-01-30 14:14:42 -08:00
// Put it all together. Name first.
WszMultiByteToWideChar(CP_ACP,0, pszName,cbName+1, (LPWSTR)(&rDef[cbCur]),cchName);
pName = (LPWSTR)(&rDef[cbCur]);
for (pch=pName; *pch; ++pch)
if (*pch == '.' || *pch == ' ')
*pch = '_';
else
if (iswupper(*pch))
*pch = towlower(*pch);
cbCur += (cchName-1)*sizeof(WCHAR);
memcpy(&rDef[cbCur], szTypeLibKeyName, sizeof(szTypeLibKeyName)-1);
cbCur += sizeof(szTypeLibKeyName)-1;
2015-01-30 14:14:42 -08:00
// Version.
memcpy(&rDef[cbCur], &ver, sizeof(ver));
cbCur += sizeof(ver);
// If minor version is non-zero, add it to the hash. It should have been in the ver struct,
// but due to a bug, it was omitted there, and fixing it "right" would have been
// breaking. So if it isn't zero, add it; if it is zero, don't add it. Any
// possible value of minor thus generates a different guid, and a value of 0 still generates
// the guid that the original, buggy, code generated.
if (usMinorVersion != 0)
{
SET_UNALIGNED_16(&rDef[cbCur], usMinorVersion);
cbCur += sizeof(USHORT);
}
// Public key.
memcpy(&rDef[cbCur], pSN, cbSN);
cbCur += cbSN;
if (pcbFetched)
*pcbFetched = cbCur;
ErrExit:
return hr;
}
#include <optsmallperfcritical.h>
//--------------------------------------------------------------------------------
// Release helper, must be called in preemptive mode. Only use this variant if
// you already know you're in preemptive mode for other reasons.
2015-01-30 14:14:42 -08:00
ULONG SafeReleasePreemp(IUnknown * pUnk, RCW * pRCW)
{
CONTRACTL {
NOTHROW;
GC_TRIGGERS;
MODE_PREEMPTIVE;
PRECONDITION(CheckPointer(pUnk, NULL_OK));
} CONTRACTL_END;
if (pUnk == NULL)
return 0;
// Message pump could happen, so arbitrary managed code could run.
CONTRACT_VIOLATION(ThrowsViolation | FaultViolation);
return pUnk->Release();
2015-01-30 14:14:42 -08:00
}
//--------------------------------------------------------------------------------
// Release helper, enables and disables GC during call-outs
ULONG SafeRelease(IUnknown* pUnk, RCW* pRCW)
{
CONTRACTL {
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pUnk, NULL_OK));
} CONTRACTL_END;
if (pUnk == NULL)
return 0;
ULONG res = 0;
Thread * const pThread = GetThreadNULLOk();
GCX_PREEMP_NO_DTOR_HAVE_THREAD(pThread);
// Message pump could happen, so arbitrary managed code could run.
CONTRACT_VIOLATION(ThrowsViolation | FaultViolation);
res = pUnk->Release();
2015-01-30 14:14:42 -08:00
GCX_PREEMP_NO_DTOR_END();
return res;
}
#include <optdefault.h>
//--------------------------------------------------------------------------------
// Determines if a COM object can be cast to the specified type.
BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
if (!obj)
return TRUE;
if (pTargetMT->IsInterface())
{
return Object::SupportsInterface(obj, pTargetMT);
}
else
{
return obj->GetMethodTable()->CanCastToClass(pTargetMT);
2015-01-30 14:14:42 -08:00
}
}
// Returns TRUE iff the argument represents the "__ComObject" type.
BOOL IsComObjectClass(TypeHandle type)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
#ifdef FEATURE_COMINTEROP
if (!type.IsTypeDesc())
{
MethodTable *pMT = type.AsMethodTable();
if (pMT->IsComObjectType())
{
// May be __ComObject or typed RCW. __ComObject must have already been loaded
// if we see an MT marked like this so calling the *NoInit method is sufficient.
return pMT == g_pBaseCOMObject;
}
}
#endif
return FALSE;
}
2015-01-30 14:14:42 -08:00
VOID
ReadBestFitCustomAttribute(MethodDesc* pMD, BOOL* BestFit, BOOL* ThrowOnUnmappableChar)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
2019-05-20 15:15:52 -07:00
ReadBestFitCustomAttribute(pMD->GetModule(),
2015-01-30 14:14:42 -08:00
pMD->GetMethodTable()->GetCl(),
BestFit, ThrowOnUnmappableChar);
}
VOID
2019-05-20 15:15:52 -07:00
ReadBestFitCustomAttribute(Module* pModule, mdTypeDef cl, BOOL* BestFit, BOOL* ThrowOnUnmappableChar)
2015-01-30 14:14:42 -08:00
{
// Set the attributes to their defaults, just to be safe.
*BestFit = TRUE;
*ThrowOnUnmappableChar = FALSE;
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
2019-05-20 15:15:52 -07:00
PRECONDITION(CheckPointer(pModule));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
HRESULT hr;
BYTE* pData;
ULONG cbCount;
2015-01-30 14:14:42 -08:00
// A well-formed BestFitMapping attribute will have at least 5 bytes
// 1,2 for the prolog (should be 0x1, 0x0)
// 3 for the BestFitMapping bool
// 4,5 for the number of named parameters (will be 0 if ThrowOnUnmappableChar doesn't exist)
// 6 - 29 for the description of ThrowOnUnmappableChar
// 30 for the ThrowOnUnmappableChar bool
// Try the assembly first
2019-05-20 15:15:52 -07:00
hr = pModule->GetCustomAttribute(TokenFromRid(1, mdtAssembly), WellKnownAttribute::BestFitMapping, (const VOID**)(&pData), &cbCount);
2015-01-30 14:14:42 -08:00
if ((hr == S_OK) && (pData) && (cbCount > 4) && (pData[0] == 1) && (pData[1] == 0))
{
_ASSERTE((cbCount == 5) || (cbCount == 30));
2015-01-30 14:14:42 -08:00
// index to 2 to skip prolog
*BestFit = pData[2] != 0;
// If this parameter exists,
if (cbCount == 30)
// index to end of data to skip description of named argument
*ThrowOnUnmappableChar = pData[29] != 0;
}
// Now try the interface/class/struct
if (IsNilToken(cl))
return;
2019-05-20 15:15:52 -07:00
hr = pModule->GetCustomAttribute(cl, WellKnownAttribute::BestFitMapping, (const VOID**)(&pData), &cbCount);
2015-01-30 14:14:42 -08:00
if ((hr == S_OK) && (pData) && (cbCount > 4) && (pData[0] == 1) && (pData[1] == 0))
{
_ASSERTE((cbCount == 5) || (cbCount == 30));
// index to 2 to skip prolog
2015-01-30 14:14:42 -08:00
*BestFit = pData[2] != 0;
2015-01-30 14:14:42 -08:00
// If this parameter exists,
if (cbCount == 30)
// index to end of data to skip description of named argument
*ThrowOnUnmappableChar = pData[29] != 0;
}
}
2022-01-06 08:49:08 +02:00
int InternalWideToAnsi(_In_reads_(iNumWideChars) LPCWSTR szWideString, int iNumWideChars, _Out_writes_bytes_opt_(cbAnsiBufferSize) LPSTR szAnsiString, int cbAnsiBufferSize, BOOL fBestFit, BOOL fThrowOnUnmappableChar)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
if ((szWideString == 0) || (iNumWideChars == 0) || (szAnsiString == 0) || (cbAnsiBufferSize == 0))
return 0;
DWORD flags = 0;
int retval;
if (fBestFit == FALSE)
flags = WC_NO_BEST_FIT_CHARS;
if (fThrowOnUnmappableChar)
{
BOOL DefaultCharUsed = FALSE;
retval = WszWideCharToMultiByte(CP_ACP,
flags,
szWideString,
iNumWideChars,
szAnsiString,
cbAnsiBufferSize,
NULL,
&DefaultCharUsed);
DWORD lastError = GetLastError();
if (retval == 0)
{
INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX;
2015-01-30 14:14:42 -08:00
COMPlusThrowHR(HRESULT_FROM_WIN32(lastError));
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(true);
2015-01-30 14:14:42 -08:00
}
if (DefaultCharUsed)
{
struct HelperThrow
{
static void Throw()
{
COMPlusThrow( kArgumentException, IDS_EE_MARSHAL_UNMAPPABLE_CHAR );
}
};
2015-01-30 14:14:42 -08:00
ENCLOSE_IN_EXCEPTION_HANDLER( HelperThrow::Throw );
}
}
else
{
retval = WszWideCharToMultiByte(CP_ACP,
flags,
szWideString,
iNumWideChars,
szAnsiString,
cbAnsiBufferSize,
NULL,
NULL);
DWORD lastError = GetLastError();
if (retval == 0)
{
INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX;
2015-01-30 14:14:42 -08:00
COMPlusThrowHR(HRESULT_FROM_WIN32(lastError));
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(true);
2015-01-30 14:14:42 -08:00
}
}
return retval;
}
namespace
{
HRESULT TryParseClassInterfaceAttribute(
2019-05-20 15:15:52 -07:00
_In_ Module *pModule,
_In_ mdToken tkObj,
_Out_ CorClassIfaceAttr *val)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
2019-05-20 15:15:52 -07:00
PRECONDITION(CheckPointer(pModule));
PRECONDITION(CheckPointer(val));
}
CONTRACTL_END
const BYTE *pVal = nullptr;
ULONG cbVal = 0;
2019-05-20 15:15:52 -07:00
HRESULT hr = pModule->GetCustomAttribute(tkObj, WellKnownAttribute::ClassInterface, (const void**)&pVal, &cbVal);
if (hr != S_OK)
{
*val = clsIfNone;
return S_FALSE;
}
CustomAttributeParser cap(pVal, cbVal);
if (FAILED(cap.ValidateProlog()))
return COR_E_BADIMAGEFORMAT;
UINT8 u1;
if (FAILED(cap.GetU1(&u1)))
return COR_E_BADIMAGEFORMAT;
*val = (CorClassIfaceAttr)(u1);
_ASSERTE(*val < clsIfLast);
return S_OK;
}
}
2015-01-30 14:14:42 -08:00
//---------------------------------------------------------
// Read the ClassInterfaceType custom attribute info from
// both assembly level and class level
2015-01-30 14:14:42 -08:00
//---------------------------------------------------------
CorClassIfaceAttr ReadClassInterfaceTypeCustomAttribute(TypeHandle type)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(!type.IsInterface());
}
CONTRACTL_END
CorClassIfaceAttr attrValueMaybe;
// First look for the class interface attribute at the class level.
HRESULT hr = TryParseClassInterfaceAttribute(type.GetModule(), type.GetCl(), &attrValueMaybe);
if (FAILED(hr))
ThrowHR(hr, BFA_BAD_CLASS_INT_CA_FORMAT);
if (hr == S_FALSE)
{
// Check the class interface attribute at the assembly level.
Assembly *pAssembly = type.GetAssembly();
hr = TryParseClassInterfaceAttribute(pAssembly->GetModule(), pAssembly->GetManifestToken(), &attrValueMaybe);
if (FAILED(hr))
ThrowHR(hr, BFA_BAD_CLASS_INT_CA_FORMAT);
2015-01-30 14:14:42 -08:00
}
if (hr == S_OK)
return attrValueMaybe;
2015-01-30 14:14:42 -08:00
return DEFAULT_CLASS_INTERFACE_TYPE;
}
#ifdef FEATURE_COMINTEROP
2015-01-30 14:14:42 -08:00
//--------------------------------------------------------------------------------
// GetErrorInfo helper, enables and disables GC during call-outs
HRESULT SafeGetErrorInfo(IErrorInfo **ppIErrInfo)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(ppIErrInfo));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
*ppIErrInfo = NULL;
GCX_PREEMP();
HRESULT hr = S_OK;
EX_TRY
{
hr = GetErrorInfo(0, ppIErrInfo);
}
EX_CATCH
{
hr = E_OUTOFMEMORY;
}
EX_END_CATCH(SwallowAllExceptions);
2015-01-30 14:14:42 -08:00
return hr;
}
#endif // FEATURE_COMINTEROP
2015-01-30 14:14:42 -08:00
#include <optsmallperfcritical.h>
//--------------------------------------------------------------------------------
// QI helper, enables and disables GC during call-outs
HRESULT SafeQueryInterface(IUnknown* pUnk, REFIID riid, IUnknown** pResUnk)
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_MODE_ANY;
_ASSERTE(pUnk);
_ASSERTE(pResUnk);
Thread * const pThread = GetThreadNULLOk();
*pResUnk = NULL;
HRESULT hr = E_FAIL;
GCX_PREEMP_NO_DTOR_HAVE_THREAD(pThread);
BEGIN_CONTRACT_VIOLATION(ThrowsViolation); // message pump could happen, so arbitrary managed code could run
struct Param { HRESULT * const hr; IUnknown** const pUnk; REFIID riid; IUnknown*** const pResUnk; } param = { &hr, &pUnk, riid, &pResUnk };
#define PAL_TRY_ARG(argName) (*(pParam->argName))
#define PAL_TRY_REFARG(argName) (pParam->argName)
PAL_TRY(Param * const, pParam, &param)
{
PAL_TRY_ARG(hr) = PAL_TRY_ARG(pUnk)->QueryInterface(PAL_TRY_REFARG(riid), (void**) PAL_TRY_ARG(pResUnk));
2015-01-30 14:14:42 -08:00
}
PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
#if defined(STACK_GUARDS_DEBUG)
// Catching and just swallowing an exception means we need to tell
// the SO code that it should go back to normal operation, as it
// currently thinks that the exception is still on the fly.
GetThread()->GetCurrentStackGuard()->RestoreCurrentGuard();
#endif
}
PAL_ENDTRY;
#undef PAL_TRY_ARG
#undef PAL_TRY_REFARG
END_CONTRACT_VIOLATION;
LOG((LF_INTEROP, LL_EVERYTHING, hr == S_OK ? "QI Succeeded\n" : "QI Failed\n"));
2015-01-30 14:14:42 -08:00
// Ensure if the QI returned ok that it actually set a pointer.
if (hr == S_OK)
{
if (*pResUnk == NULL)
hr = E_NOINTERFACE;
}
GCX_PREEMP_NO_DTOR_END();
return hr;
}
//--------------------------------------------------------------------------------
// QI helper, must be called in preemptive mode. Faster than the MODE_ANY version
2015-01-30 14:14:42 -08:00
// because it doesn't need to toggle the mode. Use this version only if you already
// know that you're in preemptive mode for other reasons.
HRESULT SafeQueryInterfacePreemp(IUnknown* pUnk, REFIID riid, IUnknown** pResUnk)
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_MODE_PREEMPTIVE;
_ASSERTE(pUnk);
_ASSERTE(pResUnk);
Thread * const pThread = GetThreadNULLOk();
*pResUnk = NULL;
HRESULT hr = E_FAIL;
BEGIN_CONTRACT_VIOLATION(ThrowsViolation); // message pump could happen, so arbitrary managed code could run
struct Param { HRESULT * const hr; IUnknown** const pUnk; REFIID riid; IUnknown*** const pResUnk; } param = { &hr, &pUnk, riid, &pResUnk };
#define PAL_TRY_ARG(argName) (*(pParam->argName))
#define PAL_TRY_REFARG(argName) (pParam->argName)
PAL_TRY(Param * const, pParam, &param)
{
PAL_TRY_ARG(hr) = PAL_TRY_ARG(pUnk)->QueryInterface(PAL_TRY_REFARG(riid), (void**) PAL_TRY_ARG(pResUnk));
2015-01-30 14:14:42 -08:00
}
PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
#if defined(STACK_GUARDS_DEBUG)
// Catching and just swallowing an exception means we need to tell
// the SO code that it should go back to normal operation, as it
// currently thinks that the exception is still on the fly.
GetThread()->GetCurrentStackGuard()->RestoreCurrentGuard();
#endif
}
PAL_ENDTRY;
#undef PAL_TRY_ARG
#undef PAL_TRY_REFARG
END_CONTRACT_VIOLATION;
LOG((LF_INTEROP, LL_EVERYTHING, hr == S_OK ? "QI Succeeded\n" : "QI Failed\n"));
2015-01-30 14:14:42 -08:00
// Ensure if the QI returned ok that it actually set a pointer.
if (hr == S_OK)
{
if (*pResUnk == NULL)
hr = E_NOINTERFACE;
}
return hr;
}
#include <optdefault.h>
#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
2015-01-30 14:14:42 -08:00
//--------------------------------------------------------------------------------
// Cleanup helpers
//--------------------------------------------------------------------------------
void MinorCleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION( GCHeapUtilities::IsGCInProgress() || ( (g_fEEShutDown & ShutDown_SyncBlock) && g_fProcessDetach ) );
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
#ifdef FEATURE_COMINTEROP
2015-01-30 14:14:42 -08:00
// No need to notify the thread that the RCW is in use here.
// This is a privileged function called during GC or shutdown.
RCW* pRCW = pInteropInfo->GetRawRCW();
if (pRCW)
pRCW->MinorCleanup();
#endif // FEATURE_COMINTEROP
#ifdef FEATURE_COMWRAPPERS
void* eoc;
if (pInteropInfo->TryGetExternalComObjectContext(&eoc))
ComWrappersNative::MarkExternalComObjectContextCollected(eoc);
#endif // FEATURE_COMWRAPPERS
2015-01-30 14:14:42 -08:00
}
void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
if ((g_fEEShutDown & ShutDown_SyncBlock) && g_fProcessDetach )
MinorCleanupSyncBlockComData(pInteropInfo);
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
ComClassFactory* pComClassFactory = pInteropInfo->GetComClassFactory();
if (pComClassFactory)
{
delete pComClassFactory;
pInteropInfo->SetComClassFactory(NULL);
}
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
#ifdef FEATURE_COMINTEROP
2015-01-30 14:14:42 -08:00
// No need to notify the thread that the RCW is in use here.
// This is only called during finalization of a __ComObject so no one
// else could have a reference to this object.
RCW* pRCW = pInteropInfo->GetRawRCW();
if (pRCW)
{
pInteropInfo->SetRawRCW(NULL);
pRCW->Cleanup();
}
ComCallWrapper* pCCW = pInteropInfo->GetCCW();
if (pCCW)
{
pInteropInfo->SetCCW(NULL);
pCCW->Cleanup();
}
#endif // FEATURE_COMINTEROP
#ifdef FEATURE_COMWRAPPERS
2020-06-16 20:10:20 -07:00
pInteropInfo->ClearManagedObjectComWrappers(&ComWrappersNative::DestroyManagedObjectComWrapper);
void* eoc;
if (pInteropInfo->TryGetExternalComObjectContext(&eoc))
{
(void)pInteropInfo->TrySetExternalComObjectContext(NULL, eoc);
ComWrappersNative::DestroyExternalComObjectContext(eoc);
}
#endif // FEATURE_COMWRAPPERS
2015-01-30 14:14:42 -08:00
}
#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
#ifdef FEATURE_COMINTEROP
2015-01-30 14:14:42 -08:00
//--------------------------------------------------------------------------------
// Helper to release all of the RCWs in the specified context across all caches.
// If pCtxCookie is NULL, release all RCWs
2020-11-11 19:16:53 -08:00
static void ReleaseRCWsInCaches(LPVOID pCtxCookie)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pCtxCookie, NULL_OK));
}
CONTRACTL_END;
2022-11-09 21:57:59 -08:00
// Release all the RCW's that live in the AppDomain.
AppDomain::GetCurrentDomain()->ReleaseRCWs(pCtxCookie);
2015-01-30 14:14:42 -08:00
if (!g_fEEShutDown)
{
GCX_COOP();
2015-01-30 14:14:42 -08:00
// If the finalizer thread has sync blocks to clean up or if it is in the process
// of cleaning up the sync blocks, we need to wait for it to finish.
if (FinalizerThread::GetFinalizerThread()->RequireSyncBlockCleanup() || SyncBlockCache::GetSyncBlockCache()->IsSyncBlockCleanupInProgress())
FinalizerThread::FinalizerThreadWait();
// If more sync blocks were added while the finalizer thread was calling the finalizers
// or while it was transitioning into a context to clean up the IP's, we need to wake
// it up again to have it clean up the newly added sync blocks.
if (FinalizerThread::GetFinalizerThread()->RequireSyncBlockCleanup() || SyncBlockCache::GetSyncBlockCache()->IsSyncBlockCleanupInProgress())
FinalizerThread::FinalizerThreadWait();
}
}
2020-11-11 19:16:53 -08:00
void ReleaseRCWsInCachesNoThrow(LPVOID pCtxCookie)
{
CONTRACTL
{
DISABLED(NOTHROW);
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pCtxCookie, NULL_OK));
}
CONTRACTL_END;
EX_TRY
{
ReleaseRCWsInCaches(pCtxCookie);
}
EX_CATCH
{
}
EX_END_CATCH(SwallowAllExceptions);
}
2015-01-30 14:14:42 -08:00
//--------------------------------------------------------------------------------
// Marshalling Helpers
//--------------------------------------------------------------------------------
// Convert an IUnknown to CCW, returns NULL if the pUnk is not on
// a managed tear-off (OR) if the pUnk is to a managed tear-off that
// has been aggregated
ComCallWrapper* GetCCWFromIUnknown(IUnknown* pUnk, BOOL bEnableCustomization)
{
CONTRACT (ComCallWrapper*)
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pUnk));
POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
}
CONTRACT_END;
2015-01-30 14:14:42 -08:00
ComCallWrapper* pWrap = MapIUnknownToWrapper(pUnk);
if (pWrap != NULL)
{
// check if this wrapper is aggregated
if (pWrap->GetOuter() != NULL)
{
pWrap = NULL;
}
}
2015-01-30 14:14:42 -08:00
RETURN pWrap;
}
HRESULT LoadRegTypeLib(_In_ REFGUID guid,
_In_ unsigned short wVerMajor,
_In_ unsigned short wVerMinor,
_Outptr_ ITypeLib **pptlib)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
*pptlib = NULL;
GCX_PREEMP();
BSTRHolder wzPath;
HRESULT hr = S_OK;
EX_TRY
{
hr = QueryPathOfRegTypeLib(guid, wVerMajor, wVerMinor, LOCALE_USER_DEFAULT, &wzPath);
if (SUCCEEDED(hr))
{
#ifdef HOST_64BIT
REGKIND rk = (REGKIND)(REGKIND_NONE | LOAD_TLB_AS_64BIT);
#else
REGKIND rk = (REGKIND)(REGKIND_NONE | LOAD_TLB_AS_32BIT);
#endif // HOST_64BIT
hr = LoadTypeLibEx(wzPath, rk, pptlib);
}
2015-01-30 14:14:42 -08:00
}
EX_CATCH
{
hr = GET_EXCEPTION()->GetHR();
}
EX_END_CATCH(SwallowAllExceptions);
return hr;
}
VOID EnsureComStarted(BOOL fCoInitCurrentThread)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
2021-03-25 14:26:44 -07:00
PRECONDITION(GetThreadNULLOk() || !fCoInitCurrentThread);
2015-01-30 14:14:42 -08:00
PRECONDITION(g_fEEStarted);
}
CONTRACTL_END;
if (g_fComStarted == FALSE)
{
FinalizerThread::GetFinalizerThread()->SetRequiresCoInitialize();
// Attempt to set the thread's apartment model (to MTA by default). May not
// succeed (if someone beat us to the punch). That doesn't matter (since
// COM+ objects are now apartment agile), we only care that a CoInitializeEx
// has been performed on this thread by us.
if (fCoInitCurrentThread)
2020-07-07 08:05:20 -07:00
GetThread()->SetApartment(Thread::AS_InMTA);
2015-01-30 14:14:42 -08:00
// set the finalizer event
FinalizerThread::EnableFinalization();
g_fComStarted = TRUE;
}
}
HRESULT EnsureComStartedNoThrow(BOOL fCoInitCurrentThread)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(g_fEEStarted);
2021-03-25 14:26:44 -07:00
PRECONDITION(GetThreadNULLOk() != NULL); // Should always be inside BEGIN_EXTERNAL_ENTRYPOINT
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
HRESULT hr = S_OK;
if (!g_fComStarted)
{
GCX_COOP();
EX_TRY
{
EnsureComStarted(fCoInitCurrentThread);
}
EX_CATCH_HRESULT(hr);
}
return hr;
}
#include <optsmallperfcritical.h>
//--------------------------------------------------------------------------------
// AddRef helper, enables and disables GC during call-outs
ULONG SafeAddRef(IUnknown* pUnk)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
ULONG res = ~0;
if (pUnk == NULL)
return res;
GCX_PREEMP_NO_DTOR();
// @TODO: Consider special-casing this when we know it's one of ours so
// that we can avoid having to 'leave' and then 'enter'.
CONTRACT_VIOLATION(ThrowsViolation); // arbitrary managed code could run
res = pUnk->AddRef();
2015-01-30 14:14:42 -08:00
GCX_PREEMP_NO_DTOR_END();
return res;
}
//--------------------------------------------------------------------------------
// AddRef helper, must be called in preemptive mode. Only use this variant if
// you already know you're in preemptive mode for other reasons.
2015-01-30 14:14:42 -08:00
ULONG SafeAddRefPreemp(IUnknown* pUnk)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_PREEMPTIVE;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
ULONG res = ~0;
if (pUnk == NULL)
return res;
// @TODO: Consider special-casing this when we know it's one of ours so
// that we can avoid having to 'leave' and then 'enter'.
CONTRACT_VIOLATION(ThrowsViolation); // arbitrary managed code could run
res = pUnk->AddRef();
2015-01-30 14:14:42 -08:00
return res;
}
#include <optdefault.h>
//--------------------------------------------------------------------------------
// Ole RPC seems to return an inconsistent SafeArray for arrays created with
// SafeArrayVector(VT_BSTR). OleAut's SafeArrayGetVartype() doesn't notice
// the inconsistency and returns a valid-seeming (but wrong vartype.)
// Our version is more discriminating. This should only be used for
// marshaling scenarios where we can assume unmanaged code permissions
// (and hence are already in a position of trusting unmanaged data.)
HRESULT ClrSafeArrayGetVartype(_In_ SAFEARRAY *psa, _Out_ VARTYPE *pvt)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(psa));
PRECONDITION(CheckPointer(pvt));
}
CONTRACTL_END;
if (pvt == NULL || psa == NULL)
{
// This is the HRESULT returned by OLEAUT if either of the args are null.
2015-01-30 14:14:42 -08:00
return E_INVALIDARG;
}
2015-01-30 14:14:42 -08:00
USHORT fFeatures = psa->fFeatures;
USHORT hardwiredType = (fFeatures & (FADF_BSTR|FADF_UNKNOWN|FADF_DISPATCH|FADF_VARIANT));
2015-01-30 14:14:42 -08:00
if (hardwiredType == FADF_BSTR && psa->cbElements == sizeof(BSTR))
{
*pvt = VT_BSTR;
return S_OK;
}
else if (hardwiredType == FADF_UNKNOWN && psa->cbElements == sizeof(IUnknown*))
{
*pvt = VT_UNKNOWN;
return S_OK;
}
else if (hardwiredType == FADF_DISPATCH && psa->cbElements == sizeof(IDispatch*))
{
*pvt = VT_DISPATCH;
return S_OK;
}
else if (hardwiredType == FADF_VARIANT && psa->cbElements == sizeof(VARIANT))
{
*pvt = VT_VARIANT;
return S_OK;
}
else
{
_ASSERTE(GetModuleHandleA("oleaut32.dll") != NULL);
// We have got a SAFEARRAY. Oleaut32.dll should have been loaded.
CONTRACT_VIOLATION(ThrowsViolation);
return ::SafeArrayGetVartype(psa, pvt);
}
}
//--------------------------------------------------------------------------------
// // safe VariantChangeType
// Release helper, enables and disables GC during call-outs
2016-02-01 10:48:17 -08:00
HRESULT SafeVariantChangeType(_Inout_ VARIANT* pVarRes, _In_ VARIANT* pVarSrc,
2015-01-30 14:14:42 -08:00
unsigned short wFlags, VARTYPE vt)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pVarRes));
PRECONDITION(CheckPointer(pVarSrc));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
HRESULT hr = S_OK;
if (pVarRes)
{
GCX_PREEMP();
EX_TRY
{
hr = VariantChangeType(pVarRes, pVarSrc, wFlags, vt);
2015-01-30 14:14:42 -08:00
}
EX_CATCH
{
hr = GET_EXCEPTION()->GetHR();
}
EX_END_CATCH(SwallowAllExceptions);
}
return hr;
}
//--------------------------------------------------------------------------------
2016-02-01 10:48:17 -08:00
HRESULT SafeVariantChangeTypeEx(_Inout_ VARIANT* pVarRes, _In_ VARIANT* pVarSrc,
2015-01-30 14:14:42 -08:00
LCID lcid, unsigned short wFlags, VARTYPE vt)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pVarRes));
PRECONDITION(CheckPointer(pVarSrc));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
GCX_PREEMP();
_ASSERTE(GetModuleHandleA("oleaut32.dll") != NULL);
CONTRACT_VIOLATION(ThrowsViolation);
HRESULT hr = VariantChangeTypeEx (pVarRes, pVarSrc,lcid,wFlags,vt);
2015-01-30 14:14:42 -08:00
return hr;
}
//--------------------------------------------------------------------------------
void SafeVariantInit(VARIANT* pVar)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pVar));
}
CONTRACTL_END;
// From the oa sources
V_VT(pVar) = VT_EMPTY;
}
//--------------------------------------------------------------------------------
// void SafeReleaseStream(IStream *pStream)
void SafeReleaseStream(IStream *pStream)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pStream));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
GCX_PREEMP();
{
HRESULT hr = CoReleaseMarshalData(pStream);
#ifdef _DEBUG
UTF8 logStr[512];
sprintf_s(logStr, ARRAY_SIZE(logStr), "Object gone: CoReleaseMarshalData returned %x, file %s, line %d\n", hr, __FILE__, __LINE__);
2015-01-30 14:14:42 -08:00
LogInterop(logStr);
if (hr != S_OK)
{
2020-01-23 20:40:29 +02:00
// Reset the stream to the beginning
2015-01-30 14:14:42 -08:00
LARGE_INTEGER li;
LISet32(li, 0);
ULARGE_INTEGER li2;
pStream->Seek(li, STREAM_SEEK_SET, &li2);
hr = CoReleaseMarshalData(pStream);
sprintf_s(logStr, ARRAY_SIZE(logStr), "Object gone: CoReleaseMarshalData returned %x, file %s, line %d\n", hr, __FILE__, __LINE__);
2015-01-30 14:14:42 -08:00
LogInterop(logStr);
}
#endif
}
ULONG cbRef = SafeReleasePreemp(pStream);
LogInteropRelease(pStream, cbRef, "Release marshal Stream");
}
//---------------------------------------------------------------------------
// is the iid represent an IClassX for this class
BOOL IsIClassX(MethodTable *pMT, REFIID riid, ComMethodTable **ppComMT)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(CheckPointer(ppComMT));
}
CONTRACTL_END;
// Walk up the hierarchy starting at the specified method table and compare
// the IID's of the IClassX's against the specified IID.
while (pMT != NULL)
{
ComCallWrapperTemplate *pTemplate = ComCallWrapperTemplate::GetTemplate(pMT);
if (pTemplate->SupportsIClassX())
{
ComMethodTable *pComMT =
ComCallWrapperTemplate::SetupComMethodTableForClass(pMT, FALSE);
_ASSERTE(pComMT);
if (IsEqualIID(riid, pComMT->GetIID()))
{
*ppComMT = pComMT;
return TRUE;
}
}
pMT = pMT->GetComPlusParentMethodTable();
}
return FALSE;
}
//---------------------------------------------------------------------------
// Returns TRUE if we support IClassX (the auto-generated class interface)
// for the given class.
BOOL ClassSupportsIClassX(MethodTable *pMT)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
return TRUE;
}
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
//---------------------------------------------------------------------------
// OBJECTREF AllocateComObject_ForManaged(MethodTable* pMT)
OBJECTREF AllocateComObject_ForManaged(MethodTable* pMT)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(pMT->IsComObjectType());
}
CONTRACTL_END;
// Calls to COM up ahead.
HRESULT hr = S_OK;
EnsureComStarted();
ComClassFactory *pComClsFac = (ComClassFactory *)GetComClassFactory(pMT);
return pComClsFac->CreateInstance(pMT, TRUE);
}
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
//---------------------------------------------------------------------------
// This method returns the default interface for the class.
DefaultInterfaceType GetDefaultInterfaceForClassInternal(TypeHandle hndClass, TypeHandle *pHndDefClass)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(!hndClass.IsNull());
PRECONDITION(CheckPointer(pHndDefClass));
PRECONDITION(!hndClass.GetMethodTable()->IsInterface());
}
CONTRACTL_END;
// Set ppDefComMT to NULL before we start.
*pHndDefClass = TypeHandle();
HRESULT hr = S_FALSE;
MethodTable* pClassMT = hndClass.GetMethodTable();
const void* pvData;
ULONG cbData;
CorClassIfaceAttr ClassItfType;
BOOL bComVisible;
PREFIX_ASSUME(pClassMT != NULL);
if (pClassMT->IsComImport())
{
ClassItfType = clsIfNone;
bComVisible = TRUE;
}
else
{
ClassItfType = pClassMT->GetComClassInterfaceType();
bComVisible = IsTypeVisibleFromCom(hndClass);
}
// If the class is not COM visible, then its default interface is IUnknown.
if (!bComVisible)
return DefaultInterfaceType_IUnknown;
2015-01-30 14:14:42 -08:00
// Start by checking for the ComDefaultInterface attribute.
2019-05-20 15:15:52 -07:00
hr = pClassMT->GetCustomAttribute(WellKnownAttribute::ComDefaultInterface, &pvData, &cbData);
2015-01-30 14:14:42 -08:00
IfFailThrow(hr);
if (hr == S_OK && cbData > 2)
{
TypeHandle DefItfType;
AppDomain *pCurrDomain = SystemDomain::GetCurrentDomain();
CustomAttributeParser cap(pvData, cbData);
IfFailThrow(cap.SkipProlog());
LPCUTF8 szStr;
ULONG cbStr;
IfFailThrow(cap.GetNonNullString(&szStr, &cbStr));
2015-01-30 14:14:42 -08:00
// Allocate a new buffer that will contain the name of the default COM interface.
StackSString defItf(SString::Utf8, szStr, cbStr);
// Load the default COM interface specified in the CA.
{
GCX_COOP();
DefItfType = TypeName::GetTypeReferencedByCustomAttribute(defItf.GetUnicode(), pClassMT->GetAssembly());
2015-01-30 14:14:42 -08:00
// If the type handle isn't a named type, then throw an exception using
// the name of the type obtained from pCurrInterfaces.
if (!DefItfType.GetMethodTable())
{
// This should only occur for TypeDesc's.
StackSString ssClassName;
DefineFullyQualifiedNameForClassW()
COMPlusThrow(kTypeLoadException, IDS_EE_INVALIDCOMDEFITF,
GetFullyQualifiedNameForClassW(pClassMT),
defItf.GetUnicode());
}
// Otherwise, if the type is not an interface thrown an exception using the actual
// name of the type.
if (!DefItfType.IsInterface())
{
StackSString ssClassName;
StackSString ssInvalidItfName;
pClassMT->_GetFullyQualifiedNameForClass(ssClassName);
DefItfType.GetMethodTable()->_GetFullyQualifiedNameForClass(ssInvalidItfName);
COMPlusThrow(kTypeLoadException, IDS_EE_INVALIDCOMDEFITF,
ssClassName.GetUnicode(), ssInvalidItfName.GetUnicode());
}
// Make sure the class implements the interface.
if (!pClassMT->CanCastToInterface(DefItfType.GetMethodTable()))
2015-01-30 14:14:42 -08:00
{
StackSString ssClassName;
StackSString ssInvalidItfName;
pClassMT->_GetFullyQualifiedNameForClass(ssClassName);
DefItfType.GetMethodTable()->_GetFullyQualifiedNameForClass(ssInvalidItfName);
COMPlusThrow(kTypeLoadException, IDS_EE_COMDEFITFNOTSUPPORTED,
ssClassName.GetUnicode(), ssInvalidItfName.GetUnicode());
}
}
// The default interface is valid so return it.
*pHndDefClass = DefItfType;
return DefaultInterfaceType_Explicit;
}
// If the class's interface type is AutoDispatch or AutoDual then return either the
2015-01-30 14:14:42 -08:00
// IClassX for the current class or IDispatch.
if (ClassItfType != clsIfNone)
{
*pHndDefClass = hndClass;
return ClassItfType == clsIfAutoDisp ? DefaultInterfaceType_AutoDispatch : DefaultInterfaceType_AutoDual;
}
// The class interface is set to NONE for this level of the hierarchy. So we need to check
// to see if this class implements an interface.
// Search for the first COM visible implemented interface. We start with the most
// derived class and work our way up the hierarchy.
2015-01-30 14:14:42 -08:00
for (MethodTable *pParentMT = pClassMT->GetParentMethodTable(); pParentMT; pParentMT = pParentMT->GetParentMethodTable())
{
MethodTable::InterfaceMapIterator it = pClassMT->IterateInterfaceMap();
while (it.Next())
{
MethodTable *pItfMT = it.GetInterfaceInfo()->GetApproxMethodTable(pClassMT->GetLoaderModule());
2015-01-30 14:14:42 -08:00
// Skip generic interfaces. Classic COM interop does not support these and we don't
// use the result of this function in WinRT scenarios. WinRT parameter marshaling
// doesn't come here at all because the default interface is always specified using
// the DefaultAttribute. Field marshaling does come here but WinRT does not support
// fields of reference types other than string.
if (!pItfMT->HasInstantiation())
{
// If the interface is visible from COM and not implemented by our parent,
2015-01-30 14:14:42 -08:00
// then use it as the default.
if (IsTypeVisibleFromCom(TypeHandle(pItfMT)) && !pParentMT->ImplementsInterface(pItfMT))
{
*pHndDefClass = TypeHandle(pItfMT);
return DefaultInterfaceType_Explicit;
}
}
}
}
// If the class is a COM import with no interfaces, then its default interface will
// be IUnknown.
if (pClassMT->IsComImport())
return DefaultInterfaceType_IUnknown;
// If we have a managed parent class then return its default interface.
MethodTable *pParentClass = pClassMT->GetComPlusParentMethodTable();
if (pParentClass)
return GetDefaultInterfaceForClassWrapper(TypeHandle(pParentClass), pHndDefClass);
// Check to see if the class is an extensible RCW.
if (pClassMT->IsComObjectType())
return DefaultInterfaceType_BaseComClass;
// The class has no interfaces and is marked as ClassInterfaceType.None.
return DefaultInterfaceType_IUnknown;
}
DefaultInterfaceType GetDefaultInterfaceForClassWrapper(TypeHandle hndClass, TypeHandle *pHndDefClass)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(!hndClass.IsNull());
}
CONTRACTL_END;
if (!hndClass.IsTypeDesc())
{
ComCallWrapperTemplate *pTemplate = hndClass.AsMethodTable()->GetComCallWrapperTemplate();
if (pTemplate != NULL)
{
// if CCW template is available, use its cache
MethodTable *pDefaultItf;
DefaultInterfaceType itfType = pTemplate->GetDefaultInterface(&pDefaultItf);
*pHndDefClass = TypeHandle(pDefaultItf);
return itfType;
}
}
return GetDefaultInterfaceForClassInternal(hndClass, pHndDefClass);
}
HRESULT TryGetDefaultInterfaceForClass(TypeHandle hndClass, TypeHandle *pHndDefClass, DefaultInterfaceType *pDefItfType)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(!hndClass.IsNull());
PRECONDITION(CheckPointer(pHndDefClass));
PRECONDITION(CheckPointer(pDefItfType));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
GCX_COOP();
2015-01-30 14:14:42 -08:00
HRESULT hr = S_OK;
OBJECTREF pThrowable = NULL;
2015-01-30 14:14:42 -08:00
GCPROTECT_BEGIN(pThrowable)
{
EX_TRY
{
*pDefItfType = GetDefaultInterfaceForClassWrapper(hndClass, pHndDefClass);
}
EX_CATCH
{
pThrowable = GET_THROWABLE();
}
EX_END_CATCH(SwallowAllExceptions);
if (pThrowable != NULL)
hr = SetupErrorInfo(pThrowable);
}
GCPROTECT_END();
return hr;
}
// Returns the default interface for a class if it's an explicit interface or the AutoDual
// class interface. Sets *pbDispatch otherwise. This is the logic used by array marshaling
// in code:OleVariant::MarshalInterfaceArrayComToOleHelper.
MethodTable *GetDefaultInterfaceMTForClass(MethodTable *pMT, BOOL *pbDispatch)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(!pMT->IsInterface());
PRECONDITION(CheckPointer(pbDispatch));
}
CONTRACTL_END;
TypeHandle hndDefItfClass;
DefaultInterfaceType DefItfType = GetDefaultInterfaceForClassWrapper(TypeHandle(pMT), &hndDefItfClass);
switch (DefItfType)
{
case DefaultInterfaceType_Explicit:
case DefaultInterfaceType_AutoDual:
{
return hndDefItfClass.GetMethodTable();
}
case DefaultInterfaceType_IUnknown:
case DefaultInterfaceType_BaseComClass:
{
*pbDispatch = FALSE;
return NULL;
}
case DefaultInterfaceType_AutoDispatch:
{
*pbDispatch = TRUE;
return NULL;
}
default:
{
_ASSERTE(!"Invalid default interface type!");
return NULL;
}
}
}
//---------------------------------------------------------------------------
// This method retrieves the list of source interfaces for a given class.
void GetComSourceInterfacesForClass(MethodTable *pMT, CQuickArray<MethodTable *> &rItfList)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(CheckPointer(pMT));
}
CONTRACTL_END;
HRESULT hr = S_OK;
const void* pvData;
ULONG cbData;
CQuickArray<CHAR> qbCurrInterfaces;
GCX_COOP();
// Reset the size of the interface list to 0.
rItfList.Shrink(0);
// Starting at the specified class MT retrieve the COM source interfaces
2015-01-30 14:14:42 -08:00
// of all the striped of the hierarchy.
for (; pMT != NULL; pMT = pMT->GetParentMethodTable())
{
// See if there is any [source] interface at this level of the hierarchy.
2019-05-20 15:15:52 -07:00
hr = pMT->GetCustomAttribute(WellKnownAttribute::ComSourceInterfaces, &pvData, &cbData);
2015-01-30 14:14:42 -08:00
IfFailThrow(hr);
if (hr == S_OK && cbData > 2)
{
AppDomain *pCurrDomain = SystemDomain::GetCurrentDomain();
CustomAttributeParser cap(pvData, cbData);
IfFailThrow(cap.SkipProlog());
while (cap.BytesLeft() != 0)
{
// Uncompress the current string of source interfaces.
BYTE const *pbStr;
ULONG cbStr;
IfFailThrow(cap.GetData(&pbStr, &cbStr));
2015-01-30 14:14:42 -08:00
// Allocate a new buffer that will contain the current list of source interfaces.
qbCurrInterfaces.ReSizeThrows(cbStr + 1);
LPUTF8 strCurrInterfaces = qbCurrInterfaces.Ptr();
memcpyNoGCRefs(strCurrInterfaces, pbStr, cbStr);
strCurrInterfaces[cbStr] = 0;
LPUTF8 pCurrInterfaces = strCurrInterfaces;
LPUTF8 pCurrInterfacesEnd = pCurrInterfaces + cbStr + 1;
while (pCurrInterfaces < pCurrInterfacesEnd && *pCurrInterfaces != 0)
{
// Load the COM source interface specified in the CA.
TypeHandle ItfType;
ItfType = TypeName::GetTypeReferencedByCustomAttribute(pCurrInterfaces, pMT->GetAssembly());
2015-01-30 14:14:42 -08:00
// If the type handle isn't a named type, then throw an exception using
// the name of the type obtained from pCurrInterfaces.
if (!ItfType.GetMethodTable())
{
// This should only occur for TypeDesc's.
StackSString ssInvalidItfName(SString::Utf8, pCurrInterfaces);
DefineFullyQualifiedNameForClassW()
COMPlusThrow(kTypeLoadException, IDS_EE_INVALIDCOMSOURCEITF,
GetFullyQualifiedNameForClassW(pMT),
ssInvalidItfName.GetUnicode());
}
// Otherwise, if the type is not an interface thrown an exception using the actual
// name of the type.
if (!ItfType.IsInterface())
{
StackSString ssClassName;
StackSString ssInvalidItfName;
pMT->_GetFullyQualifiedNameForClass(ssClassName);
ItfType.GetMethodTable()->_GetFullyQualifiedNameForClass(ssInvalidItfName);
COMPlusThrow(kTypeLoadException, IDS_EE_INVALIDCOMSOURCEITF,
ssClassName.GetUnicode(), ssInvalidItfName.GetUnicode());
}
// Ensure the source interface is not generic.
if (ItfType.HasInstantiation())
{
StackSString ssClassName;
StackSString ssInvalidItfName;
pMT->_GetFullyQualifiedNameForClass(ssClassName);
ItfType.GetMethodTable()->_GetFullyQualifiedNameForClass(ssInvalidItfName);
COMPlusThrow(kTypeLoadException, IDS_EE_INVALIDCOMSOURCEITF,
ssClassName.GetUnicode(), ssInvalidItfName.GetUnicode());
}
// Retrieve the IID of the COM source interface.
IID ItfIID;
ItfType.GetMethodTable()->GetGuid(&ItfIID, TRUE);
2015-01-30 14:14:42 -08:00
// Go through the list of source interfaces and check to see if the new one is a duplicate.
// It can be a duplicate either if it is the same interface or if it has the same IID.
BOOL bItfInList = FALSE;
for (UINT i = 0; i < rItfList.Size(); i++)
{
if (rItfList[i] == ItfType.GetMethodTable())
{
bItfInList = TRUE;
break;
}
IID ItfIID2;
rItfList[i]->GetGuid(&ItfIID2, TRUE);
if (IsEqualIID(ItfIID, ItfIID2))
{
bItfInList = TRUE;
break;
}
}
// If the COM source interface is not in the list then add it.
if (!bItfInList)
{
size_t OldSize = rItfList.Size();
rItfList.ReSizeThrows(OldSize + 1);
rItfList[OldSize] = ItfType.GetMethodTable();
}
// Process the next COM source interfaces in the CA.
pCurrInterfaces += strlen(pCurrInterfaces) + 1;
}
}
}
}
}
//--------------------------------------------------------------------------------
// These methods convert a native IEnumVARIANT to a managed IEnumerator.
OBJECTREF ConvertEnumVariantToMngEnum(IEnumVARIANT *pNativeEnum)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
OBJECTREF MngEnum = NULL;
OBJECTREF EnumeratorToEnumVariantMarshaler = NULL;
GCPROTECT_BEGIN(EnumeratorToEnumVariantMarshaler)
{
// Retrieve the custom marshaler and the MD to use to convert the IEnumVARIANT.
StdMngIEnumerator *pStdMngIEnumInfo = SystemDomain::GetCurrentDomain()->GetMngStdInterfacesInfo()->GetStdMngIEnumerator();
MethodDesc *pEnumNativeToManagedMD = pStdMngIEnumInfo->GetCustomMarshalerMD(CustomMarshalerMethods_MarshalNativeToManaged);
EnumeratorToEnumVariantMarshaler = pStdMngIEnumInfo->GetCustomMarshaler();
MethodDescCallSite enumNativeToManaged(pEnumNativeToManagedMD, &EnumeratorToEnumVariantMarshaler);
// Prepare the arguments that will be passed to MarshalNativeToManaged.
ARG_SLOT MarshalNativeToManagedArgs[] = {
ObjToArgSlot(EnumeratorToEnumVariantMarshaler),
(ARG_SLOT)pNativeEnum
};
// Retrieve the managed view for the current native interface pointer.
MngEnum = enumNativeToManaged.Call_RetOBJECTREF(MarshalNativeToManagedArgs);
}
GCPROTECT_END();
return MngEnum;
}
//--------------------------------------------------------------------------------
// This method converts an OLE_COLOR to a System.Color.
void ConvertOleColorToSystemColor(OLE_COLOR SrcOleColor, SYSTEMCOLOR *pDestSysColor)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
// Retrieve the method desc to use for the current AD.
MethodDesc *pOleColorToSystemColorMD =
GetAppDomain()->GetLoaderAllocator()->GetMarshalingData()->GetOleColorMarshalingInfo()->GetOleColorToSystemColorMD();
2015-01-30 14:14:42 -08:00
MethodDescCallSite oleColorToSystemColor(pOleColorToSystemColorMD);
_ASSERTE(pOleColorToSystemColorMD->HasRetBuffArg());
ARG_SLOT Args[] =
2015-01-30 14:14:42 -08:00
{
PtrToArgSlot(pDestSysColor),
PtrToArgSlot(SrcOleColor)
};
oleColorToSystemColor.Call(Args);
}
//--------------------------------------------------------------------------------
// This method converts a System.Color to an OLE_COLOR.
OLE_COLOR ConvertSystemColorToOleColor(OBJECTREF *pSrcObj)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
// Retrieve the method desc to use for the current AD.
MethodDesc *pSystemColorToOleColorMD =
GetAppDomain()->GetLoaderAllocator()->GetMarshalingData()->GetOleColorMarshalingInfo()->GetSystemColorToOleColorMD();
2015-01-30 14:14:42 -08:00
MethodDescCallSite systemColorToOleColor(pSystemColorToOleColorMD);
// Set up the args and call the method.
SYSTEMCOLOR *pSrcSysColor = (SYSTEMCOLOR *)(*pSrcObj)->UnBox();
return systemColorToOleColor.CallWithValueTypes_RetOleColor((const ARG_SLOT *)&pSrcSysColor);
}
//--------------------------------------------------------------------------------
// This method generates a stringized version of a class interface that contains
2015-01-30 14:14:42 -08:00
// the signatures of all the methods and fields.
ULONG GetStringizedClassItfDef(TypeHandle InterfaceType, CQuickArray<BYTE> &rDef)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(!InterfaceType.IsNull());
}
CONTRACTL_END;
LPCWSTR szName;
2015-01-30 14:14:42 -08:00
ULONG cchName;
MethodTable* pIntfMT = InterfaceType.GetMethodTable();
PREFIX_ASSUME(pIntfMT != NULL);
2015-01-30 14:14:42 -08:00
MethodTable* pDeclaringMT = NULL;
DWORD nSlots; // Slots on the pseudo interface.
mdToken tkMb; // A method or field token.
ULONG cbCur;
HRESULT hr = S_OK;
ULONG i;
// Should be an actual class.
_ASSERTE(!pIntfMT->IsInterface());
// See what sort of IClassX this class gets.
TypeHandle thDefItf;
BOOL bGenerateMethods = FALSE;
DefaultInterfaceType DefItfType = GetDefaultInterfaceForClassWrapper(TypeHandle(pIntfMT), &thDefItf);
2015-01-30 14:14:42 -08:00
// The results apply to this class if the thDefItf is this class itself, not a parent class.
// A side effect is that [ComVisible(false)] types' guids are generated without members.
if (thDefItf.GetMethodTable() == pIntfMT && DefItfType == DefaultInterfaceType_AutoDual)
bGenerateMethods = TRUE;
// Get the name of the class.
DefineFullyQualifiedNameForClassW();
szName = GetFullyQualifiedNameForClassNestedAwareW(pIntfMT);
cchName = (ULONG)u16_strlen(szName);
2015-01-30 14:14:42 -08:00
// Start with the interface name.
cbCur = cchName * sizeof(WCHAR);
rDef.ReSizeThrows(cbCur + sizeof(WCHAR));
wcscpy_s(reinterpret_cast<LPWSTR>(rDef.Ptr()), rDef.Size()/sizeof(WCHAR), szName);
if (bGenerateMethods)
{
ComMTMemberInfoMap MemberMap(pIntfMT); // The map of members.
// Retrieve the method properties.
MemberMap.Init(sizeof(void*));
CQuickArray<ComMTMethodProps> &rProps = MemberMap.GetMethods();
nSlots = (DWORD)rProps.Size();
// Now add the methods to the TypeInfo.
for (i=0; i<nSlots; ++i)
{
ComMTMethodProps *pProps = &rProps[i];
if (pProps->bMemberVisible)
{
if (pProps->semantic < FieldSemanticOffset)
{
pDeclaringMT = pProps->pMeth->GetMethodTable();
tkMb = pProps->pMeth->GetMemberDef();
cbCur = GetStringizedMethodDef(pDeclaringMT, tkMb, rDef, cbCur);
}
else
{
ComCallMethodDesc *pFieldMeth; // A MethodDesc for a field call.
FieldDesc *pField; // A FieldDesc.
pFieldMeth = reinterpret_cast<ComCallMethodDesc*>(pProps->pMeth);
pField = pFieldMeth->GetFieldDesc();
pDeclaringMT = pField->GetApproxEnclosingMethodTable();
tkMb = pField->GetMemberDef();
cbCur = GetStringizedFieldDef(pDeclaringMT, tkMb, rDef, cbCur);
}
}
}
}
2015-01-30 14:14:42 -08:00
// Return the number of bytes.
return cbCur;
} // ULONG GetStringizedClassItfDef()
//--------------------------------------------------------------------------------
// Helper to get the GUID of a class interface.
void GenerateClassItfGuid(TypeHandle InterfaceType, GUID *pGuid)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(!InterfaceType.IsNull());
PRECONDITION(CheckPointer(pGuid));
}
CONTRACTL_END;
LPWSTR szName; // Name to turn to a guid.
ULONG cchName; // Length of the name (possibly after decoration).
CQuickArray<BYTE> rName; // Buffer to accumulate signatures.
ULONG cbCur; // Current offset.
HRESULT hr = S_OK; // A result.
cbCur = GetStringizedClassItfDef(InterfaceType, rName);
2015-01-30 14:14:42 -08:00
// Pad up to a whole WCHAR.
if (cbCur % sizeof(WCHAR))
{
int cbDelta = sizeof(WCHAR) - (cbCur % sizeof(WCHAR));
rName.ReSizeThrows(cbCur + cbDelta);
memset(rName.Ptr() + cbCur, 0, cbDelta);
cbCur += cbDelta;
}
// Point to the new buffer.
cchName = cbCur / sizeof(WCHAR);
szName = reinterpret_cast<LPWSTR>(rName.Ptr());
// Generate guid from name.
CorGuidFromNameW(pGuid, szName, cchName);
} // void GenerateClassItfGuid()
HRESULT TryGenerateClassItfGuid(TypeHandle InterfaceType, GUID *pGuid)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(!InterfaceType.IsNull());
PRECONDITION(CheckPointer(pGuid));
}
CONTRACTL_END;
GCX_COOP();
HRESULT hr = S_OK;
OBJECTREF pThrowable = NULL;
GCPROTECT_BEGIN(pThrowable)
{
EX_TRY
{
GenerateClassItfGuid(InterfaceType, pGuid);
}
EX_CATCH
{
pThrowable = GET_THROWABLE();
}
EX_END_CATCH (SwallowAllExceptions);
if (pThrowable != NULL)
hr = SetupErrorInfo(pThrowable);
}
GCPROTECT_END();
2015-01-30 14:14:42 -08:00
return hr;
}
//--------------------------------------------------------------------------------
// Helper to get the GUID of the typelib that is created from an assembly.
HRESULT GetTypeLibGuidForAssembly(Assembly *pAssembly, GUID *pGuid)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pAssembly));
PRECONDITION(CheckPointer(pGuid));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
HRESULT hr = S_OK;
CQuickArray<BYTE> rName; // String for guid.
ULONG cbData; // Size of the string in bytes.
// Get GUID from Assembly, else Generate from name.
hr = pAssembly->GetMDImport()->GetItemGuid(TokenFromRid(1, mdtAssembly), pGuid);
2015-01-30 14:14:42 -08:00
if (*pGuid == GUID_NULL)
{
// Get the string.
IfFailGo(GetStringizedTypeLibGuidForAssembly(pAssembly, rName, 0, &cbData));
2015-01-30 14:14:42 -08:00
// Pad to a whole WCHAR.
if (cbData % sizeof(WCHAR))
{
IfFailGo(rName.ReSizeNoThrow(cbData + sizeof(WCHAR)-(cbData%sizeof(WCHAR))));
while (cbData % sizeof(WCHAR))
rName[cbData++] = 0;
}
2015-01-30 14:14:42 -08:00
// Turn into guid
CorGuidFromNameW(pGuid, (LPWSTR)rName.Ptr(), cbData/sizeof(WCHAR));
}
ErrExit:
return hr;
} // HRESULT GetTypeLibGuidForAssembly()
//--------------------------------------------------------------------------------
// Helper to get the version of the typelib that is created from an assembly.
HRESULT GetTypeLibVersionForAssembly(
_In_ Assembly *pAssembly,
_Out_ USHORT *pMajorVersion,
_Out_ USHORT *pMinorVersion)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pAssembly));
PRECONDITION(CheckPointer(pMajorVersion));
PRECONDITION(CheckPointer(pMinorVersion));
}
CONTRACTL_END;
HRESULT hr;
const BYTE *pbData = nullptr;
ULONG cbData = 0;
2015-01-30 14:14:42 -08:00
// Check to see if the TypeLibVersionAttribute is set.
IfFailRet(pAssembly->GetMDImport()->GetCustomAttributeByName(TokenFromRid(1, mdtAssembly), INTEROP_TYPELIBVERSION_TYPE, (const void**)&pbData, &cbData));
// For attribute contents, see https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.typelibversionattribute
if (cbData >= (2 + 2 * sizeof(UINT32)))
2015-01-30 14:14:42 -08:00
{
CustomAttributeParser cap(pbData, cbData);
IfFailRet(cap.SkipProlog());
// Retrieve the major and minor version from the attribute.
UINT32 u4;
IfFailRet(cap.GetU4(&u4));
*pMajorVersion = GET_VERSION_USHORT_FROM_INT(u4);
IfFailRet(cap.GetU4(&u4));
*pMinorVersion = GET_VERSION_USHORT_FROM_INT(u4);
}
else
{
// Use the assembly's major and minor version number.
IfFailRet(pAssembly->GetVersion(pMajorVersion, pMinorVersion, nullptr, nullptr));
2015-01-30 14:14:42 -08:00
}
// Some system don't handle a typelib with a version of 0.0.
// When that happens, change it to 1.0.
2015-01-30 14:14:42 -08:00
if (*pMajorVersion == 0 && *pMinorVersion == 0)
*pMajorVersion = 1;
return S_OK;
2015-01-30 14:14:42 -08:00
} // HRESULT TypeLibExporter::GetTypeLibVersionFromAssembly()
//---------------------------------------------------------------------------
// This method determines if a member is visible from COM.
BOOL IsMethodVisibleFromCom(MethodDesc *pMD)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pMD));
}
CONTRACTL_END;
HRESULT hr = S_OK;
mdProperty pd;
2015-01-30 14:14:42 -08:00
LPCUTF8 pPropName;
ULONG uSemantic;
mdMethodDef md = pMD->GetMemberDef();
2015-01-30 14:14:42 -08:00
// See if there is property information for this member.
hr = pMD->GetModule()->GetPropertyInfoForMethodDef(md, &pd, &pPropName, &uSemantic);
IfFailThrow(hr);
if (hr == S_OK)
{
return IsMemberVisibleFromCom(pMD->GetMethodTable(), pd, md);
}
else
{
return IsMemberVisibleFromCom(pMD->GetMethodTable(), md, mdTokenNil);
}
}
//---------------------------------------------------------------------------
// This method determines if a type is visible from COM or not based on
2015-01-30 14:14:42 -08:00
// its visibility. This version of the method works with a type handle.
// This version will ignore a type's generic attributes.
//
// This API should *never* be called directly!!!
static BOOL SpecialIsGenericTypeVisibleFromCom(TypeHandle hndType)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(!hndType.IsNull());
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
DWORD dwFlags;
mdTypeDef tdEnclosingType;
HRESULT hr;
const BYTE * pVal;
ULONG cbVal;
MethodTable * pMT = hndType.GetMethodTable();
_ASSERTE(pMT);
2015-01-30 14:14:42 -08:00
mdTypeDef mdType = pMT->GetCl();
IMDInternalImport * pInternalImport = pMT->GetMDImport();
Assembly * pAssembly = pMT->GetAssembly();
2019-05-20 15:15:52 -07:00
Module * pModule = pMT->GetModule();
2015-01-30 14:14:42 -08:00
// If the type is a COM imported interface then it is visible from COM.
if (pMT->IsInterface() && pMT->IsComImport())
return TRUE;
// If the type is an array, then it is not visible from COM.
if (pMT->IsArray())
return FALSE;
2015-01-30 14:14:42 -08:00
// Retrieve the flags for the current type.
tdEnclosingType = mdType;
if (FAILED(pInternalImport->GetTypeDefProps(tdEnclosingType, &dwFlags, 0)))
{
return FALSE;
}
2015-01-30 14:14:42 -08:00
// Handle nested types.
while (IsTdNestedPublic(dwFlags))
{
hr = pInternalImport->GetNestedClassProps(tdEnclosingType, &tdEnclosingType);
if (FAILED(hr))
{
return FALSE;
}
2015-01-30 14:14:42 -08:00
// Retrieve the flags for the enclosing type.
if (FAILED(pInternalImport->GetTypeDefProps(tdEnclosingType, &dwFlags, 0)))
{
return FALSE;
}
}
2015-01-30 14:14:42 -08:00
// If the outermost type is not visible then the specified type is not visible.
if (!IsTdPublic(dwFlags))
return FALSE;
// Check to see if the type has the ComVisible attribute set.
2019-05-20 15:15:52 -07:00
hr = pModule->GetCustomAttribute(mdType, WellKnownAttribute::ComVisible, (const void**)&pVal, &cbVal);
2015-01-30 14:14:42 -08:00
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
if (FAILED(cap.SkipProlog()))
return FALSE;
UINT8 u1;
if (FAILED(cap.GetU1(&u1)))
return FALSE;
return (BOOL)u1;
}
// Check to see if the assembly has the ComVisible attribute set.
2019-05-20 15:15:52 -07:00
hr = pModule->GetCustomAttribute(pAssembly->GetManifestToken(), WellKnownAttribute::ComVisible, (const void**)&pVal, &cbVal);
2015-01-30 14:14:42 -08:00
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
if (FAILED(cap.SkipProlog()))
return FALSE;
UINT8 u1;
if (FAILED(cap.GetU1(&u1)))
return FALSE;
return (BOOL)u1;
}
// The type is visible.
return TRUE;
}
//---------------------------------------------------------------------------
// This method determines if a type is visible from COM or not based on
// its visibility. This version of the method works with a type handle.
BOOL IsTypeVisibleFromCom(TypeHandle hndType)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(!hndType.IsNull());
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
// If the type is a generic type, then it is not visible from COM.
if (hndType.HasInstantiation() || hndType.IsGenericVariable())
return FALSE;
2015-01-30 14:14:42 -08:00
return SpecialIsGenericTypeVisibleFromCom(hndType);
}
//--------------------------------------------------------------------------------
// Validate that the given target is valid for the specified type.
BOOL IsComTargetValidForType(REFLECTCLASSBASEREF* pRefClassObj, OBJECTREF* pTarget)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pRefClassObj));
PRECONDITION(CheckPointer(pTarget));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
MethodTable* pInvokedMT = (*pRefClassObj)->GetType().GetMethodTable();
MethodTable* pTargetMT = (*pTarget)->GetMethodTable();
2015-01-30 14:14:42 -08:00
_ASSERTE(pTargetMT);
PREFIX_ASSUME(pInvokedMT != NULL);
// If the target class and the invoke class are identical then the invoke is valid.
if (pTargetMT == pInvokedMT)
return TRUE;
// We always allow calling InvokeMember on a __ComObject type regardless of the type
// of the target object.
if (IsComObjectClass((*pRefClassObj)->GetType()))
return TRUE;
// If the class that is being invoked on is an interface then check to see if the
// target class supports that interface.
if (pInvokedMT->IsInterface())
return Object::SupportsInterface(*pTarget, pInvokedMT);
// Check to see if the target class inherits from the invoked class.
while (pTargetMT)
{
pTargetMT = pTargetMT->GetParentMethodTable();
if (pTargetMT == pInvokedMT)
{
// The target class inherits from the invoked class.
return TRUE;
}
}
// There is no valid relationship between the invoked and the target classes.
return FALSE;
}
2022-01-06 08:49:08 +02:00
DISPID ExtractStandardDispId(_In_z_ LPWSTR strStdDispIdMemberName)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
// Find the first character after the = in the standard DISPID member name.
LPWSTR strDispId = (LPWSTR)u16_strchr(&strStdDispIdMemberName[STANDARD_DISPID_PREFIX_LENGTH], W('=')) + 1;
2015-01-30 14:14:42 -08:00
if (!strDispId)
COMPlusThrow(kArgumentException, IDS_EE_INVALID_STD_DISPID_NAME);
// Validate that the last character of the standard member name is a ].
LPWSTR strClosingBracket = (LPWSTR)u16_strchr(strDispId, W(']'));
2015-01-30 14:14:42 -08:00
if (!strClosingBracket || (strClosingBracket[1] != 0))
COMPlusThrow(kArgumentException, IDS_EE_INVALID_STD_DISPID_NAME);
// Extract the number from the standard DISPID member name.
return _wtoi(strDispId);
}
static HRESULT InvokeExHelper(
IDispatchEx * pDispEx,
DISPID MemberID,
LCID lcid,
WORD flags,
DISPPARAMS * pDispParams,
VARIANT* pVarResult,
EXCEPINFO * pExcepInfo,
IServiceProvider *pspCaller)
{
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_MODE_ANY;
_ASSERTE(pDispEx != NULL);
struct Param : CallOutFilterParam {
HRESULT hr;
IDispatchEx * pDispEx;
DISPID MemberID;
LCID lcid;
WORD flags;
DISPPARAMS * pDispParams;
VARIANT* pVarResult;
EXCEPINFO * pExcepInfo;
IServiceProvider * pspCaller;
}; Param param;
2015-01-30 14:14:42 -08:00
param.OneShot = TRUE; // Inherited from CallOutFilterParam
param.hr = S_OK;
param.pDispEx = pDispEx;
param.MemberID = MemberID;
param.lcid = lcid;
param.flags = flags;
param.pDispParams = pDispParams;
param.pVarResult = pVarResult;
param.pExcepInfo = pExcepInfo;
param.pspCaller = pspCaller;
2015-01-30 14:14:42 -08:00
PAL_TRY(Param *, pParam, &param)
{
pParam->hr = pParam->pDispEx->InvokeEx(pParam->MemberID,
pParam->lcid,
pParam->flags,
pParam->pDispParams,
pParam->pVarResult,
pParam->pExcepInfo,
pParam->pspCaller);
}
PAL_EXCEPT_FILTER(CallOutFilter)
{
_ASSERTE(!"CallOutFilter returned EXECUTE_HANDLER.");
}
PAL_ENDTRY;
return param.hr;
}
static HRESULT InvokeHelper(
IDispatch * pDisp,
DISPID MemberID,
REFIID riid,
LCID lcid,
WORD flags,
DISPPARAMS * pDispParams,
VARIANT* pVarResult,
EXCEPINFO * pExcepInfo,
UINT *piArgErr)
{
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_MODE_ANY;
_ASSERTE(pDisp != NULL);
struct Param : CallOutFilterParam {
HRESULT hr;
IDispatch * pDisp;
DISPID MemberID;
REFIID riid;
LCID lcid;
WORD flags;
DISPPARAMS * pDispParams;
VARIANT * pVarResult;
EXCEPINFO * pExcepInfo;
UINT * piArgErr;
Param(REFIID _riid) : riid(_riid) {}
}; Param param(riid);
param.OneShot = TRUE; // Inherited from CallOutFilterParam
param.hr = S_OK;
param.pDisp = pDisp;
param.MemberID = MemberID;
//param.riid = riid;
param.lcid = lcid;
param.flags = flags;
param.pDispParams = pDispParams;
param.pVarResult = pVarResult;
param.pExcepInfo = pExcepInfo;
param.piArgErr = piArgErr;
PAL_TRY(Param *, pParam, &param)
{
pParam->hr = pParam->pDisp->Invoke(pParam->MemberID,
pParam->riid,
pParam->lcid,
pParam->flags,
pParam->pDispParams,
pParam->pVarResult,
pParam->pExcepInfo,
pParam->piArgErr);
}
PAL_EXCEPT_FILTER(CallOutFilter)
{
_ASSERTE(!"CallOutFilter returned EXECUTE_HANDLER.");
}
PAL_ENDTRY;
return param.hr;
}
void DispInvokeConvertObjectToVariant(OBJECTREF *pSrcObj, VARIANT *pDestVar, ByrefArgumentInfo *pByrefArgInfo)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pSrcObj));
PRECONDITION(IsProtectedByGCFrame (pSrcObj));
PRECONDITION(CheckPointer(pDestVar));
2015-01-30 14:14:42 -08:00
PRECONDITION(CheckPointer(pByrefArgInfo));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
if (pByrefArgInfo->m_bByref)
{
if (*pSrcObj == NULL)
{
V_VT(pDestVar) = VT_VARIANT | VT_BYREF;
pDestVar->pvarVal = &pByrefArgInfo->m_Val;
}
else if (CoreLibBinder::IsClass((*pSrcObj)->GetMethodTable(), CLASS__VARIANT_WRAPPER))
2015-01-30 14:14:42 -08:00
{
OBJECTREF WrappedObj = (*((VARIANTWRAPPEROBJECTREF*)pSrcObj))->GetWrappedObject();
GCPROTECT_BEGIN(WrappedObj)
{
OleVariant::MarshalOleVariantForObject(&WrappedObj, &pByrefArgInfo->m_Val);
V_VT(pDestVar) = VT_VARIANT | VT_BYREF;
pDestVar->pvarVal = &pByrefArgInfo->m_Val;
}
GCPROTECT_END();
}
else
{
OleVariant::MarshalOleVariantForObject(pSrcObj, &pByrefArgInfo->m_Val);
OleVariant::CreateByrefVariantForVariant(&pByrefArgInfo->m_Val, pDestVar);
}
}
else
{
OleVariant::MarshalOleVariantForObject(pSrcObj, pDestVar);
}
}
static void DoIUInvokeDispMethod(IDispatchEx* pDispEx, IDispatch* pDisp, DISPID MemberID, LCID lcid,
2015-01-30 14:14:42 -08:00
WORD flags, DISPPARAMS* pDispParams, VARIANT* pVarResult)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
UINT iArgErr;
EXCEPINFO ExcepInfo;
HRESULT hr;
memset(&ExcepInfo, 0, sizeof(EXCEPINFO));
2015-01-30 14:14:42 -08:00
GCX_COOP();
OBJECTREF pThrowable = NULL;
GCPROTECT_BEGIN(pThrowable);
{
// Call the method
EX_TRY
2015-01-30 14:14:42 -08:00
{
{
// We are about to make call's to COM so switch to preemptive GC.
GCX_PREEMP();
if (pDispEx)
{
hr = InvokeExHelper(pDispEx, MemberID, lcid, flags, pDispParams,
pVarResult, &ExcepInfo, NULL);
}
else
{
hr = InvokeHelper( pDisp, MemberID, IID_NULL, lcid, flags,
pDispParams, pVarResult, &ExcepInfo, &iArgErr);
}
}
// If the invoke call failed then throw an exception based on the EXCEPINFO.
if (FAILED(hr))
{
if (hr == DISP_E_EXCEPTION)
{
// This method will free the BSTR's in the EXCEPINFO.
COMPlusThrowHR(&ExcepInfo);
}
else
{
COMPlusThrowHR(hr);
}
}
}
EX_CATCH
2015-01-30 14:14:42 -08:00
{
// If we get here we need to throw an TargetInvocationException
pThrowable = GET_THROWABLE();
_ASSERTE(pThrowable != NULL);
}
EX_END_CATCH(RethrowTerminalExceptions);
if (pThrowable != NULL)
{
COMPlusThrow(InvokeUtil::CreateTargetExcept(&pThrowable));
}
}
GCPROTECT_END();
}
FORCEINLINE void DispParamHolderRelease(VARIANT* value)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
if (value)
{
if (V_VT(value) & VT_BYREF)
{
VariantHolder TmpVar;
OleVariant::ExtractContentsFromByrefVariant(value, &TmpVar);
}
2015-01-30 14:14:42 -08:00
SafeVariantClear(value);
}
}
class DispParamHolder : public Wrapper<VARIANT*, DispParamHolderDoNothing, DispParamHolderRelease, NULL>
{
public:
DispParamHolder(VARIANT* p = NULL)
: Wrapper<VARIANT*, DispParamHolderDoNothing, DispParamHolderRelease, NULL>(p)
{
WRAPPER_NO_CONTRACT;
}
FORCEINLINE void operator=(VARIANT* p)
{
WRAPPER_NO_CONTRACT;
Wrapper<VARIANT*, DispParamHolderDoNothing, DispParamHolderRelease, NULL>::operator=(p);
}
};
//--------------------------------------------------------------------------------
// InvokeDispMethod will convert a set of managed objects and call IDispatch. The
// result will be returned as a CLR Variant pointed to by pRetVal.
void IUInvokeDispMethod(
REFLECTCLASSBASEREF* pRefClassObj,
OBJECTREF* pTarget,
OBJECTREF* pName,
DISPID *pMemberID,
OBJECTREF* pArgs,
OBJECTREF* pByrefModifiers,
OBJECTREF* pNamedArgs,
OBJECTREF* pRetVal,
LCID lcid,
WORD flags,
BOOL bIgnoreReturn,
BOOL bIgnoreCase)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pTarget));
2015-01-30 14:14:42 -08:00
}
CONTRACTL_END;
HRESULT hr;
UINT i;
UINT iSrcArg;
UINT iDestArg;
VARIANT VarResult;
UINT cArgs = 0;
UINT cNamedArgs = 0;
DISPPARAMS DispParams = {0};
DISPID* aDispID = NULL;
DISPID MemberID = 0;
ByrefArgumentInfo* aByrefArgInfos = NULL;
BOOL bSomeArgsAreByref = FALSE;
SafeComHolder<IDispatch> pDisp = NULL;
SafeComHolder<IDispatchEx> pDispEx = NULL;
VariantPtrHolder pVarResult = NULL;
NewArrayHolder<DispParamHolder> params = NULL;
//
// Function initialization.
//
SafeVariantInit(&VarResult);
// InteropUtil.h does not know about anything other than OBJECTREF so
// convert the OBJECTREF's to their real type.
STRINGREF* pStrName = (STRINGREF*) pName;
PTRARRAYREF* pArrArgs = (PTRARRAYREF*) pArgs;
PTRARRAYREF* pArrByrefModifiers = (PTRARRAYREF*) pByrefModifiers;
PTRARRAYREF* pArrNamedArgs = (PTRARRAYREF*) pNamedArgs;
MethodTable* pInvokedMT = (*pRefClassObj)->GetType().GetMethodTable();
PREFIX_ASSUME(pInvokedMT != NULL);
// Retrieve the total count of arguments.
if (*pArrArgs != NULL)
cArgs = (*pArrArgs)->GetNumComponents();
// Retrieve the count of named arguments.
if (*pArrNamedArgs != NULL)
cNamedArgs = (*pArrNamedArgs)->GetNumComponents();
// Validate that the target is valid for the specified type.
if (!IsComTargetValidForType(pRefClassObj, pTarget))
COMPlusThrow(kTargetException, W("RFLCT_Targ_ITargMismatch"));
2015-01-30 14:14:42 -08:00
// If the invoked type is an interface, make sure it is IDispatch based.
if (pInvokedMT->IsInterface())
{
CorIfaceAttr ifaceType = pInvokedMT->GetComInterfaceType();
if (!IsDispatchBasedItf(ifaceType))
COMPlusThrow(kTargetInvocationException, IDS_EE_INTERFACE_NOT_DISPATCH_BASED);
}
// Validate that the target is a COM object.
_ASSERTE((*pTarget)->GetMethodTable()->IsComObjectType());
//
// Initialize the DISPPARAMS structure.
//
if (cArgs > 0)
{
UINT cPositionalArgs = cArgs - cNamedArgs;
2015-01-30 14:14:42 -08:00
DispParams.cArgs = cArgs;
DispParams.rgvarg = (VARIANTARG *)_alloca(cArgs * sizeof(VARIANTARG));
params = new DispParamHolder[cArgs];
// Initialize all the variants.
GCX_PREEMP();
for (i = 0; i < cArgs; i++)
{
SafeVariantInit(&DispParams.rgvarg[i]);
params[i] = &DispParams.rgvarg[i];
}
}
//
// Retrieve the IDispatch interface that will be invoked on.
//
if (pInvokedMT->IsInterface())
{
// The invoked type is a dispatch or dual interface so we will make the
// invocation on it.
pDisp = (IDispatch *)ComObject::GetComIPFromRCWThrowing(pTarget, pInvokedMT);
}
else
{
// A class was passed in so we will make the invocation on the default
// IDispatch for the COM component.
RCWHolder pRCW(GetThread());
RCWPROTECT_BEGIN(pRCW, *pTarget);
2022-08-02 00:56:33 +03:00
// Retrieve the IDispatch pointer from the wrapper.
2015-01-30 14:14:42 -08:00
pDisp = (IDispatch*)pRCW->GetIDispatch();
if (!pDisp)
COMPlusThrow(kTargetInvocationException, IDS_EE_NO_IDISPATCH_ON_TARGET);
// If we aren't ignoring case, then we need to try and QI for IDispatchEx to
2015-01-30 14:14:42 -08:00
// be able to use IDispatchEx::GetDispID() which has a flag to control case
// sentisitivity.
if (!bIgnoreCase && cNamedArgs == 0)
{
RCW_VTABLEPTR(pRCW);
hr = SafeQueryInterface(pDisp, IID_IDispatchEx, (IUnknown**)&pDispEx);
if (FAILED(hr))
pDispEx = NULL;
}
RCWPROTECT_END(pRCW);
}
_ASSERTE((IUnknown*)pDisp != NULL);
//
// Prepare the DISPID's that will be passed to invoke.
//
if (pMemberID && (*pMemberID != DISPID_UNKNOWN) && (cNamedArgs == 0))
{
// The caller specified a member ID and we don't have any named arguments so
// we can simply use the member ID the caller specified.
MemberID = *pMemberID;
}
else
{
int strNameLength = (*pStrName)->GetStringLength();
// Check if we are invoking on the default member.
if (strNameLength == 0)
{
// Set the DISPID to 0 (default member).
MemberID = 0;
_ASSERTE(cNamedArgs == 0);
if (cNamedArgs != 0)
COMPlusThrow(kNotSupportedException,W("NotSupported_IDispInvokeDefaultMemberWithNamedArgs"));
}
else
{
//
// Create an array of strings that will be passed to GetIDsOfNames().
//
UINT cNamesToConvert = cNamedArgs + 1;
LPWSTR strTmpName = NULL;
2015-01-30 14:14:42 -08:00
// Allocate the array of strings to convert, the array of pinned handles and the
// array of converted DISPID's.
size_t allocSize = cNamesToConvert * sizeof(LPWSTR);
if (allocSize < cNamesToConvert)
COMPlusThrowArgumentOutOfRange(W("namedParameters"), W("ArgumentOutOfRange_Capacity"));
LPWSTR *aNamesToConvert = (LPWSTR *)_alloca(allocSize);
2015-01-30 14:14:42 -08:00
allocSize = cNamesToConvert * sizeof(DISPID);
if (allocSize < cNamesToConvert)
COMPlusThrowArgumentOutOfRange(W("namedParameters"), W("ArgumentOutOfRange_Capacity"));
aDispID = (DISPID *)_alloca(allocSize);
// The first name to convert is the name of the method itself.
aNamesToConvert[0] = (*pStrName)->GetBuffer();
// Check to see if the name is for a standard DISPID.
if (SString::_wcsnicmp(aNamesToConvert[0], STANDARD_DISPID_PREFIX, STANDARD_DISPID_PREFIX_LENGTH) == 0)
{
// The name is for a standard DISPID so extract it from the name.
MemberID = ExtractStandardDispId(aNamesToConvert[0]);
// Make sure there are no named arguments to convert.
if (cNamedArgs > 0)
{
STRINGREF *pNamedArgsData = (STRINGREF *)(*pArrNamedArgs)->GetDataPtr();
for (i = 0; i < cNamedArgs; i++)
{
// The first name to convert is the name of the method itself.
strTmpName = pNamedArgsData[i]->GetBuffer();
// Check to see if the name is for a standard DISPID.
if (SString::_wcsnicmp(strTmpName, STANDARD_DISPID_PREFIX, STANDARD_DISPID_PREFIX_LENGTH) != 0)
COMPlusThrow(kArgumentException, IDS_EE_NON_STD_NAME_WITH_STD_DISPID);
// The name is for a standard DISPID so extract it from the name.
aDispID[i + 1] = ExtractStandardDispId(strTmpName);
}
}
}
else
{
BOOL fGotIt = FALSE;
BOOL fIsNonGenericComObject = pInvokedMT->IsInterface() || (pInvokedMT != g_pBaseCOMObject && pInvokedMT->IsComObjectType());
BOOL fUseCache = fIsNonGenericComObject && !(IUnknown*)pDispEx && strNameLength <= ReflectionMaxCachedNameLength && cNamedArgs == 0;
DispIDCacheElement vDispIDElement;
// If the object is not a generic COM object and the member meets the criteria to be
// in the cache then look up the DISPID in the cache.
if (fUseCache)
{
vDispIDElement.pMT = pInvokedMT;
vDispIDElement.strNameLength = strNameLength;
vDispIDElement.lcid = lcid;
2022-01-12 15:05:32 +02:00
wcscpy_s(vDispIDElement.strName, ARRAY_SIZE(vDispIDElement.strName), aNamesToConvert[0]);
2015-01-30 14:14:42 -08:00
// Only look up if the cache has already been created.
DispIDCache* pDispIDCache = GetAppDomain()->GetRefDispIDCache();
fGotIt = pDispIDCache->GetFromCache (&vDispIDElement, MemberID);
}
if (!fGotIt)
{
NewArrayHolder<PinningHandleHolder> ahndPinnedObjs = new PinningHandleHolder[cNamesToConvert];
ahndPinnedObjs[0] = GetAppDomain()->CreatePinningHandle((OBJECTREF)*pStrName);
// Copy the named arguments into the array of names to convert.
if (cNamedArgs > 0)
{
STRINGREF *pNamedArgsData = (STRINGREF *)(*pArrNamedArgs)->GetDataPtr();
for (i = 0; i < cNamedArgs; i++)
{
// Pin the string object and retrieve a pointer to its data.
ahndPinnedObjs[i + 1] = GetAppDomain()->CreatePinningHandle((OBJECTREF)pNamedArgsData[i]);
aNamesToConvert[i + 1] = pNamedArgsData[i]->GetBuffer();
}
}
//
// Call GetIDsOfNames to convert the names to DISPID's
//
{
// We are about to make call's to COM so switch to preemptive GC.
GCX_PREEMP();
if ((IUnknown*)pDispEx)
{
// We should only get here if we are doing a case sensitive lookup and
// we don't have any named arguments.
_ASSERTE(cNamedArgs == 0);
_ASSERTE(!bIgnoreCase);
// We managed to retrieve an IDispatchEx IP so we will use it to
// retrieve the DISPID.
BSTRHolder bstrTmpName = SysAllocString(aNamesToConvert[0]);
if (!bstrTmpName)
COMPlusThrowOM();
hr = pDispEx->GetDispID(bstrTmpName, fdexNameCaseSensitive, aDispID);
}
else
{
// Call GetIdsOfNames() to retrieve the DISPID's of the method and of the arguments.
hr = pDisp->GetIDsOfNames(
IID_NULL,
aNamesToConvert,
cNamesToConvert,
lcid,
aDispID
);
}
}
if (FAILED(hr))
{
// Check to see if the user wants to invoke the new enum member.
if (cNamesToConvert == 1 && SString::_wcsicmp(aNamesToConvert[0], GET_ENUMERATOR_METHOD_NAME) == 0)
{
// Invoke the new enum member.
MemberID = DISPID_NEWENUM;
}
else
{
// The name is unknown.
COMPlusThrowHR(hr);
}
}
else
{
// The member ID is the first elements of the array we got back from GetIdsOfNames.
MemberID = aDispID[0];
}
// If the object is not a generic COM object and the member meets the criteria to be
// in the cache then insert the member in the cache.
if (fUseCache)
{
DispIDCache *pDispIDCache = GetAppDomain()->GetRefDispIDCache();
pDispIDCache->AddToCache (&vDispIDElement, MemberID);
}
}
}
}
// Store the member ID if the caller passed in a place to store it.
if (pMemberID)
*pMemberID = MemberID;
}
//
// Fill in the DISPPARAMS structure.
//
if (cArgs > 0)
{
// Allocate the byref argument information.
aByrefArgInfos = (ByrefArgumentInfo*)_alloca(cArgs * sizeof(ByrefArgumentInfo));
memset(aByrefArgInfos, 0, cArgs * sizeof(ByrefArgumentInfo));
// Set the byref bit on the arguments that have the byref modifier.
if (*pArrByrefModifiers != NULL)
{
BYTE *aByrefModifiers = (BYTE*)(*pArrByrefModifiers)->GetDataPtr();
for (i = 0; i < cArgs; i++)
{
if (aByrefModifiers[i])
{
aByrefArgInfos[i].m_bByref = TRUE;
bSomeArgsAreByref = TRUE;
}
}
}
// We need to protect the temporary object that will be used to convert from
// the managed objects to OLE variants.
OBJECTREF TmpObj = NULL;
GCPROTECT_BEGIN(TmpObj)
{
if (!(flags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)))
{
// For anything other than a put or a putref we just use the specified
// named arguments.
DispParams.cNamedArgs = cNamedArgs;
DispParams.rgdispidNamedArgs = (cNamedArgs == 0) ? NULL : &aDispID[1];
// Convert the named arguments from COM+ to OLE. These arguments are in the same order
// on both sides.
for (i = 0; i < cNamedArgs; i++)
{
iSrcArg = i;
iDestArg = i;
TmpObj = ((OBJECTREF*)(*pArrArgs)->GetDataPtr())[iSrcArg];
DispInvokeConvertObjectToVariant(&TmpObj, &DispParams.rgvarg[iDestArg], &aByrefArgInfos[iSrcArg]);
2015-01-30 14:14:42 -08:00
}
// Convert the unnamed arguments. These need to be presented in reverse order to IDispatch::Invoke().
for (iSrcArg = cNamedArgs, iDestArg = cArgs - 1; iSrcArg < cArgs; iSrcArg++, iDestArg--)
{
TmpObj = ((OBJECTREF*)(*pArrArgs)->GetDataPtr())[iSrcArg];
DispInvokeConvertObjectToVariant(&TmpObj, &DispParams.rgvarg[iDestArg], &aByrefArgInfos[iSrcArg]);
2015-01-30 14:14:42 -08:00
}
}
else
{
// If we are doing a property put then we need to set the DISPID of the
// argument to DISP_PROPERTYPUT if there is at least one argument.
DispParams.cNamedArgs = cNamedArgs + 1;
DispParams.rgdispidNamedArgs = (DISPID*)_alloca((cNamedArgs + 1) * sizeof(DISPID));
2015-01-30 14:14:42 -08:00
// Fill in the array of named arguments.
DispParams.rgdispidNamedArgs[0] = DISPID_PROPERTYPUT;
for (i = 1; i < cNamedArgs; i++)
DispParams.rgdispidNamedArgs[i] = aDispID[i];
// The last argument from reflection becomes the first argument that must be passed to IDispatch.
iSrcArg = cArgs - 1;
iDestArg = 0;
TmpObj = ((OBJECTREF*)(*pArrArgs)->GetDataPtr())[iSrcArg];
DispInvokeConvertObjectToVariant(&TmpObj, &DispParams.rgvarg[iDestArg], &aByrefArgInfos[iSrcArg]);
2015-01-30 14:14:42 -08:00
// Convert the named arguments from COM+ to OLE. These arguments are in the same order
// on both sides.
for (i = 0; i < cNamedArgs; i++)
{
iSrcArg = i;
iDestArg = i + 1;
TmpObj = ((OBJECTREF*)(*pArrArgs)->GetDataPtr())[iSrcArg];
DispInvokeConvertObjectToVariant(&TmpObj, &DispParams.rgvarg[iDestArg], &aByrefArgInfos[iSrcArg]);
2015-01-30 14:14:42 -08:00
}
// Convert the unnamed arguments. These need to be presented in reverse order to IDispatch::Invoke().
for (iSrcArg = cNamedArgs, iDestArg = cArgs - 1; iSrcArg < cArgs - 1; iSrcArg++, iDestArg--)
{
TmpObj = ((OBJECTREF*)(*pArrArgs)->GetDataPtr())[iSrcArg];
DispInvokeConvertObjectToVariant(&TmpObj, &DispParams.rgvarg[iDestArg], &aByrefArgInfos[iSrcArg]);
2015-01-30 14:14:42 -08:00
}
}
}
GCPROTECT_END();
}
else
{
// There are no arguments.
DispParams.cArgs = cArgs;
DispParams.cNamedArgs = 0;
DispParams.rgdispidNamedArgs = NULL;
DispParams.rgvarg = NULL;
}
// If we're calling on DISPID=-4, then pass both METHOD and PROPERTYGET
if (MemberID == DISPID_NEWENUM)
{
_ASSERTE((flags & DISPATCH_METHOD) && "Expected DISPATCH_METHOD to be set.");
flags |= DISPATCH_METHOD | DISPATCH_PROPERTYGET;
}
//
// Call invoke on the target's IDispatch.
//
if (!bIgnoreReturn)
pVarResult = &VarResult;
DoIUInvokeDispMethod(pDispEx, pDisp, MemberID, lcid, flags, &DispParams, pVarResult);
//
// Return value handling and cleanup.
//
// Back propagate any byref args.
if (bSomeArgsAreByref)
{
OBJECTREF TmpObj = NULL;
GCPROTECT_BEGIN(TmpObj)
{
for (i = 0; i < cArgs; i++)
{
if (aByrefArgInfos[i].m_bByref)
{
// Convert the variant back to an object.
OleVariant::MarshalObjectForOleVariant(&aByrefArgInfos[i].m_Val, &TmpObj);
(*pArrArgs)->SetAt(i, TmpObj);
}
2015-01-30 14:14:42 -08:00
}
}
GCPROTECT_END();
}
if (!bIgnoreReturn)
{
if (MemberID == DISPID_NEWENUM)
{
//
// Use a custom marshaler to convert the IEnumVARIANT to an IEnumerator.
//
// Start by making sure that the variant we got back contains an IP.
if ((VarResult.vt != VT_UNKNOWN) || !VarResult.punkVal)
COMPlusThrow(kInvalidCastException, IDS_EE_INVOKE_NEW_ENUM_INVALID_RETURN);
// Have the custom marshaler do the conversion.
*pRetVal = ConvertEnumVariantToMngEnum((IEnumVARIANT *)VarResult.punkVal);
}
else
{
// Convert the return variant to a COR variant.
OleVariant::MarshalObjectForOleVariant(&VarResult, pRetVal);
}
}
}
#if defined(FEATURE_COMINTEROP_UNMANAGED_ACTIVATION) && defined(FEATURE_COMINTEROP)
static void GetComClassHelper(
_Out_ OBJECTREF *pRef,
_In_ EEClassFactoryInfoHashTable *pClassFactHash,
_In_ ClassFactoryInfo *pClassFactInfo)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
INJECT_FAULT(ThrowOutOfMemory());
PRECONDITION(CheckPointer(pRef));
PRECONDITION(CheckPointer(pClassFactHash));
PRECONDITION(CheckPointer(pClassFactInfo));
}
CONTRACTL_END;
OBJECTHANDLE hRef;
AppDomain *pDomain = GetAppDomain();
CrstHolder ch(pDomain->GetRefClassFactCrst());
// Check again.
if (pClassFactHash->GetValue(pClassFactInfo, (HashDatum *)&hRef))
{
*pRef = ObjectFromHandle(hRef);
}
else
{
//
// There is no managed class for this CLSID
// so we will create a ComClassFactory to
// represent it.
//
NewHolder<ComClassFactory> pComClsFac = new ComClassFactory(pClassFactInfo->m_clsid);
pComClsFac->SetManagedVersion();
NewArrayHolder<WCHAR> wszRefServer = NULL;
if (pClassFactInfo->m_strServerName)
{
size_t len = u16_strlen(pClassFactInfo->m_strServerName)+1;
wszRefServer = new WCHAR[len];
wcscpy_s(wszRefServer, len, pClassFactInfo->m_strServerName);
}
pComClsFac->Init(wszRefServer, NULL);
AllocateComClassObject(pComClsFac, pRef);
// Insert to hash.
hRef = pDomain->CreateHandle(*pRef);
pClassFactHash->InsertValue(pClassFactInfo, (LPVOID)hRef);
// Make sure the hash code is working.
_ASSERTE (pClassFactHash->GetValue(pClassFactInfo, (HashDatum *)&hRef));
wszRefServer.SuppressRelease();
pComClsFac.SuppressRelease();
}
}
2015-01-30 14:14:42 -08:00
//-------------------------------------------------------------
// returns a ComClass reflect class that wraps the IClassFactory
void GetComClassFromCLSID(REFCLSID clsid, _In_opt_z_ PCWSTR wszServer, OBJECTREF *pRef)
2015-01-30 14:14:42 -08:00
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(pRef != NULL);
}
CONTRACTL_END;
//
// See if we can find the well known managed class for this CLSID.
2015-01-30 14:14:42 -08:00
//
// Check if we have in the hash.
OBJECTHANDLE hRef;
ClassFactoryInfo ClassFactInfo;
ClassFactInfo.m_clsid = clsid;
ClassFactInfo.m_strServerName = wszServer;
EEClassFactoryInfoHashTable *pClassFactHash = GetAppDomain()->GetClassFactHash();
if (pClassFactHash->GetValue(&ClassFactInfo, (HashDatum*) &hRef))
{
*pRef = ObjectFromHandle(hRef);
2015-01-30 14:14:42 -08:00
}
else
{
GetComClassHelper(pRef, pClassFactHash, &ClassFactInfo);
2015-01-30 14:14:42 -08:00
}
// If we made it this far *pRef better be set.
_ASSERTE(*pRef != NULL);
}
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION && FEATURE_COMINTEROP
2015-01-30 14:14:42 -08:00
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
//-------------------------------------------------------------
// check if a ComClassFactory has been setup for this class
// if not set one up
ClassFactoryBase *GetComClassFactory(MethodTable* pClassMT)
{
CONTRACT (ClassFactoryBase*)
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(ThrowOutOfMemory());
PRECONDITION(CheckPointer(pClassMT));
PRECONDITION(pClassMT->IsComObjectType());
2015-01-30 14:14:42 -08:00
POSTCONDITION(CheckPointer(RETVAL));
}
CONTRACT_END;
2022-08-02 00:56:33 +03:00
// Work our way up the hierarchy until we find the first COM import type.
while (!pClassMT->IsComImport())
2015-01-30 14:14:42 -08:00
{
pClassMT = pClassMT->GetParentMethodTable();
_ASSERTE(pClassMT != NULL);
_ASSERTE(pClassMT->IsComObjectType());
2015-01-30 14:14:42 -08:00
}
// check if com data has been setup for this class
ClassFactoryBase *pClsFac = pClassMT->GetComClassFactory();
if (pClsFac == NULL)
{
NewHolder<ClassFactoryBase> pNewFactory;
GUID guid;
pClassMT->GetGuid(&guid, TRUE);
2015-01-30 14:14:42 -08:00
ComClassFactory *pComClsFac = new ComClassFactory(guid);
2015-01-30 14:14:42 -08:00
pNewFactory = pComClsFac;
2015-01-30 14:14:42 -08:00
pComClsFac->Init(NULL, pClassMT);
2015-01-30 14:14:42 -08:00
// store the class factory in EE Class
if (!pClassMT->SetComClassFactory(pNewFactory))
{
// another thread beat us to it
pNewFactory = pClassMT->GetComClassFactory();
}
pClsFac = pNewFactory.Extract();
}
RETURN pClsFac;
}
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
//-------------------------------------------------------------------
// void InitializeComInterop()
// Called from EEStartup, to initialize com Interop specific data
2015-01-30 14:14:42 -08:00
// structures.
//-------------------------------------------------------------------
void InitializeComInterop()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
InitializeSListHead(&RCW::s_RCWStandbyList);
ComCall::Init();
#ifdef TARGET_X86
2015-01-30 14:14:42 -08:00
ComPlusCall::Init();
#endif
CtxEntryCache::Init();
ComCallWrapperTemplate::Init();
#ifdef _DEBUG
2022-07-17 08:11:11 +03:00
InitializeInteropLogging();
2015-01-30 14:14:42 -08:00
#endif //_DEBUG
}
#ifdef _DEBUG
//-------------------------------------------------------------------
// LOGGING APIS
//-------------------------------------------------------------------
static int g_TraceCount = 0;
2021-03-29 13:58:50 -07:00
static IUnknown* g_pTraceIUnknown = NULL;
2015-01-30 14:14:42 -08:00
2022-07-17 08:11:11 +03:00
VOID InitializeInteropLogging()
2015-01-30 14:14:42 -08:00
{
WRAPPER_NO_CONTRACT;
2015-01-30 14:14:42 -08:00
g_TraceCount = g_pConfig->GetTraceWrapper();
}
2022-01-06 08:49:08 +02:00
VOID LogInterop(_In_z_ LPCSTR szMsg)
2015-01-30 14:14:42 -08:00
{
LIMITED_METHOD_CONTRACT;
LOG( (LF_INTEROP, LL_INFO10, "%s\n",szMsg) );
}
//-------------------------------------------------------------------
// VOID LogRCWCreate(RCW* pWrap, IUnknown* pUnk)
// log wrapper create
//-------------------------------------------------------------------
VOID LogRCWCreate(RCW* pWrap, IUnknown* pUnk)
{
if (!LoggingOn(LF_INTEROP, LL_ALWAYS))
2015-01-30 14:14:42 -08:00
return;
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
static int count = 0;
LPVOID pCurrCtx = GetCurrentCtxCookie();
// pre-increment the count, so it can never be zero
count++;
if (count == g_TraceCount)
{
g_pTraceIUnknown = pUnk;
}
if (g_pTraceIUnknown == 0 || g_pTraceIUnknown == pUnk)
{
LOG( (LF_INTEROP,
LL_INFO10,
"Create RCW: Wrapper %p #%d IUnknown:%p Context %p\n",
pWrap, count,
pUnk,
pCurrCtx) );
}
}
//-------------------------------------------------------------------
// VOID LogRCWMinorCleanup(RCW* pWrap)
// log wrapper minor cleanup
//-------------------------------------------------------------------
VOID LogRCWMinorCleanup(RCW* pWrap)
{
if (!LoggingOn(LF_INTEROP, LL_ALWAYS))
2015-01-30 14:14:42 -08:00
return;
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pWrap));
}
CONTRACTL_END;
static int dest_count = 0;
dest_count++;
IUnknown *pUnk = pWrap->GetRawIUnknown_NoAddRef_NoThrow();
if (g_pTraceIUnknown == 0 || g_pTraceIUnknown == pUnk)
{
LPVOID pCurrCtx = GetCurrentCtxCookie();
LOG( (LF_INTEROP,
LL_INFO10,
"Minor Cleanup RCW: Wrapper %p #%d IUnknown %p Context: %p\n",
pWrap, dest_count,
pUnk,
pCurrCtx) );
}
}
//-------------------------------------------------------------------
// VOID LogRCWDestroy(RCW* pWrap, IUnknown* pUnk)
// log wrapper destroy
//-------------------------------------------------------------------
VOID LogRCWDestroy(RCW* pWrap)
{
if (!LoggingOn(LF_INTEROP, LL_ALWAYS))
2015-01-30 14:14:42 -08:00
return;
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pWrap));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
static int dest_count = 0;
dest_count++;
IUnknown *pUnk = pWrap->GetRawIUnknown_NoAddRef_NoThrow();
if (g_pTraceIUnknown == 0 || g_pTraceIUnknown == pUnk)
{
LPVOID pCurrCtx = GetCurrentCtxCookie();
STRESS_LOG4(
LF_INTEROP,
LL_INFO10,
"Destroy RCW: Wrapper %p #%d IUnknown %p Context: %p\n",
pWrap, dest_count,
pUnk,
pCurrCtx);
}
}
//-------------------------------------------------------------------
// VOID LogInteropLeak(IUnkEntry * pEntry)
//-------------------------------------------------------------------
VOID LogInteropLeak(IUnkEntry * pEntry)
{
if (!LoggingOn(LF_INTEROP, LL_ALWAYS))
2015-01-30 14:14:42 -08:00
return;
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pEntry));
}
CONTRACTL_END;
IUnknown *pUnk = pEntry->GetRawIUnknown_NoAddRef_NoThrow();
if (g_pTraceIUnknown == 0 || g_pTraceIUnknown == pUnk)
{
LOG( (LF_INTEROP,
LL_INFO10,
"IUnkEntry Leak: %p Context: %p\n",
pUnk,
pEntry->GetCtxCookie()) );
}
}
//-------------------------------------------------------------------
// VOID LogInteropLeak(IUnknown* pItf)
//-------------------------------------------------------------------
VOID LogInteropLeak(IUnknown* pItf)
{
if (!LoggingOn(LF_INTEROP, LL_ALWAYS))
2015-01-30 14:14:42 -08:00
return;
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
LPVOID pCurrCtx = NULL;
if (g_pTraceIUnknown == 0 || g_pTraceIUnknown == pItf)
{
pCurrCtx = GetCurrentCtxCookie();
LOG((LF_INTEROP,
LL_EVERYTHING,
"Leak: Itf = %p, CurrCtx = %p\n",
pItf, pCurrCtx));
}
}
//-------------------------------------------------------------------
// VOID LogInteropQI(IUnknown* pItf, REFIID iid, HRESULT hr, LPCSTR szMsg)
//-------------------------------------------------------------------
2022-01-06 08:49:08 +02:00
VOID LogInteropQI(IUnknown* pItf, REFIID iid, HRESULT hrArg, _In_z_ LPCSTR szMsg)
2015-01-30 14:14:42 -08:00
{
if (!LoggingOn(LF_INTEROP, LL_ALWAYS))
2015-01-30 14:14:42 -08:00
return;
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pItf));
}
CONTRACTL_END;
LPVOID pCurrCtx = NULL;
HRESULT hr = S_OK;
SafeComHolder<IUnknown> pUnk = NULL;
int cch = 0;
CHAR szIID[GUID_STR_BUFFER_LEN];
2015-01-30 14:14:42 -08:00
hr = SafeQueryInterface(pItf, IID_IUnknown, &pUnk);
if (g_pTraceIUnknown == 0 || g_pTraceIUnknown == pUnk)
{
pCurrCtx = GetCurrentCtxCookie();
cch = GuidToLPSTR(iid, szIID);
2015-01-30 14:14:42 -08:00
_ASSERTE(cch > 0);
if (SUCCEEDED(hrArg))
{
LOG((LF_INTEROP,
LL_EVERYTHING,
"Succeeded QI: Unk = %p, Itf = %p, CurrCtx = %p, IID = %s, Msg: %s\n",
(IUnknown*)pUnk, pItf, pCurrCtx, szIID, szMsg));
2015-01-30 14:14:42 -08:00
}
else
{
LOG((LF_INTEROP,
LL_EVERYTHING,
"Failed QI: Unk = %p, Itf = %p, CurrCtx = %p, IID = %s, HR = %p, Msg: %s\n",
(IUnknown*)pUnk, pItf, pCurrCtx, szIID, hrArg, szMsg));
2015-01-30 14:14:42 -08:00
}
}
}
//-------------------------------------------------------------------
// VOID LogInteropAddRef(IUnknown* pItf, ULONG cbRef, LPCSTR szMsg)
//-------------------------------------------------------------------
2022-01-06 08:49:08 +02:00
VOID LogInteropAddRef(IUnknown* pItf, ULONG cbRef, _In_z_ LPCSTR szMsg)
2015-01-30 14:14:42 -08:00
{
if (!LoggingOn(LF_INTEROP, LL_ALWAYS))
2015-01-30 14:14:42 -08:00
return;
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pItf));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
LPVOID pCurrCtx = NULL;
HRESULT hr = S_OK;
SafeComHolder<IUnknown> pUnk = NULL;
hr = SafeQueryInterface(pItf, IID_IUnknown, &pUnk);
if (g_pTraceIUnknown == 0 || g_pTraceIUnknown == pUnk)
{
pCurrCtx = GetCurrentCtxCookie();
LOG((LF_INTEROP,
LL_EVERYTHING,
"AddRef: Unk = %p, Itf = %p, CurrCtx = %p, RefCount = %d, Msg: %s\n",
(IUnknown*)pUnk, pItf, pCurrCtx, cbRef, szMsg));
}
}
//-------------------------------------------------------------------
// VOID LogInteropRelease(IUnknown* pItf, ULONG cbRef, LPCSTR szMsg)
//-------------------------------------------------------------------
2022-01-06 08:49:08 +02:00
VOID LogInteropRelease(IUnknown* pItf, ULONG cbRef, _In_z_ LPCSTR szMsg)
2015-01-30 14:14:42 -08:00
{
if (!LoggingOn(LF_INTEROP, LL_ALWAYS))
2015-01-30 14:14:42 -08:00
return;
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pItf, NULL_OK));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
LPVOID pCurrCtx = NULL;
if (g_pTraceIUnknown == 0 || g_pTraceIUnknown == pItf)
{
pCurrCtx = GetCurrentCtxCookie();
LOG((LF_INTEROP,
LL_EVERYTHING,
"Release: Itf = %p, CurrCtx = %p, RefCount = %d, Msg: %s\n",
pItf, pCurrCtx, cbRef, szMsg));
}
}
#endif // _DEBUG
IUnknown* MarshalObjectToInterface(OBJECTREF* ppObject, MethodTable* pItfMT, MethodTable* pClassMT, DWORD dwFlags)
{
CONTRACTL
{
THROWS;
MODE_COOPERATIVE;
GC_TRIGGERS;
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
// When an interface method table is specified, fDispIntf must be consistent with the
// interface type.
BOOL bDispatch = (dwFlags & ItfMarshalInfo::ITF_MARSHAL_DISP_ITF);
BOOL bUseBasicItf = (dwFlags & ItfMarshalInfo::ITF_MARSHAL_USE_BASIC_ITF);
2015-01-30 14:14:42 -08:00
_ASSERTE(!pItfMT || (!pItfMT->IsInterface() && bDispatch) ||
(!!bDispatch == IsDispatchBasedItf(pItfMT->GetComInterfaceType())));
2015-01-30 14:14:42 -08:00
if (pItfMT)
{
return GetComIPFromObjectRef(ppObject, pItfMT);
}
else if (!bUseBasicItf)
{
return GetComIPFromObjectRef(ppObject, pClassMT);
}
else
{
ComIpType ReqIpType = bDispatch ? ComIpType_Dispatch : ComIpType_Unknown;
2015-01-30 14:14:42 -08:00
return GetComIPFromObjectRef(ppObject, ReqIpType, NULL);
}
}
void UnmarshalObjectFromInterface(OBJECTREF *ppObjectDest, IUnknown **ppUnkSrc, MethodTable *pItfMT, MethodTable *pClassMT, DWORD dwFlags)
{
CONTRACTL
{
THROWS;
MODE_COOPERATIVE;
GC_TRIGGERS;
PRECONDITION(IsProtectedByGCFrame(ppObjectDest));
}
CONTRACTL_END;
2015-01-30 14:14:42 -08:00
_ASSERTE(!pClassMT || !pClassMT->IsInterface());
DWORD dwObjFromComIPFlags = ObjFromComIP::FromItfMarshalInfoFlags(dwFlags);
GetObjectRefFromComIP(
ppObjectDest, // Object
ppUnkSrc, // Interface pointer
pClassMT, // Class type
dwObjFromComIPFlags // Flags
);
2015-01-30 14:14:42 -08:00
// Make sure the interface is supported.
_ASSERTE(!pItfMT || pItfMT->IsInterface() || pItfMT->GetComClassInterfaceType() != clsIfNone);
2021-08-12 10:47:58 -07:00
bool fIsInterface = (pItfMT != NULL && pItfMT->IsInterface());
2015-01-30 14:14:42 -08:00
if (fIsInterface)
{
// We only verify that the object supports the interface for non-WinRT scenarios because we
// believe that the likelihood of improperly constructed programs is significantly lower
// with WinRT and the Object::SupportsInterface check is very expensive.
if (!Object::SupportsInterface(*ppObjectDest, pItfMT))
2015-01-30 14:14:42 -08:00
{
COMPlusThrowInvalidCastException(ppObjectDest, TypeHandle(pItfMT));
2015-01-30 14:14:42 -08:00
}
}
}
#endif // FEATURE_COMINTEROP