Enable COM interop for collectible classes (#20919)
authorJan Vorlicek <janvorli@microsoft.com>
Sat, 1 Dec 2018 09:31:35 +0000 (10:31 +0100)
committerGitHub <noreply@github.com>
Sat, 1 Dec 2018 09:31:35 +0000 (10:31 +0100)
* 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:
src/System.Private.CoreLib/Resources/Strings.resx
src/inc/CrstTypes.def
src/inc/crsttypes.h
src/vm/appdomain.cpp
src/vm/appdomain.hpp
src/vm/ceeload.cpp
src/vm/comcallablewrapper.cpp
src/vm/comcallablewrapper.h
src/vm/comtoclrcall.h
src/vm/dispatchinfo.cpp
src/vm/dispatchinfo.h
src/vm/interoputil.cpp
src/vm/loaderallocator.cpp
src/vm/loaderallocator.hpp
src/vm/methodtable.cpp
src/vm/runtimecallablewrapper.cpp
src/vm/stdinterfaces.cpp
tests/issues.targets
tests/src/Common/CoreCLRTestLibrary/Utilities.cs
tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitivesInALC.csproj [new file with mode: 0644]
tests/src/Interop/COM/NETClients/Primitives/TestInALC.cs [new file with mode: 0644]
tests/src/Interop/MarshalAPI/IUnknown/IUnknownTest.csproj
tests/src/Interop/MarshalAPI/IUnknown/IUnknownTestInALC.csproj [new file with mode: 0644]
tests/src/Interop/MarshalAPI/IUnknown/TestInALC.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/DefaultTestInALC.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/TestInALC.cs [new file with mode: 0644]

index 9fa2e10..e5de28b 100644 (file)
   <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>
index 180171e..bb996a7 100644 (file)
@@ -696,3 +696,6 @@ End
 Crst TieredCompilation
     AcquiredBefore ThreadpoolTimerQueue
 End
+
+Crst COMCallWrapper
+End
index 2335843..7c867f7 100644 (file)
@@ -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",
index 19999cd..1f97825 100644 (file)
@@ -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*)
index b326b26..4163c30 100644 (file)
@@ -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;
 
index 9ff186e..eb54b95 100644 (file)
@@ -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;
index 9b3d997..57d1ede 100644 (file)
@@ -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;
 
index 56ebc94..e58b4c8 100644 (file)
@@ -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;
index d2f3891..5425fc0 100644 (file)
@@ -402,7 +402,7 @@ public:
         CONTRACTL
         {
             NOTHROW;
-            GC_TRIGGERS;
+            GC_NOTRIGGER;
             MODE_ANY;
         }
         CONTRACTL_END;
index fe79ac6..1b4fbb4 100644 (file)
@@ -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();
         }
index 97b4ea7..f5f9b9b 100644 (file)
@@ -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);
index 8bb08e4..510e494 100644 (file)
@@ -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;
index 314f5cb..d51af51 100644 (file)
@@ -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
index f2d7290..a283721 100644 (file)
@@ -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;
index 5ed91d1..cf7ddcc 100644 (file)
@@ -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);
 }
 
 //==========================================================================================
index 0832b56..6280c2c 100644 (file)
@@ -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();
     }
     
index cfcfd43..b96681f 100644 (file)
@@ -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;
             }
index 9271921..308f10e 100644 (file)
         <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>
index d1ef171..4a27948 100644 (file)
@@ -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 (file)
index 0000000..4840931
--- /dev/null
@@ -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 (file)
index 0000000..eb5e9dd
--- /dev/null
@@ -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);
+        }
+    }
+}
index ddcd2c3..855cb06 100644 (file)
@@ -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 (file)
index 0000000..26824ed
--- /dev/null
@@ -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 (file)
index 0000000..c14766a
--- /dev/null
@@ -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 (file)
index 0000000..f197d1a
--- /dev/null
@@ -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 (file)
index 0000000..265d70f
--- /dev/null
@@ -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);
+        }
+    }
+}