diff options
author | Jan Vorlicek <janvorli@microsoft.com> | 2018-12-01 10:31:35 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-01 10:31:35 +0100 |
commit | 4c461d754ff29d1ba37c145676c397062378d1c0 (patch) | |
tree | 3f31b4015f010054dae1b5bd2f2a516915d0fe74 | |
parent | 9fca919fdc8f20f80d923088620ec026196785bf (diff) | |
download | coreclr-4c461d754ff29d1ba37c145676c397062378d1c0.tar.gz coreclr-4c461d754ff29d1ba37c145676c397062378d1c0.tar.bz2 coreclr-4c461d754ff29d1ba37c145676c397062378d1c0.zip |
Enable COM interop for collectible classes (#20919)
* Enable COM interop for collectible classes
* Modify DispatchInfo to use LoaderAllocator handles
The DispatchMemberInfo was using global handles to refer to the managed
MemberInfo instances. That doesn't work with unloadability.
This change modifies it to use handles allocated from LoaderAllocator.
* Disable COM interop for WinRT types
* Remove collectible check from IsTypeVisibleFromCom. That fixes three
new COM interop tests
* Add collectible check to GetComClassFactory when we check for
unsupported interop with WinRT
* Add COM unloadability tests
Add two tests to test COM unloadability:
* One for using native COM server from managed COM client
* One for using managed COM objects from native client
* Add unloading test for IUnknownTest
* Disable NETClientPrimitivesInALC on Win ARM
The NETClientPrimitives is disabled there too.
26 files changed, 623 insertions, 408 deletions
diff --git a/src/System.Private.CoreLib/Resources/Strings.resx b/src/System.Private.CoreLib/Resources/Strings.resx index 9fa2e10ce7..e5de28bf3a 100644 --- a/src/System.Private.CoreLib/Resources/Strings.resx +++ b/src/System.Private.CoreLib/Resources/Strings.resx @@ -2884,8 +2884,8 @@ <data name="NotSupported_CollectibleBoundNonCollectible" xml:space="preserve"> <value>A non-collectible assembly may not reference a collectible assembly.</value> </data> - <data name="NotSupported_CollectibleCOM" xml:space="preserve"> - <value>COM Interop is not supported for collectible types.</value> + <data name="NotSupported_CollectibleWinRT" xml:space="preserve"> + <value>WinRT Interop is not supported for collectible types.</value> </data> <data name="NotSupported_CreateInstanceWithTypeBuilder" xml:space="preserve"> <value>CreateInstance cannot be used with an object of type TypeBuilder.</value> diff --git a/src/inc/CrstTypes.def b/src/inc/CrstTypes.def index 180171ed3a..bb996a7af5 100644 --- a/src/inc/CrstTypes.def +++ b/src/inc/CrstTypes.def @@ -696,3 +696,6 @@ End Crst TieredCompilation AcquiredBefore ThreadpoolTimerQueue End + +Crst COMCallWrapper +End diff --git a/src/inc/crsttypes.h b/src/inc/crsttypes.h index 23358432c2..7c867f7fb8 100644 --- a/src/inc/crsttypes.h +++ b/src/inc/crsttypes.h @@ -35,138 +35,139 @@ enum CrstType CrstCLRPrivBinderMaps = 16, CrstCLRPrivBinderMapsAdd = 17, CrstCodeFragmentHeap = 18, - CrstCOMWrapperCache = 19, - CrstConnectionNameTable = 20, - CrstContexts = 21, - CrstCoreCLRBinderLog = 22, - CrstCrstCLRPrivBinderLocalWinMDPath = 23, - CrstCSPCache = 24, - CrstDataTest1 = 25, - CrstDataTest2 = 26, - CrstDbgTransport = 27, - CrstDeadlockDetection = 28, - CrstDebuggerController = 29, - CrstDebuggerFavorLock = 30, - CrstDebuggerHeapExecMemLock = 31, - CrstDebuggerHeapLock = 32, - CrstDebuggerJitInfo = 33, - CrstDebuggerMutex = 34, - CrstDelegateToFPtrHash = 35, - CrstDomainLocalBlock = 36, - CrstDynamicIL = 37, - CrstDynamicMT = 38, - CrstDynLinkZapItems = 39, - CrstEtwTypeLogHash = 40, - CrstEventPipe = 41, - CrstEventStore = 42, - CrstException = 43, - CrstExecuteManLock = 44, - CrstExecuteManRangeLock = 45, - CrstFCall = 46, - CrstFriendAccessCache = 47, - CrstFuncPtrStubs = 48, - CrstFusionAppCtx = 49, - CrstGCCover = 50, - CrstGCMemoryPressure = 51, - CrstGlobalStrLiteralMap = 52, - CrstHandleTable = 53, - CrstHostAssemblyMap = 54, - CrstHostAssemblyMapAdd = 55, - CrstIbcProfile = 56, - CrstIJWFixupData = 57, - CrstIJWHash = 58, - CrstILStubGen = 59, - CrstInlineTrackingMap = 60, - CrstInstMethodHashTable = 61, - CrstInterfaceVTableMap = 62, - CrstInterop = 63, - CrstInteropData = 64, - CrstIOThreadpoolWorker = 65, - CrstIsJMCMethod = 66, - CrstISymUnmanagedReader = 67, - CrstJit = 68, - CrstJitGenericHandleCache = 69, - CrstJitPerf = 70, - CrstJumpStubCache = 71, - CrstLeafLock = 72, - CrstListLock = 73, - CrstLoaderAllocator = 74, - CrstLoaderAllocatorReferences = 75, - CrstLoaderHeap = 76, - CrstMda = 77, - CrstMetadataTracker = 78, - CrstModIntPairList = 79, - CrstModule = 80, - CrstModuleFixup = 81, - CrstModuleLookupTable = 82, - CrstMulticoreJitHash = 83, - CrstMulticoreJitManager = 84, - CrstMUThunkHash = 85, - CrstNativeBinderInit = 86, - CrstNativeImageCache = 87, - CrstNls = 88, - CrstNotifyGdb = 89, - CrstObjectList = 90, - CrstOnEventManager = 91, - CrstPatchEntryPoint = 92, - CrstPEImage = 93, - CrstPEImagePDBStream = 94, - CrstPendingTypeLoadEntry = 95, - CrstPinHandle = 96, - CrstPinnedByrefValidation = 97, - CrstProfilerGCRefDataFreeList = 98, - CrstProfilingAPIStatus = 99, - CrstPublisherCertificate = 100, - CrstRCWCache = 101, - CrstRCWCleanupList = 102, - CrstRCWRefCache = 103, - CrstReadyToRunEntryPointToMethodDescMap = 104, - CrstReDacl = 105, - CrstReflection = 106, - CrstReJITDomainTable = 107, - CrstReJITGlobalRequest = 108, - CrstRemoting = 109, - CrstRetThunkCache = 110, - CrstRWLock = 111, - CrstSavedExceptionInfo = 112, - CrstSaveModuleProfileData = 113, - CrstSecurityStackwalkCache = 114, - CrstSharedAssemblyCreate = 115, - CrstSigConvert = 116, - CrstSingleUseLock = 117, - CrstSpecialStatics = 118, - CrstSqmManager = 119, - CrstStackSampler = 120, - CrstStressLog = 121, - CrstStrongName = 122, - CrstStubCache = 123, - CrstStubDispatchCache = 124, - CrstStubUnwindInfoHeapSegments = 125, - CrstSyncBlockCache = 126, - CrstSyncHashLock = 127, - CrstSystemBaseDomain = 128, - CrstSystemDomain = 129, - CrstSystemDomainDelayedUnloadList = 130, - CrstThreadIdDispenser = 131, - CrstThreadpoolEventCache = 132, - CrstThreadpoolTimerQueue = 133, - CrstThreadpoolWaitThreads = 134, - CrstThreadpoolWorker = 135, - CrstThreadStaticDataHashTable = 136, - CrstThreadStore = 137, - CrstTieredCompilation = 138, - CrstTPMethodTable = 139, - CrstTypeEquivalenceMap = 140, - CrstTypeIDMap = 141, - CrstUMEntryThunkCache = 142, - CrstUMThunkHash = 143, - CrstUniqueStack = 144, - CrstUnresolvedClassLock = 145, - CrstUnwindInfoTableLock = 146, - CrstVSDIndirectionCellLock = 147, - CrstWinRTFactoryCache = 148, - CrstWrapperTemplate = 149, - kNumberOfCrstTypes = 150 + CrstCOMCallWrapper = 19, + CrstCOMWrapperCache = 20, + CrstConnectionNameTable = 21, + CrstContexts = 22, + CrstCoreCLRBinderLog = 23, + CrstCrstCLRPrivBinderLocalWinMDPath = 24, + CrstCSPCache = 25, + CrstDataTest1 = 26, + CrstDataTest2 = 27, + CrstDbgTransport = 28, + CrstDeadlockDetection = 29, + CrstDebuggerController = 30, + CrstDebuggerFavorLock = 31, + CrstDebuggerHeapExecMemLock = 32, + CrstDebuggerHeapLock = 33, + CrstDebuggerJitInfo = 34, + CrstDebuggerMutex = 35, + CrstDelegateToFPtrHash = 36, + CrstDomainLocalBlock = 37, + CrstDynamicIL = 38, + CrstDynamicMT = 39, + CrstDynLinkZapItems = 40, + CrstEtwTypeLogHash = 41, + CrstEventPipe = 42, + CrstEventStore = 43, + CrstException = 44, + CrstExecuteManLock = 45, + CrstExecuteManRangeLock = 46, + CrstFCall = 47, + CrstFriendAccessCache = 48, + CrstFuncPtrStubs = 49, + CrstFusionAppCtx = 50, + CrstGCCover = 51, + CrstGCMemoryPressure = 52, + CrstGlobalStrLiteralMap = 53, + CrstHandleTable = 54, + CrstHostAssemblyMap = 55, + CrstHostAssemblyMapAdd = 56, + CrstIbcProfile = 57, + CrstIJWFixupData = 58, + CrstIJWHash = 59, + CrstILStubGen = 60, + CrstInlineTrackingMap = 61, + CrstInstMethodHashTable = 62, + CrstInterfaceVTableMap = 63, + CrstInterop = 64, + CrstInteropData = 65, + CrstIOThreadpoolWorker = 66, + CrstIsJMCMethod = 67, + CrstISymUnmanagedReader = 68, + CrstJit = 69, + CrstJitGenericHandleCache = 70, + CrstJitPerf = 71, + CrstJumpStubCache = 72, + CrstLeafLock = 73, + CrstListLock = 74, + CrstLoaderAllocator = 75, + CrstLoaderAllocatorReferences = 76, + CrstLoaderHeap = 77, + CrstMda = 78, + CrstMetadataTracker = 79, + CrstModIntPairList = 80, + CrstModule = 81, + CrstModuleFixup = 82, + CrstModuleLookupTable = 83, + CrstMulticoreJitHash = 84, + CrstMulticoreJitManager = 85, + CrstMUThunkHash = 86, + CrstNativeBinderInit = 87, + CrstNativeImageCache = 88, + CrstNls = 89, + CrstNotifyGdb = 90, + CrstObjectList = 91, + CrstOnEventManager = 92, + CrstPatchEntryPoint = 93, + CrstPEImage = 94, + CrstPEImagePDBStream = 95, + CrstPendingTypeLoadEntry = 96, + CrstPinHandle = 97, + CrstPinnedByrefValidation = 98, + CrstProfilerGCRefDataFreeList = 99, + CrstProfilingAPIStatus = 100, + CrstPublisherCertificate = 101, + CrstRCWCache = 102, + CrstRCWCleanupList = 103, + CrstRCWRefCache = 104, + CrstReadyToRunEntryPointToMethodDescMap = 105, + CrstReDacl = 106, + CrstReflection = 107, + CrstReJITDomainTable = 108, + CrstReJITGlobalRequest = 109, + CrstRemoting = 110, + CrstRetThunkCache = 111, + CrstRWLock = 112, + CrstSavedExceptionInfo = 113, + CrstSaveModuleProfileData = 114, + CrstSecurityStackwalkCache = 115, + CrstSharedAssemblyCreate = 116, + CrstSigConvert = 117, + CrstSingleUseLock = 118, + CrstSpecialStatics = 119, + CrstSqmManager = 120, + CrstStackSampler = 121, + CrstStressLog = 122, + CrstStrongName = 123, + CrstStubCache = 124, + CrstStubDispatchCache = 125, + CrstStubUnwindInfoHeapSegments = 126, + CrstSyncBlockCache = 127, + CrstSyncHashLock = 128, + CrstSystemBaseDomain = 129, + CrstSystemDomain = 130, + CrstSystemDomainDelayedUnloadList = 131, + CrstThreadIdDispenser = 132, + CrstThreadpoolEventCache = 133, + CrstThreadpoolTimerQueue = 134, + CrstThreadpoolWaitThreads = 135, + CrstThreadpoolWorker = 136, + CrstThreadStaticDataHashTable = 137, + CrstThreadStore = 138, + CrstTieredCompilation = 139, + CrstTPMethodTable = 140, + CrstTypeEquivalenceMap = 141, + CrstTypeIDMap = 142, + CrstUMEntryThunkCache = 143, + CrstUMThunkHash = 144, + CrstUniqueStack = 145, + CrstUnresolvedClassLock = 146, + CrstUnwindInfoTableLock = 147, + CrstVSDIndirectionCellLock = 148, + CrstWinRTFactoryCache = 149, + CrstWrapperTemplate = 150, + kNumberOfCrstTypes = 151 }; #endif // __CRST_TYPES_INCLUDED @@ -196,6 +197,7 @@ int g_rgCrstLevelMap[] = 0, // CrstCLRPrivBinderMaps 3, // CrstCLRPrivBinderMapsAdd 6, // CrstCodeFragmentHeap + 0, // CrstCOMCallWrapper 4, // CrstCOMWrapperCache 0, // CrstConnectionNameTable 15, // CrstContexts @@ -351,6 +353,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstCLRPrivBinderMaps", "CrstCLRPrivBinderMapsAdd", "CrstCodeFragmentHeap", + "CrstCOMCallWrapper", "CrstCOMWrapperCache", "CrstConnectionNameTable", "CrstContexts", diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index 19999cda26..1f978256fa 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -3554,7 +3554,6 @@ AppDomain::AppDomain() m_dwFlags = 0; m_pDefaultContext = NULL; #ifdef FEATURE_COMINTEROP - m_pComCallWrapperCache = NULL; m_pRCWCache = NULL; m_pRCWRefCache = NULL; m_pLicenseInteropHelperMT = NULL; @@ -3892,28 +3891,6 @@ void AppDomain::Terminate() delete m_pRCWRefCache; m_pRCWRefCache = NULL; } - - if (m_pComCallWrapperCache) - { - m_pComCallWrapperCache->Neuter(); - m_pComCallWrapperCache->Release(); - } - - // if the above released the wrapper cache, then it will call back and reset our - // m_pComCallWrapperCache to null. If not null, then need to set it's domain pointer to - // null. - if (! m_pComCallWrapperCache) - { - LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::Terminate ComCallWrapperCache released\n")); - } -#ifdef _DEBUG - else - { - m_pComCallWrapperCache = NULL; - LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::Terminate ComCallWrapperCache not released\n")); - } -#endif // _DEBUG - #endif // FEATURE_COMINTEROP @@ -6910,28 +6887,6 @@ BOOL AppDomain::WasSystemAssemblyLoadEventSent(void) #ifdef FEATURE_COMINTEROP -ComCallWrapperCache *AppDomain::GetComCallWrapperCache() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - - if (! m_pComCallWrapperCache) - { - BaseDomain::LockHolder lh(this); - - if (! m_pComCallWrapperCache) - m_pComCallWrapperCache = ComCallWrapperCache::Create(this); - } - _ASSERTE(m_pComCallWrapperCache); - return m_pComCallWrapperCache; -} - RCWRefCache *AppDomain::GetRCWRefCache() { CONTRACT(RCWRefCache*) diff --git a/src/vm/appdomain.hpp b/src/vm/appdomain.hpp index b326b26519..4163c306d9 100644 --- a/src/vm/appdomain.hpp +++ b/src/vm/appdomain.hpp @@ -1101,45 +1101,6 @@ public: // This will look up interop data for a method table // -#ifndef DACCESS_COMPILE - // Returns the data pointer if present, NULL otherwise - InteropMethodTableData *LookupComInteropData(MethodTable *pMT) - { - // Take the lock - CrstHolder holder(&m_InteropDataCrst); - - // Lookup - InteropMethodTableData *pData = (InteropMethodTableData*) m_interopDataHash.LookupValue((UPTR) pMT, (LPVOID) NULL); - - // Not there... - if (pData == (InteropMethodTableData*) INVALIDENTRY) - return NULL; - - // Found it - return pData; - } - - // Returns TRUE if successfully inserted, FALSE if this would be a duplicate entry - BOOL InsertComInteropData(MethodTable* pMT, InteropMethodTableData *pData) - { - // We don't keep track of this kind of information for interfaces - _ASSERTE(!pMT->IsInterface()); - - // Take the lock - CrstHolder holder(&m_InteropDataCrst); - - // Check to see that it's not already in there - InteropMethodTableData *pDupData = (InteropMethodTableData*) m_interopDataHash.LookupValue((UPTR) pMT, (LPVOID) NULL); - if (pDupData != (InteropMethodTableData*) INVALIDENTRY) - return FALSE; - - // Not in there, so insert - m_interopDataHash.InsertValue((UPTR) pMT, (LPVOID) pData); - - // Success - return TRUE; - } -#endif // DACCESS_COMPILE #endif // FEATURE_COMINTEROP void SetDisableInterfaceCache() @@ -2508,7 +2469,6 @@ public: } #ifdef FEATURE_COMINTEROP - ComCallWrapperCache* GetComCallWrapperCache(); RCWCache *GetRCWCache() { WRAPPER_NO_CONTRACT; @@ -2530,12 +2490,6 @@ public: RCWRefCache *GetRCWRefCache(); - void ResetComCallWrapperCache() - { - LIMITED_METHOD_CONTRACT; - m_pComCallWrapperCache = NULL; - } - MethodTable* GetLicenseInteropHelperMethodTable(); #endif // FEATURE_COMINTEROP @@ -3155,10 +3109,6 @@ private: // Hash table that remembers the last cached WinRT factory object per type per appdomain. WinRTFactoryCache *m_pWinRTFactoryCache; - // The wrapper cache for this domain - it has its own CCacheLineAllocator on a per domain basis - // to allow the domain to go away and eventually kill the memory when all refs are gone - ComCallWrapperCache *m_pComCallWrapperCache; - // this cache stores the RCWs in this domain RCWCache *m_pRCWCache; diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp index 9ff186e756..eb54b95a4e 100644 --- a/src/vm/ceeload.cpp +++ b/src/vm/ceeload.cpp @@ -829,6 +829,12 @@ BOOL Module::CanCacheWinRTTypeByGuid(MethodTable *pMT) } CONTRACTL_END; + // Don't cache WinRT types in collectible modules. + if (IsCollectible()) + { + return FALSE; + } + // Don't cache mscorlib-internal declarations of WinRT types. if (IsSystem() && pMT->IsProjectedFromWinRT()) return FALSE; diff --git a/src/vm/comcallablewrapper.cpp b/src/vm/comcallablewrapper.cpp index 9b3d997c06..57d1ede8ff 100644 --- a/src/vm/comcallablewrapper.cpp +++ b/src/vm/comcallablewrapper.cpp @@ -1149,13 +1149,6 @@ VOID SimpleComCallWrapper::Neuter(bool fSkipHandleCleanup) // NULL the context, we shall only use m_dwDomainId from this point on m_pContext = NULL; - // NULL the handles of DispatchMemberInfo if the AppDomain host them has been unloaded - DispatchExInfo *pDispatchExInfo = GetDispatchExInfo(); - if (pDispatchExInfo) - { - pDispatchExInfo->DestroyMemberInfoHandles(); - } - StackSString ssMessage; ComCallWrapper *pWrap = m_pWrap; if (g_pConfig->LogCCWRefCountChangeEnabled()) @@ -2242,10 +2235,7 @@ ComCallWrapper* ComCallWrapper::CopyFromTemplate(ComCallWrapperTemplate* pTempla size_t numInterfaces = pTemplate->GetNumInterfaces(); // we have a template, create a wrapper and initialize from the template - // alloc wrapper, aligned 32 bytes - if (pWrapperCache->IsDomainUnloading()) - COMPlusThrow(kAppDomainUnloadedException); - + // alloc wrapper, aligned to cache line NewCCWHolder pStartWrapper(pWrapperCache); pStartWrapper = (ComCallWrapper*)pWrapperCache->GetCacheLineAllocator()-> #ifdef _WIN64 @@ -2588,15 +2578,7 @@ ComCallWrapper* ComCallWrapper::CreateWrapper(OBJECTREF* ppObj, ComCallWrapperTe ComCallWrapperCache *pWrapperCache = NULL; TypeHandle thClass = pServer->GetTrueTypeHandle(); - // - // Collectible types do not support com interop - // - if (thClass.GetMethodTable()->Collectible()) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM")); - } - - pWrapperCache = pContext->GetDomain()->GetComCallWrapperCache(); + pWrapperCache = thClass.GetMethodTable()->GetLoaderAllocator()->GetComCallWrapperCache(); { // check if somebody beat us to it @@ -3809,7 +3791,7 @@ ComCallWrapperCache::ComCallWrapperCache() : m_lock(CrstCOMWrapperCache), m_cbRef(0), m_pCacheLineAllocator(NULL), - m_pDomain(NULL) + m_pLoaderAllocator(NULL) { WRAPPER_NO_CONTRACT; @@ -3824,26 +3806,25 @@ ComCallWrapperCache::~ComCallWrapperCache() CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::~ComCallWrapperCache %8.8x in domain [%d] %8.8x %S\n", - this, GetDomain()?GetDomain()->GetId().m_dwId:0, - GetDomain(), GetDomain() ? GetDomain()->GetFriendlyNameForLogging() : NULL)); + LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::~ComCallWrapperCache %8.8x in loader allocator [%d] %8.8x\n", + this, GetLoaderAllocator() ? GetLoaderAllocator()->GetCreationNumber() : 0, GetLoaderAllocator())); - if (m_pCacheLineAllocator) + if (m_pCacheLineAllocator) { delete m_pCacheLineAllocator; m_pCacheLineAllocator = NULL; } - AppDomain *pDomain = GetDomain(); // don't use member directly, need to mask off flags - if (pDomain) + LoaderAllocator *pLoaderAllocator = GetLoaderAllocator(); // don't use member directly, need to mask off flags + if (pLoaderAllocator) { - // clear hook in AppDomain as we're going away - pDomain->ResetComCallWrapperCache(); + // clear hook in LoaderAllocator as we're going away + pLoaderAllocator->ResetComCallWrapperCache(); } } @@ -3852,7 +3833,7 @@ ComCallWrapperCache::~ComCallWrapperCache() // ComCallable wrapper manager // Create/Init method //------------------------------------------------------------------- -ComCallWrapperCache *ComCallWrapperCache::Create(AppDomain *pDomain) +ComCallWrapperCache *ComCallWrapperCache::Create(LoaderAllocator *pLoaderAllocator) { CONTRACT (ComCallWrapperCache*) { @@ -3860,19 +3841,19 @@ ComCallWrapperCache *ComCallWrapperCache::Create(AppDomain *pDomain) GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(CheckPointer(pDomain)); + PRECONDITION(CheckPointer(pLoaderAllocator)); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; NewHolder<ComCallWrapperCache> pWrapperCache = new ComCallWrapperCache(); - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Create %8.8x in domain %8.8x %S\n", - (ComCallWrapperCache *)pWrapperCache, pDomain, pDomain->GetFriendlyName(FALSE))); + LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Create %8.8x in loader allocator [%d] %8.8x\n", + (ComCallWrapperCache *)pWrapperCache, pLoaderAllocator ? pLoaderAllocator->GetCreationNumber() : 0, pLoaderAllocator)); NewHolder<CCacheLineAllocator> line = new CCacheLineAllocator; - pWrapperCache->m_pDomain = pDomain; + pWrapperCache->m_pLoaderAllocator = pLoaderAllocator; pWrapperCache->m_pCacheLineAllocator = line; pWrapperCache->AddRef(); @@ -3882,18 +3863,6 @@ ComCallWrapperCache *ComCallWrapperCache::Create(AppDomain *pDomain) RETURN pWrapperCache; } - -void ComCallWrapperCache::Neuter() -{ - WRAPPER_NO_CONTRACT; - - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Neuter %8.8x in domain [%d] %8.8x %S\n", - this, GetDomain()?GetDomain()->GetId().m_dwId:0, - GetDomain(),GetDomain() ? GetDomain()->GetFriendlyNameForLogging() : NULL)); - ClearDomain(); -} - - //------------------------------------------------------------------- // ComCallable wrapper manager // LONG AddRef() @@ -3911,9 +3880,8 @@ LONG ComCallWrapperCache::AddRef() COUNTER_ONLY(GetPerfCounters().m_Interop.cCCW++); LONG i = FastInterlockIncrement(&m_cbRef); - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Addref %8.8x with %d in domain [%d] %8.8x %S\n", - this, i, GetDomain()?GetDomain()->GetId().m_dwId:0, - GetDomain(), GetDomain() ? GetDomain()->GetFriendlyNameForLogging() : NULL)); + LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Addref %8.8x with %d in loader allocator [%d] %8.8x\n", + this, i, GetLoaderAllocator()?GetLoaderAllocator()->GetCreationNumber() : 0, GetLoaderAllocator())); return i; } @@ -3937,9 +3905,8 @@ LONG ComCallWrapperCache::Release() LONG i = FastInterlockDecrement(&m_cbRef); _ASSERTE(i >= 0); - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Release %8.8x with %d in domain [%d] %8.8x %S\n", - this, i, GetDomain()?GetDomain()->GetId().m_dwId:0, - GetDomain(), GetDomain() ? GetDomain()->GetFriendlyNameForLogging() : NULL)); + LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Release %8.8x with %d in loader allocator [%d] %8.8x\n", + this, i, GetLoaderAllocator() ? GetLoaderAllocator()->GetCreationNumber() : 0, GetLoaderAllocator())); if ( i == 0) delete this; diff --git a/src/vm/comcallablewrapper.h b/src/vm/comcallablewrapper.h index 56ebc94a96..e58b4c80f9 100644 --- a/src/vm/comcallablewrapper.h +++ b/src/vm/comcallablewrapper.h @@ -41,11 +41,6 @@ typedef DPTR(struct SimpleComCallWrapper) PTR_SimpleComCallWrapper; class ComCallWrapperCache { - enum - { - AD_IS_UNLOADING = 0x01, - }; - public: // Encapsulate a SpinLockHolder, so that clients of our lock don't have to know // the details of our implementation. @@ -62,12 +57,8 @@ public: ComCallWrapperCache(); ~ComCallWrapperCache(); - // create a new WrapperCache (one per domain) - static ComCallWrapperCache* Create(AppDomain *pDomain); - - // Called when the domain is going away. We may have outstanding references to this cache, - // so we keep it around in a neutered state. - void Neuter(); + // create a new WrapperCache (one per each LoaderAllocator) + static ComCallWrapperCache* Create(LoaderAllocator *pLoaderAllocator); // refcount LONG AddRef(); @@ -87,9 +78,9 @@ public: RETURN m_pCacheLineAllocator; } - AppDomain* GetDomain() + LoaderAllocator* GetLoaderAllocator() { - CONTRACT (AppDomain*) + CONTRACT (LoaderAllocator*) { WRAPPER(THROWS); WRAPPER(GC_TRIGGERS); @@ -98,41 +89,13 @@ public: } CONTRACT_END; - RETURN ((AppDomain*)((size_t)m_pDomain & ~AD_IS_UNLOADING)); - } - - void ClearDomain() - { - LIMITED_METHOD_CONTRACT; - - m_pDomain = (AppDomain *)AD_IS_UNLOADING; - } - - void SetDomainIsUnloading() - { - LIMITED_METHOD_CONTRACT; - - m_pDomain = (AppDomain*)((size_t)m_pDomain | AD_IS_UNLOADING); - } - - void ResetDomainIsUnloading() - { - LIMITED_METHOD_CONTRACT; - - m_pDomain = (AppDomain*)((size_t)m_pDomain & (~AD_IS_UNLOADING)); - } - - BOOL IsDomainUnloading() - { - LIMITED_METHOD_CONTRACT; - - return ((size_t)m_pDomain & AD_IS_UNLOADING) != 0; + RETURN m_pLoaderAllocator; } private: LONG m_cbRef; CCacheLineAllocator* m_pCacheLineAllocator; - AppDomain* m_pDomain; + LoaderAllocator* m_pLoaderAllocator; // spin lock for fast synchronization Crst m_lock; diff --git a/src/vm/comtoclrcall.h b/src/vm/comtoclrcall.h index d2f3891993..5425fc0074 100644 --- a/src/vm/comtoclrcall.h +++ b/src/vm/comtoclrcall.h @@ -402,7 +402,7 @@ public: CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; diff --git a/src/vm/dispatchinfo.cpp b/src/vm/dispatchinfo.cpp index fe79ac6260..1b4fbb45e4 100644 --- a/src/vm/dispatchinfo.cpp +++ b/src/vm/dispatchinfo.cpp @@ -137,10 +137,6 @@ DispatchMemberInfo::~DispatchMemberInfo() if (m_pParamInOnly) delete [] m_pParamInOnly; - // Destroy the member info object. - if (m_hndMemberInfo) - DestroyHandle(m_hndMemberInfo); - // Clear the name of the member. m_strName.Clear(); } @@ -307,14 +303,14 @@ PTRARRAYREF DispatchMemberInfo::GetParameters() { case Method: { - pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, GetMemberInfoObject()->GetTypeHandle()); _ASSERTE(pGetParamsMD && "Unable to find method MemberBase::GetParameters"); break; } case Property: { - pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, GetMemberInfoObject()->GetTypeHandle()); _ASSERTE(pGetParamsMD && "Unable to find method PropertyInfo::GetIndexParameters"); break; } @@ -323,14 +319,17 @@ PTRARRAYREF DispatchMemberInfo::GetParameters() // If the member has parameters then retrieve the array of parameters. if (pGetParamsMD != NULL) { - MethodDescCallSite getParams(pGetParamsMD, m_hndMemberInfo); + OBJECTREF memberInfoObject = GetMemberInfoObject(); + GCPROTECT_BEGIN(memberInfoObject) + MethodDescCallSite getParams(pGetParamsMD, &memberInfoObject); ARG_SLOT GetParamsArgs[] = { - ObjToArgSlot(ObjectFromHandle(m_hndMemberInfo)) + ObjToArgSlot(memberInfoObject) }; ParamArray = (PTRARRAYREF) getParams.Call_RetOBJECTREF(GetParamsArgs); + GCPROTECT_END(); } return ParamArray; @@ -565,7 +564,7 @@ void DispatchMemberInfo::DetermineMemberType() } CONTRACTL_END; - OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + OBJECTREF MemberInfoObj = GetMemberInfoObject(); // Check to see if the member info is of a type we have already seen. TypeHandle pMemberInfoClass = MemberInfoObj->GetTypeHandle(); @@ -617,7 +616,7 @@ void DispatchMemberInfo::DetermineParamCount() MethodDesc *pGetParamsMD = NULL; - OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + OBJECTREF MemberInfoObj = GetMemberInfoObject(); GCPROTECT_BEGIN(MemberInfoObj); { // Retrieve the method to use to retrieve the array of parameters. @@ -625,14 +624,14 @@ void DispatchMemberInfo::DetermineParamCount() { case Method: { - pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, GetMemberInfoObject()->GetTypeHandle()); _ASSERTE(pGetParamsMD && "Unable to find method MemberBase::GetParameters"); break; } case Property: { - pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, GetMemberInfoObject()->GetTypeHandle()); _ASSERTE(pGetParamsMD && "Unable to find method PropertyInfo::GetIndexParameters"); break; } @@ -645,7 +644,7 @@ void DispatchMemberInfo::DetermineParamCount() ARG_SLOT GetParamsArgs[] = { - ObjToArgSlot(ObjectFromHandle(m_hndMemberInfo)) + ObjToArgSlot(GetMemberInfoObject()) }; PTRARRAYREF ParamArray = (PTRARRAYREF) getParams.Call_RetOBJECTREF(GetParamsArgs); @@ -676,7 +675,7 @@ void DispatchMemberInfo::DetermineCultureAwareness() MethodTable * pLcIdConvAttrClass = MscorlibBinder::GetClass(CLASS__LCID_CONVERSION_TYPE); // Check to see if the attribute is set. - OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + OBJECTREF MemberInfoObj = GetMemberInfoObject(); GCPROTECT_BEGIN(MemberInfoObj); { // Retrieve the method to use to determine if the DispIdAttribute custom attribute is set. @@ -732,7 +731,7 @@ void DispatchMemberInfo::SetUpParamMarshalerInfo() BOOL bSetUpReturnValueOnly = FALSE; OBJECTREF SetterObj = NULL; OBJECTREF GetterObj = NULL; - OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + OBJECTREF MemberInfoObj = GetMemberInfoObject(); GCPROTECT_BEGIN(SetterObj); GCPROTECT_BEGIN(GetterObj); @@ -1024,6 +1023,18 @@ void DispatchMemberInfo::SetUpDispParamAttributes(int iParam, MarshalInfo* Info) m_pParamInOnly[iParam] = ( Info->IsIn() && !Info->IsOut() ); } +#ifndef DACCESS_COMPILE +OBJECTREF DispatchMemberInfo::GetMemberInfoObject() +{ + return m_pDispInfo->GetLoaderAllocator()->GetHandleValue(m_hndMemberInfo); +} + +void DispatchMemberInfo::ClearMemberInfoObject() +{ + m_pDispInfo->GetLoaderAllocator()->SetHandleValue(m_hndMemberInfo, NULL); +} +#endif // DACCESS_COMPILE + //-------------------------------------------------------------------------------- // The DispatchInfo class implementation. @@ -1125,7 +1136,7 @@ DispatchMemberInfo* DispatchInfo::FindMember(SString& strName, BOOL bCaseSensiti DispatchMemberInfo *pCurrMemberInfo = m_pFirstMemberInfo; while (pCurrMemberInfo) { - if (ObjectFromHandle(pCurrMemberInfo->m_hndMemberInfo) != NULL) + if (pCurrMemberInfo->GetMemberInfoObject() != NULL) { // Compare the 2 strings. if (bCaseSensitive ? @@ -1162,7 +1173,7 @@ DispatchMemberInfo* DispatchInfo::CreateDispatchMemberInfoInstance(DISPID DispID CONTRACT_END; DispatchMemberInfo* pInfo = new DispatchMemberInfo(this, DispID, strMemberName, MemberInfoObj); - pInfo->SetHandle(MemberInfoObj->GetMethodTable()->GetDomain()->CreateHandle(MemberInfoObj)); + pInfo->SetHandle(GetLoaderAllocator()->AllocateHandle(MemberInfoObj)); RETURN pInfo; } @@ -1584,7 +1595,7 @@ void DispatchInfo::InvokeMemberWorker(DispatchMemberInfo* pDispMemberInfo, } // Retrieve the member info object and the type of the member. - pObjs->MemberInfo = ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo); + pObjs->MemberInfo = pDispMemberInfo->GetMemberInfoObject(); MemberType = pDispMemberInfo->GetMemberType(); switch (MemberType) @@ -2056,7 +2067,7 @@ HRESULT DispatchInfo::InvokeMember(SimpleComCallWrapper *pSimpleWrap, DISPID id, // DispatchMemberInfo *pDispMemberInfo = FindMember(id); - if (!pDispMemberInfo || !(*((Object **)pDispMemberInfo->m_hndMemberInfo))) + if (!pDispMemberInfo || !pDispMemberInfo->GetMemberInfoObject()) { pDispMemberInfo = NULL; } @@ -2288,27 +2299,6 @@ HRESULT DispatchInfo::InvokeMember(SimpleComCallWrapper *pSimpleWrap, DISPID id, return hr; } -void DispatchInfo::DestroyMemberInfoHandles() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - DispatchMemberInfo* pCurrMember = m_pFirstMemberInfo; - while (pCurrMember) - { - // Destroy the handle - DestroyHandle(pCurrMember->m_hndMemberInfo); - pCurrMember->m_hndMemberInfo = NULL; - // Process the next member. - pCurrMember = pCurrMember->m_pNext; - } -} - // Parameter marshaling helpers. void DispatchInfo::MarshalParamNativeToManaged(DispatchMemberInfo *pMemberInfo, int iParam, VARIANT *pSrcVar, OBJECTREF *pDestObj) { @@ -2793,7 +2783,7 @@ BOOL DispatchInfo::SynchWithManagedView() while (pCurrMemberInfo) { // We can simply compare the OBJECTREF's. - if (CurrMemberInfoObj == ObjectFromHandle(pCurrMemberInfo->m_hndMemberInfo)) + if (CurrMemberInfoObj == pCurrMemberInfo->GetMemberInfoObject()) { // We have found a match. bMatch = TRUE; @@ -3317,7 +3307,7 @@ DispatchMemberInfo* DispatchExInfo::CreateDispatchMemberInfoInstance(DISPID Disp AppDomain* pDomain = SystemDomain::GetAppDomainFromId(m_pSimpleWrapperOwner->GetDomainID(), ADV_CURRENTAD); - pInfo->SetHandle(pDomain->CreateHandle(MemberInfoObj)); + pInfo->SetHandle(GetLoaderAllocator()->AllocateHandle(MemberInfoObj)); RETURN pInfo; } @@ -3352,7 +3342,7 @@ DispatchMemberInfo* DispatchExInfo::GetFirstMember() } // Now we need to make sure we skip any members that are deleted. - while ((*ppNextMemberInfo) && !ObjectFromHandle((*ppNextMemberInfo)->m_hndMemberInfo)) + while ((*ppNextMemberInfo) && !(*ppNextMemberInfo)->GetMemberInfoObject()) ppNextMemberInfo = &((*ppNextMemberInfo)->m_pNext); RETURN *ppNextMemberInfo; @@ -3392,7 +3382,7 @@ DispatchMemberInfo* DispatchExInfo::GetNextMember(DISPID CurrMemberDispID) } // Now we need to make sure we skip any members that are deleted. - while ((*ppNextMemberInfo) && !ObjectFromHandle((*ppNextMemberInfo)->m_hndMemberInfo)) + while ((*ppNextMemberInfo) && !(*ppNextMemberInfo)->GetMemberInfoObject()) ppNextMemberInfo = &((*ppNextMemberInfo)->m_pNext); RETURN *ppNextMemberInfo; @@ -3501,7 +3491,7 @@ void DispatchExInfo::DeleteMember(DISPID DispID) CrstHolder ch(&m_lock); // If the member does not exist, it is static or has been deleted then we have nothing more to do. - if (pDispMemberInfo && (ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo) != NULL)) + if (pDispMemberInfo && (pDispMemberInfo->GetMemberInfoObject() != NULL)) { OBJECTREF TargetObj = GetReflectionObject(); GCPROTECT_BEGIN(TargetObj); @@ -3510,7 +3500,7 @@ void DispatchExInfo::DeleteMember(DISPID DispID) MethodDesc *pMD = GetIExpandoMD(METHOD__IEXPANDO__REMOVE_MEMBER); MethodDescCallSite removeMember(pMD, &TargetObj); - OBJECTREF MemberInfoObj = ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo); + OBJECTREF MemberInfoObj = pDispMemberInfo->GetMemberInfoObject(); // Prepare the arguments that will be passed to RemoveMember. ARG_SLOT Args[] = @@ -3523,7 +3513,7 @@ void DispatchExInfo::DeleteMember(DISPID DispID) removeMember.Call(Args); // Set the handle to point to NULL to indicate the member has been removed. - StoreObjectInHandle(pDispMemberInfo->m_hndMemberInfo, NULL); + pDispMemberInfo->ClearMemberInfoObject(); GCPROTECT_END(); } diff --git a/src/vm/dispatchinfo.h b/src/vm/dispatchinfo.h index 97b4ea7f39..f5f9b9b713 100644 --- a/src/vm/dispatchinfo.h +++ b/src/vm/dispatchinfo.h @@ -138,7 +138,7 @@ struct DispatchMemberInfo return m_bLastParamOleVarArg; } - void SetHandle(OBJECTHANDLE objhnd) + void SetHandle(LOADERHANDLE objhnd) { m_hndMemberInfo = objhnd; } @@ -149,6 +149,11 @@ struct DispatchMemberInfo return m_bRequiresManagedCleanup; } +#ifndef DACCESS_COMPILE + OBJECTREF GetMemberInfoObject(); + void ClearMemberInfoObject(); +#endif // DACCESS_COMPILE + // Parameter marshaling methods. void MarshalParamNativeToManaged(int iParam, VARIANT *pSrcVar, OBJECTREF *pDestObj); void MarshalParamManagedToNativeRef(int iParam, OBJECTREF *pSrcObj, VARIANT *pRefVar); @@ -174,7 +179,7 @@ private: void SetUpDispParamAttributes(int iParam, MarshalInfo* Info); public: DISPID m_DispID; - OBJECTHANDLE m_hndMemberInfo; + LOADERHANDLE m_hndMemberInfo; DispParamMarshaler** m_apParamMarshaler; BOOL* m_pParamInOnly; DispatchMemberInfo* m_pNext; @@ -276,9 +281,6 @@ public: int* pManagedMethodParamIndexMap, VARIANT** aByrefArgOleVariant); - // Method to NULL the handles inside DispatchMemberInfo - void DestroyMemberInfoHandles(); - // Methods to retrieve the cached MD's static MethodDesc* GetFieldInfoMD(BinderMethodID Method, TypeHandle hndFieldInfoType); static MethodDesc* GetPropertyInfoMD(BinderMethodID Method, TypeHandle hndPropInfoType); @@ -296,6 +298,11 @@ public: // Returns TRUE if the argument is "Missing" static BOOL VariantIsMissing(VARIANT *pOle); + LoaderAllocator* GetLoaderAllocator() + { + return m_pMT->GetLoaderAllocator(); + } + protected: // Parameter marshaling helpers. void MarshalParamNativeToManaged(DispatchMemberInfo *pMemberInfo, int iParam, VARIANT *pSrcVar, OBJECTREF *pDestObj); diff --git a/src/vm/interoputil.cpp b/src/vm/interoputil.cpp index 8bb08e4e2a..510e494f18 100644 --- a/src/vm/interoputil.cpp +++ b/src/vm/interoputil.cpp @@ -3771,10 +3771,6 @@ BOOL IsTypeVisibleFromCom(TypeHandle hndType) return FALSE; } - // If the type is collectible, then it is not visible from COM. - if (hndType.GetLoaderAllocator()->IsCollectible()) - return FALSE; - return SpecialIsGenericTypeVisibleFromCom(hndType); } @@ -5095,11 +5091,11 @@ ClassFactoryBase *GetComClassFactory(MethodTable* pClassMT) if (pClsFac == NULL) { // - // Collectible types do not support com interop + // Collectible types do not support WinRT interop // - if (pClassMT->Collectible()) + if (pClassMT->Collectible() && (pClassMT->IsExportedToWinRT() || pClassMT->IsProjectedFromWinRT())) { - COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM")); + COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleWinRT")); } NewHolder<ClassFactoryBase> pNewFactory; diff --git a/src/vm/loaderallocator.cpp b/src/vm/loaderallocator.cpp index 314f5cb43e..d51af51347 100644 --- a/src/vm/loaderallocator.cpp +++ b/src/vm/loaderallocator.cpp @@ -10,6 +10,7 @@ #ifndef DACCESS_COMPILE #include "comdelegate.h" #endif +#include "comcallablewrapper.h" //***************************************************************************** // Used by LoaderAllocator::Init for easier readability. @@ -72,6 +73,10 @@ LoaderAllocator::LoaderAllocator() m_pJumpStubCache = NULL; m_IsCollectible = false; +#ifdef FEATURE_COMINTEROP + m_pComCallWrapperCache = NULL; +#endif + m_pUMEntryThunkCache = NULL; m_nLoaderAllocator = InterlockedIncrement64((LONGLONG *)&LoaderAllocator::cLoaderAllocatorsCreated); @@ -657,6 +662,27 @@ BOOL QCALLTYPE LoaderAllocator::Destroy(QCall::LoaderAllocatorHandle pLoaderAllo // This will probably change for shared code unloading _ASSERTE(pID->GetType() == LAT_Assembly); +#ifdef FEATURE_COMINTEROP + if (pLoaderAllocator->m_pComCallWrapperCache) + { + pLoaderAllocator->m_pComCallWrapperCache->Release(); + + // if the above released the wrapper cache, then it will call back and reset our + // m_pComCallWrapperCache to null. + if (!pLoaderAllocator->m_pComCallWrapperCache) + { + LOG((LF_CLASSLOADER, LL_INFO10, "LoaderAllocator::Destroy ComCallWrapperCache released\n")); + } + #ifdef _DEBUG + else + { + pLoaderAllocator->m_pComCallWrapperCache = NULL; + LOG((LF_CLASSLOADER, LL_INFO10, "LoaderAllocator::Destroy ComCallWrapperCache not released\n")); + } + #endif // _DEBUG + } +#endif // FEATURE_COMINTEROP + DomainAssembly* pDomainAssembly = (DomainAssembly*)(pID->GetDomainAssemblyIterator()); if (pDomainAssembly != NULL) { @@ -1043,6 +1069,10 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) m_pDomain = pDomain; m_crstLoaderAllocator.Init(CrstLoaderAllocator, (CrstFlags)CRST_UNSAFE_COOPGC); +#ifdef FEATURE_COMINTEROP + m_InteropDataCrst.Init(CrstInteropData, CRST_REENTRANCY); + m_ComCallWrapperCrst.Init(CrstCOMCallWrapper); +#endif // // Initialize the heaps @@ -1189,6 +1219,14 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) // Set up the IL stub cache m_ILStubCache.Init(m_pHighFrequencyHeap); + +#ifdef FEATURE_COMINTEROP + // Init the COM Interop data hash + { + LockOwner lock = { &m_InteropDataCrst, IsOwnerOfCrst }; + m_interopDataHash.Init(0, NULL, false, &lock); + } +#endif // FEATURE_COMINTEROP } @@ -1295,6 +1333,10 @@ void LoaderAllocator::Terminate() m_pUMEntryThunkCache = NULL; m_crstLoaderAllocator.Destroy(); +#ifdef FEATURE_COMINTEROP + m_ComCallWrapperCrst.Destroy(); + m_InteropDataCrst.Destroy(); +#endif m_LoaderAllocatorReferences.RemoveAll(); // In collectible types we merge the low frequency and high frequency heaps @@ -1858,6 +1900,30 @@ void AssemblyLoaderAllocator::ReleaseManagedAssemblyLoadContext() } } +#ifdef FEATURE_COMINTEROP +ComCallWrapperCache * LoaderAllocator::GetComCallWrapperCache() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + if (!m_pComCallWrapperCache) + { + CrstHolder lh(&m_ComCallWrapperCrst); + + if (!m_pComCallWrapperCache) + m_pComCallWrapperCache = ComCallWrapperCache::Create(this); + } + _ASSERTE(m_pComCallWrapperCache); + return m_pComCallWrapperCache; +} +#endif // FEATURE_COMINTEROP + // U->M thunks created in this LoaderAllocator and not associated with a delegate. UMEntryThunkCache *LoaderAllocator::GetUMEntryThunkCache() { @@ -1886,4 +1952,47 @@ UMEntryThunkCache *LoaderAllocator::GetUMEntryThunkCache() #endif // !CROSSGEN_COMPILE +#ifdef FEATURE_COMINTEROP + +// Look up interop data for a method table +// Returns the data pointer if present, NULL otherwise +InteropMethodTableData *LoaderAllocator::LookupComInteropData(MethodTable *pMT) +{ + // Take the lock + CrstHolder holder(&m_InteropDataCrst); + + // Lookup + InteropMethodTableData *pData = (InteropMethodTableData*)m_interopDataHash.LookupValue((UPTR)pMT, (LPVOID)NULL); + + // Not there... + if (pData == (InteropMethodTableData*)INVALIDENTRY) + return NULL; + + // Found it + return pData; +} + +// Returns TRUE if successfully inserted, FALSE if this would be a duplicate entry +BOOL LoaderAllocator::InsertComInteropData(MethodTable* pMT, InteropMethodTableData *pData) +{ + // We don't keep track of this kind of information for interfaces + _ASSERTE(!pMT->IsInterface()); + + // Take the lock + CrstHolder holder(&m_InteropDataCrst); + + // Check to see that it's not already in there + InteropMethodTableData *pDupData = (InteropMethodTableData*)m_interopDataHash.LookupValue((UPTR)pMT, (LPVOID)NULL); + if (pDupData != (InteropMethodTableData*)INVALIDENTRY) + return FALSE; + + // Not in there, so insert + m_interopDataHash.InsertValue((UPTR)pMT, (LPVOID)pData); + + // Success + return TRUE; +} + +#endif // FEATURE_COMINTEROP + #endif // !DACCESS_COMPILE diff --git a/src/vm/loaderallocator.hpp b/src/vm/loaderallocator.hpp index f2d729071b..a283721c37 100644 --- a/src/vm/loaderallocator.hpp +++ b/src/vm/loaderallocator.hpp @@ -129,6 +129,10 @@ class ListLockEntryBase; typedef ListLockEntryBase<void*> ListLockEntry; class UMEntryThunkCache; +#ifdef FEATURE_COMINTEROP +class ComCallWrapperCache; +#endif // FEATURE_COMINTEROP + class LoaderAllocator { VPTR_BASE_VTABLE_CLASS(LoaderAllocator) @@ -248,6 +252,18 @@ private: SList<FailedTypeInitCleanupListItem> m_failedTypeInitCleanupList; SegmentedHandleIndexStack m_freeHandleIndexesStack; +#ifdef FEATURE_COMINTEROP + // The wrapper cache for this loader allocator - it has its own CCacheLineAllocator on a per loader allocator basis + // to allow the loader allocator to go away and eventually kill the memory when all refs are gone + + VolatilePtr<ComCallWrapperCache> m_pComCallWrapperCache; + // Used for synchronizing creation of the m_pComCallWrapperCache + CrstExplicitInit m_ComCallWrapperCrst; + // Hash table that maps a MethodTable to COM Interop compatibility data. + PtrHashMap m_interopDataHash; + // Used for synchronizing access to the m_interopDataHash + CrstExplicitInit m_InteropDataCrst; +#endif #ifndef DACCESS_COMPILE @@ -509,6 +525,7 @@ public: void InitVirtualCallStubManager(BaseDomain *pDomain); void UninitVirtualCallStubManager(); + #ifndef CROSSGEN_COMPILE inline VirtualCallStubManager *GetVirtualCallStubManager() { @@ -530,6 +547,30 @@ public: LIMITED_METHOD_CONTRACT; return &m_ILStubCache; } + +#ifdef FEATURE_COMINTEROP + + ComCallWrapperCache * GetComCallWrapperCache(); + + void ResetComCallWrapperCache() + { + LIMITED_METHOD_CONTRACT; + m_pComCallWrapperCache = NULL; + } + +#ifndef DACCESS_COMPILE + + // Look up interop data for a method table + // Returns the data pointer if present, NULL otherwise + InteropMethodTableData *LookupComInteropData(MethodTable *pMT); + + // Returns TRUE if successfully inserted, FALSE if this would be a duplicate entry + BOOL InsertComInteropData(MethodTable* pMT, InteropMethodTableData *pData); + +#endif // DACCESS_COMPILE + +#endif // FEATURE_COMINTEROP + }; // class LoaderAllocator typedef VPTR(LoaderAllocator) PTR_LoaderAllocator; diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp index 5ed91d1fe2..cf7ddccfc7 100644 --- a/src/vm/methodtable.cpp +++ b/src/vm/methodtable.cpp @@ -8313,7 +8313,8 @@ Instantiation MethodTable::GetInstantiationOfParentClass(MethodTable *pWhichPare InteropMethodTableData *MethodTable::LookupComInteropData() { WRAPPER_NO_CONTRACT; - return GetDomain()->LookupComInteropData(this); + + return GetLoaderAllocator()->LookupComInteropData(this); } //========================================================================================== @@ -8321,7 +8322,8 @@ InteropMethodTableData *MethodTable::LookupComInteropData() BOOL MethodTable::InsertComInteropData(InteropMethodTableData *pData) { WRAPPER_NO_CONTRACT; - return GetDomain()->InsertComInteropData(this, pData); + + return GetLoaderAllocator()->InsertComInteropData(this, pData); } //========================================================================================== diff --git a/src/vm/runtimecallablewrapper.cpp b/src/vm/runtimecallablewrapper.cpp index 0832b5616b..6280c2c10f 100644 --- a/src/vm/runtimecallablewrapper.cpp +++ b/src/vm/runtimecallablewrapper.cpp @@ -629,7 +629,6 @@ void ComClassFactory::Init(__in_opt WCHAR* pwszProgID, __in_opt WCHAR* pwszServe m_pwszProgID = pwszProgID; m_pwszServer = pwszServer; - _ASSERTE(pClassMT == NULL || !pClassMT->Collectible()); m_pClassMT = pClassMT; } @@ -2959,14 +2958,6 @@ IUnknown* RCW::GetComIPFromRCW(MethodTable* pMT) RETURN result; } - // - // Collectible types do not support com interop - // - if (pMT->Collectible()) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM")); - } - // returns an AddRef'ed IP RETURN GetComIPForMethodTableFromCache(pMT); } @@ -4739,15 +4730,6 @@ OBJECTREF ComObject::CreateComObjectRef(MethodTable* pMT) { pMT->CheckRestore(); pMT->EnsureInstanceActive(); - - // - // Collectible types do not support com interop - // - if (pMT->Collectible()) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM")); - } - pMT->CheckRunClassInitThrowing(); } diff --git a/src/vm/stdinterfaces.cpp b/src/vm/stdinterfaces.cpp index cfcfd436a2..b96681f76d 100644 --- a/src/vm/stdinterfaces.cpp +++ b/src/vm/stdinterfaces.cpp @@ -1948,7 +1948,7 @@ HRESULT __stdcall DispatchEx_GetMemberName ( DispatchMemberInfo *pDispMemberInfo = pDispExInfo->SynchFindMember(id); // If the member does not exist then we return DISP_E_MEMBERNOTFOUND. - if (!pDispMemberInfo || !ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo)) + if (!pDispMemberInfo || !pDispMemberInfo->GetMemberInfoObject()) { hr = DISP_E_MEMBERNOTFOUND; } @@ -2008,7 +2008,7 @@ HRESULT __stdcall DispatchEx_GetMemberProperties ( DispatchMemberInfo *pDispMemberInfo = pDispExInfo->SynchFindMember(id); // If the member does not exist then we return DISP_E_MEMBERNOTFOUND. - if (!pDispMemberInfo || (MemberInfoObj = ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo)) == NULL) + if (!pDispMemberInfo || (MemberInfoObj = pDispMemberInfo->GetMemberInfoObject()) == NULL) { hr = DISP_E_MEMBERNOTFOUND; } diff --git a/tests/issues.targets b/tests/issues.targets index 927192148d..308f10ecd5 100644 --- a/tests/issues.targets +++ b/tests/issues.targets @@ -324,6 +324,9 @@ <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NETClients/Primitives/NETClientPrimitives/*"> <Issue>20682</Issue> </ExcludeList> + <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NETClients/Primitives/NETClientPrimitivesInALC/*"> + <Issue>20682</Issue> + </ExcludeList> <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NativeClients/Primitives/*"> <Issue>20682</Issue> </ExcludeList> diff --git a/tests/src/Common/CoreCLRTestLibrary/Utilities.cs b/tests/src/Common/CoreCLRTestLibrary/Utilities.cs index d1ef171349..4a27948a86 100644 --- a/tests/src/Common/CoreCLRTestLibrary/Utilities.cs +++ b/tests/src/Common/CoreCLRTestLibrary/Utilities.cs @@ -10,6 +10,7 @@ using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Loader; using System.Security; using System.Text; using System.Threading; @@ -303,5 +304,64 @@ namespace TestLibrary public static IntPtr HKEY_LOCAL_MACHINE => new IntPtr(unchecked((int)0x80000002)); } + + class TestAssemblyLoadContext : AssemblyLoadContext + { + public TestAssemblyLoadContext() : base(isCollectible: true) + { + + } + + protected override Assembly Load(AssemblyName assemblyName) + { + return null; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int ExecuteAndUnloadInternal(string assemblyPath, string[] args, Action<AssemblyLoadContext> unloadingCallback, out WeakReference alcWeakRef) + { + TestAssemblyLoadContext alc = new TestAssemblyLoadContext(); + if (unloadingCallback != null) + { + alc.Unloading += unloadingCallback; + } + alcWeakRef = new WeakReference(alc); + + Assembly a = alc.LoadFromAssemblyPath(assemblyPath); + + object[] argsObjArray = (a.EntryPoint.GetParameters().Length != 0) ? new object[] { args } : null; + object res = a.EntryPoint.Invoke(null, argsObjArray); + + alc.Unload(); + + return (a.EntryPoint.ReturnType == typeof(void)) ? Environment.ExitCode : Convert.ToInt32(res); + } + + public static int ExecuteAndUnload(string assemblyPath, string[] args, Action<AssemblyLoadContext> unloadingCallback = null) + { + WeakReference alcWeakRef; + int exitCode; + + exitCode = ExecuteAndUnloadInternal(assemblyPath, args, unloadingCallback, out alcWeakRef); + + for (int i = 0; i < 8 && alcWeakRef.IsAlive; i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + if (alcWeakRef.IsAlive) + { + exitCode += 100; + Console.WriteLine("Unload failed"); + } + else + { + Console.WriteLine("Unload succeeded"); + } + + return exitCode; + } } } diff --git a/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitivesInALC.csproj b/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitivesInALC.csproj new file mode 100644 index 0000000000..48409316a0 --- /dev/null +++ b/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitivesInALC.csproj @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <AssemblyName>NETClientPrimitivesInALC</AssemblyName> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{85C57688-DA98-4DE3-AC9B-526E4747434C}</ProjectGuid> + <OutputType>Exe</OutputType> + <ProjectTypeGuids>{209912F9-0DA1-4184-9CC1-8D583BAF4A28};{87799F5D-CEBD-499D-BDBA-B2C6105CD766}</ProjectTypeGuids> + <ApplicationManifest>App.manifest</ApplicationManifest> + + <!-- Blocked on ILAsm supporting embedding resources. See https://github.com/dotnet/coreclr/issues/20819 --> + <IlrtTestKind>BuildOnly</IlrtTestKind> + + <!-- Blocked on CrossGen.exe supporting embedding resources. See https://github.com/dotnet/coreclr/issues/21006 --> + <CrossGenTest>false</CrossGenTest> + + <!-- Test unsupported outside of windows --> + <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows> + <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild> + </PropertyGroup> + <!-- Default configurations to help VS understand the configurations --> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + </PropertyGroup> + <ItemGroup> + <Compile Include="TestInALC.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="../../NativeServer/CMakeLists.txt" /> + <ProjectReference Include="../../../../Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" /> + <ProjectReference Include="NetClientPrimitives.csproj" /> + </ItemGroup> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> +</Project> diff --git a/tests/src/Interop/COM/NETClients/Primitives/TestInALC.cs b/tests/src/Interop/COM/NETClients/Primitives/TestInALC.cs new file mode 100644 index 0000000000..eb5e9dd240 --- /dev/null +++ b/tests/src/Interop/COM/NETClients/Primitives/TestInALC.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.IO; +using System.Reflection; + +namespace TestInALC +{ + class Test + { + static int Main(string[] args) + { + string currentAssemblyDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); + string testAssemblyFullPath = Path.Combine(currentAssemblyDirectory, "NETClientPrimitives.exe"); + return TestLibrary.Utilities.ExecuteAndUnload(testAssemblyFullPath, args); + } + } +} diff --git a/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTest.csproj b/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTest.csproj index ddcd2c3e5b..855cb065de 100644 --- a/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTest.csproj +++ b/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTest.csproj @@ -28,7 +28,7 @@ </CodeAnalysisDependentAssemblyPaths> </ItemGroup> <ItemGroup> - <Compile Include="*.cs" /> + <Compile Include="IUnknownTest.cs" /> </ItemGroup> <ItemGroup> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> diff --git a/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTestInALC.csproj b/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTestInALC.csproj new file mode 100644 index 0000000000..26824ed271 --- /dev/null +++ b/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTestInALC.csproj @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <AssemblyName>IUnknownTestInALC</AssemblyName> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid> + <OutputType>Exe</OutputType> + <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> + + <DefineConstants>$(DefineConstants);STATIC</DefineConstants> + + <!-- Test unsupported outside of windows --> + <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows> + <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild> + </PropertyGroup> + <!-- Default configurations to help VS understand the configurations --> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + </PropertyGroup> + <ItemGroup> + <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> + <Visible>False</Visible> + </CodeAnalysisDependentAssemblyPaths> + </ItemGroup> + <ItemGroup> + <Compile Include="TestInALC.cs" /> + </ItemGroup> + <ItemGroup> + <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\Common\CoreCLRTestLibrary\CoreCLRTestLibrary.csproj"> + <Project>{c8c0dc74-fac4-45b1-81fe-70c4808366e0}</Project> + <Name>CoreCLRTestLibrary</Name> + </ProjectReference> + <ProjectReference Include="IUnknownTest.csproj" /> + <ProjectReference Include="CMakeLists.txt" /> + </ItemGroup> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> +</Project> diff --git a/tests/src/Interop/MarshalAPI/IUnknown/TestInALC.cs b/tests/src/Interop/MarshalAPI/IUnknown/TestInALC.cs new file mode 100644 index 0000000000..c14766a08c --- /dev/null +++ b/tests/src/Interop/MarshalAPI/IUnknown/TestInALC.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.IO; +using System.Reflection; + +namespace TestInALC +{ + class Test + { + static int Main(string[] args) + { + string currentAssemblyDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); + string testAssemblyFullPath = Path.Combine(currentAssemblyDirectory, "IUnknownTest.exe"); + return TestLibrary.Utilities.ExecuteAndUnload(testAssemblyFullPath, args); + } + } +} diff --git a/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/DefaultTestInALC.csproj b/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/DefaultTestInALC.csproj new file mode 100644 index 0000000000..f197d1aab0 --- /dev/null +++ b/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/DefaultTestInALC.csproj @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <AssemblyName>DefaultTestInALC</AssemblyName> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{43531C46-AFE2-4254-93C6-7F17E30D750C}</ProjectGuid> + <OutputType>Exe</OutputType> + <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\</SolutionDir> + <DefineConstants>$(DefineConstants);STATIC</DefineConstants> + <!-- Test unsupported outside of windows --> + <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows> + <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild> + </PropertyGroup> + <!-- Default configurations to help VS understand the configurations --> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup> + <ItemGroup> + <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> + <Visible>False</Visible> + </CodeAnalysisDependentAssemblyPaths> + </ItemGroup> + <ItemGroup> + <Compile Include="TestInALC.cs" /> + </ItemGroup> + <ItemGroup> + <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\CMakeLists.txt" /> + <ProjectReference Include="DefaultTest.csproj" /> + </ItemGroup> + <Import Project="../../../Interop.settings.targets" /> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> +</Project> diff --git a/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/TestInALC.cs b/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/TestInALC.cs new file mode 100644 index 0000000000..265d70fa32 --- /dev/null +++ b/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/TestInALC.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.IO; +using System.Reflection; + +namespace TestInALC +{ + class Test + { + static int Main(string[] args) + { + string currentAssemblyDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); + string testAssemblyFullPath = Path.Combine(currentAssemblyDirectory, "DefaultTest.exe"); + return TestLibrary.Utilities.ExecuteAndUnload(testAssemblyFullPath, args); + } + } +} |