Alter CCW wrapping semantics (dotnet/coreclr#23709)
authorAaron Robinson <arobins@microsoft.com>
Tue, 9 Apr 2019 22:07:34 +0000 (15:07 -0700)
committerGitHub <noreply@github.com>
Tue, 9 Apr 2019 22:07:34 +0000 (15:07 -0700)
* Update CCW semantics to not unwrap when a managed COM server was activated
  from a managed COM client. This is a functional change from .NET Framework.

* Add support for CoreShim to "attach" to the existing CLR instance when
 running from a CoreRun scenario.

* Add testing for NET COM client activating a NET COM server

Commit migrated from https://github.com/dotnet/coreclr/commit/a6b0eef9d4a61e3ef5c3879a5016931f8ca0cf99

22 files changed:
src/coreclr/src/coreclr/hosts/corerun/corerun.cpp
src/coreclr/src/coreclr/hosts/coreshim/CoreShim.cpp
src/coreclr/src/coreclr/hosts/coreshim/CoreShim.h
src/coreclr/src/vm/comcallablewrapper.cpp
src/coreclr/src/vm/comcallablewrapper.h
src/coreclr/src/vm/interopconverter.cpp
src/coreclr/src/vm/runtimecallablewrapper.cpp
src/coreclr/tests/issues.targets
src/coreclr/tests/src/Interop/COM/NETClients/Aggregation/NETClientAggregation.csproj
src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/App.manifest [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/ConsumeNETServer.csproj [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/CoreShim.X.manifest [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/Program.cs [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/NETClients/Events/NETClientEvents.csproj
src/coreclr/tests/src/Interop/COM/NETClients/IDispatch/NETClientIDispatch.csproj
src/coreclr/tests/src/Interop/COM/NETClients/Licensing/NETClientLicense.csproj
src/coreclr/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitives.csproj
src/coreclr/tests/src/Interop/COM/NETServer/ConsumeNETServerTesting.cs [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/NETServer/NETServer.csproj
src/coreclr/tests/src/Interop/COM/ServerContracts/Server.CoClasses.cs [moved from src/coreclr/tests/src/Interop/COM/ServerContracts/NativeServers.cs with 86% similarity]
src/coreclr/tests/src/Interop/COM/ServerContracts/Server.Contracts.cs
src/coreclr/tests/src/Interop/COM/ServerContracts/ServerGuids.cs

index 42aa300..e1864c8 100644 (file)
@@ -423,6 +423,41 @@ private:
     ULONG_PTR _actCookie;
 };
 
+class ClrInstanceDetails
+{
+    static void * _currentClrInstance;
+    static unsigned int _currentAppDomainId;
+
+public: // static
+    static HRESULT GetDetails(void **clrInstance, unsigned int *appDomainId)
+    {
+        *clrInstance = _currentClrInstance;
+        *appDomainId = _currentAppDomainId;
+        return S_OK;
+    }
+
+public:
+    ClrInstanceDetails(void *clrInstance, unsigned int appDomainId)
+    {
+        _currentClrInstance = clrInstance;
+        _currentAppDomainId = appDomainId;
+    }
+
+    ~ClrInstanceDetails()
+    {
+        _currentClrInstance = nullptr;
+        _currentAppDomainId = 0;
+    }
+};
+
+void * ClrInstanceDetails::_currentClrInstance;
+unsigned int ClrInstanceDetails::_currentAppDomainId;
+
+extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void **clrInstance, unsigned int *appDomainId)
+{
+    return ClrInstanceDetails::GetDetails(clrInstance, appDomainId);
+}
+
 bool TryLoadHostPolicy(StackSString& hostPolicyPath)
 {
     const WCHAR *hostpolicyName = W("hostpolicy.dll");
@@ -666,6 +701,7 @@ bool TryRun(const int argc, const wchar_t* argv[], Logger &log, const bool verbo
 
     {
         ActivationContext cxt{ log, managedAssemblyFullName.GetUnicode() };
+        ClrInstanceDetails current{ host, domainId };
 
         hr = host->ExecuteAssembly(domainId, managedAssemblyFullName, argc - 1, (argc - 1) ? &(argv[1]) : NULL, &exitCode);
         if (FAILED(hr))
index 7a5c3a1..238e40f 100644 (file)
@@ -334,6 +334,7 @@ HRESULT coreclr::CreateTpaList(_Inout_ std::string &tpaList, _In_opt_z_ const WC
 
 coreclr::coreclr(_Inout_ AutoModule hmod)
     : _hmod{ std::move(hmod) }
+    , _attached{ false }
     , _clrInst{ nullptr }
     , _appDomainId{ std::numeric_limits<uint32_t>::max() }
 {
@@ -349,7 +350,7 @@ coreclr::coreclr(_Inout_ AutoModule hmod)
 
 coreclr::~coreclr()
 {
-    if (_clrInst != nullptr)
+    if (_clrInst != nullptr && !_attached)
     {
         HRESULT hr = _shutdown(_clrInst, _appDomainId);
         assert(SUCCEEDED(hr));
@@ -370,6 +371,21 @@ HRESULT coreclr::Initialize(
         appDomainName = "CoreShim";
 
     HRESULT hr;
+
+    // Check if this is hosted scenario - launched via CoreRun.exe
+    HMODULE mod = ::GetModuleHandleW(W("CoreRun.exe"));
+    if (mod != NULL)
+    {
+        using GetCurrentClrDetailsFunc = HRESULT(*)(void **clrInstance, unsigned int *appDomainId);
+        auto getCurrentClrDetails = (GetCurrentClrDetailsFunc)::GetProcAddress(mod, "GetCurrentClrDetails");
+        RETURN_IF_FAILED(getCurrentClrDetails(&_clrInst, &_appDomainId));
+        if (_clrInst != nullptr)
+        {
+            _attached = true;
+            return S_OK;
+        }
+    }
+
     try
     {
         const std::wstring exePathW = GetExePath();
index d4c8b0a..5875b45 100644 (file)
@@ -174,6 +174,7 @@ public:
 private:
     AutoModule _hmod;
 
+    bool _attached;
     void *_clrInst;
     uint32_t _appDomainId;
 
index 713d7c1..3eb9851 100644 (file)
@@ -918,7 +918,7 @@ void SimpleComCallWrapper::BuildRefCountLogMessage(LPCWSTR wszOperation, StackSS
     LPCUTF8 pszNamespace;
     if (SUCCEEDED(m_pMT->GetMDImport()->GetNameOfTypeDef(m_pMT->GetCl(), &pszClassName, &pszNamespace)))
     {
-        OBJECTHANDLE handle = GetMainWrapper()->GetRawObjectHandle();
+        OBJECTHANDLE handle = GetMainWrapper()->GetObjectHandle();
         _UNCHECKED_OBJECTREF obj = NULL;
 
         // Force retriving the handle without using OBJECTREF and under cooperative mode
@@ -2016,12 +2016,27 @@ void SimpleComCallWrapper::EnumConnectionPoints(IEnumConnectionPoints **ppEnumCP
 //  
 //--------------------------------------------------------------------------
 
-
 //--------------------------------------------------------------------------
-// void ComCallWrapper::MarkHandleWeak()
-//  mark the wrapper as holding a weak handle to the object
+// Check if the wrapper has been deactivated
 //--------------------------------------------------------------------------
+BOOL ComCallWrapper::IsHandleWeak()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    SimpleComCallWrapper* simpleWrap = GetSimpleWrapper();
+    _ASSERTE(simpleWrap);
+    return simpleWrap->IsHandleWeak();
+}
 
+//--------------------------------------------------------------------------
+// Mark the wrapper as holding a weak handle to the object
+//--------------------------------------------------------------------------
 void ComCallWrapper::MarkHandleWeak()
 {
     CONTRACTL
@@ -2032,18 +2047,51 @@ void ComCallWrapper::MarkHandleWeak()
     }
     CONTRACTL_END;
 
-    SyncBlock* pSyncBlock = GetSyncBlock();
-    _ASSERTE(pSyncBlock);
+    SimpleComCallWrapper* simpleWrap = GetSimpleWrapper();
+    _ASSERTE(simpleWrap);
+    simpleWrap->MarkHandleWeak();
+}
+
+//--------------------------------------------------------------------------
+// Mark the wrapper as not having a weak handle
+//--------------------------------------------------------------------------
+void ComCallWrapper::ResetHandleStrength()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
 
-    GetSimpleWrapper()->MarkHandleWeak();
+    SimpleComCallWrapper* simpleWrap = GetSimpleWrapper();
+    _ASSERTE(simpleWrap);
+    simpleWrap->ResetHandleStrength();
 }
 
 //--------------------------------------------------------------------------
-// void ComCallWrapper::ResetHandleStrength()
-//  mark the wrapper as not having a weak handle
+// Check if the wrapper was activated via COM
 //--------------------------------------------------------------------------
+BOOL ComCallWrapper::IsComActivated()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
 
-void ComCallWrapper::ResetHandleStrength()
+    SimpleComCallWrapper* simpleWrap = GetSimpleWrapper();
+    _ASSERTE(simpleWrap);
+    return simpleWrap->IsComActivated();
+}
+
+//--------------------------------------------------------------------------
+// Mark the wrapper as being created via COM activation
+//--------------------------------------------------------------------------
+VOID ComCallWrapper::MarkComActivated()
 {
     CONTRACTL
     {
@@ -2052,11 +2100,10 @@ void ComCallWrapper::ResetHandleStrength()
         MODE_ANY;
     }
     CONTRACTL_END;
-    
-    SyncBlock* pSyncBlock = GetSyncBlock();
-    _ASSERTE(pSyncBlock);
 
-    GetSimpleWrapper()->ResetHandleStrength();
+    SimpleComCallWrapper* simpleWrap = GetSimpleWrapper();
+    _ASSERTE(simpleWrap);
+    simpleWrap->MarkComActivated();
 }
 
 //--------------------------------------------------------------------------
index 8ec126f..5773a9d 100644 (file)
@@ -997,27 +997,20 @@ private:
     };
     
 public:
-    VOID ResetHandleStrength();
+    BOOL IsHandleWeak();
     VOID MarkHandleWeak();
+    VOID ResetHandleStrength();
 
-    BOOL IsHandleWeak();
+    BOOL IsComActivated();
+    VOID MarkComActivated();
 
-    OBJECTHANDLE GetObjectHandle();
-    OBJECTHANDLE GetRawObjectHandle() { LIMITED_METHOD_CONTRACT; return m_ppThis; } // no NULL check
+    OBJECTHANDLE GetObjectHandle() { LIMITED_METHOD_CONTRACT; return m_ppThis; }
 
-protected:
     // don't instantiate this class directly
-    ComCallWrapper()
-    {
-        LIMITED_METHOD_CONTRACT;
-    }
-    ~ComCallWrapper()
-    {
-        LIMITED_METHOD_CONTRACT;
-    }
-    
-    void Init();
+    ComCallWrapper() = delete;
+    ~ComCallWrapper() = delete;
 
+protected:
 #ifndef DACCESS_COMPILE
     inline static void SetNext(ComCallWrapper* pWrap, ComCallWrapper* pNextWrapper)
     {
@@ -1438,7 +1431,7 @@ private:
         enum_IsAggregated                      = 0x1,
         enum_IsExtendsCom                      = 0x2,
         enum_IsHandleWeak                      = 0x4,
-        // unused                              = 0x8,
+        enum_IsComActivated                    = 0x8,
         // unused                              = 0x10,
         enum_IsPegged                          = 0x80,
         // unused                              = 0x100,
@@ -1622,6 +1615,18 @@ public:
         return m_flags & enum_IsExtendsCom;
     }
 
+    BOOL IsComActivated()
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_flags & enum_IsComActivated;
+    }
+
+    void MarkComActivated()
+    {
+        LIMITED_METHOD_CONTRACT;
+        FastInterlockOr((ULONG*)&m_flags, enum_IsComActivated);
+    }
+
     inline BOOL IsPegged()
     {
         LIMITED_METHOD_DAC_CONTRACT;
@@ -2060,20 +2065,6 @@ private:
     LONGLONG                        m_llRefCount;
  };
 
-inline OBJECTHANDLE ComCallWrapper::GetObjectHandle()
-{
-    CONTRACT (OBJECTHANDLE)
-    {
-        WRAPPER(THROWS);
-        WRAPPER(GC_TRIGGERS);
-        MODE_COOPERATIVE;
-        POSTCONDITION(CheckPointer(RETVAL));
-    }
-    CONTRACT_END;
-    
-    RETURN m_ppThis;
-}
-
 //--------------------------------------------------------------------------------
 // ComCallWrapper* ComCallWrapper::InlineGetWrapper(OBJECTREF* ppObj, ComCallWrapperTemplate *pTemplate)
 // returns the wrapper for the object, if not yet created, creates one
@@ -2275,8 +2266,6 @@ inline ULONG ComCallWrapper::GetJupiterRefCount()
     return m_pSimpleWrapper->GetJupiterRefCount();
 }
 
-
-
 inline PTR_ComCallWrapper ComCallWrapper::GetWrapperFromIP(PTR_IUnknown pUnk)
 {
     CONTRACT (PTR_ComCallWrapper)
@@ -2340,27 +2329,6 @@ inline PTR_ComCallWrapperTemplate ComCallWrapper::GetComCallWrapperTemplate()
     return GetSimpleWrapper()->GetComCallWrapperTemplate();
 }
 
-//--------------------------------------------------------------------------
-//  BOOL ComCallWrapper::BOOL IsHandleWeak()
-// check if the wrapper has been deactivated
-// Moved here to make DAC build happy and hopefully get it inlined
-//--------------------------------------------------------------------------
-inline BOOL ComCallWrapper::IsHandleWeak()
-{
-    CONTRACTL
-    {
-        NOTHROW;
-        GC_NOTRIGGER;
-        MODE_ANY;
-    }
-    CONTRACTL_END;
-    
-    SimpleComCallWrapper* pSimpleWrap = GetSimpleWrapper();
-    _ASSERTE(pSimpleWrap);
-    
-    return pSimpleWrap->IsHandleWeak();
-}
-
 inline BOOL ComCallWrapper::IsWrapperActive()
 {
     CONTRACTL
@@ -2384,12 +2352,12 @@ inline BOOL ComCallWrapper::IsWrapperActive()
         
     BOOL bHasStrongCOMRefCount = ((cbRef > 0) || bHasJupiterStrongRefCount);
 
-    BOOL bIsWrapperActive = (bHasStrongCOMRefCount && !IsHandleWeak());
+    BOOL bIsWrapperActive = (bHasStrongCOMRefCount && !m_pSimpleWrapper->IsHandleWeak());
 
     LOG((LF_INTEROP, LL_INFO1000, 
          "CCW 0x%p: cbRef = 0x%x, cbJupiterRef = 0x%x, IsPegged = %d, GlobalPegging = %d, IsHandleWeak = %d\n", 
          this, 
-         cbRef, cbJupiterRef, IsPegged(), RCWWalker::IsGlobalPeggingOn(), IsHandleWeak()));
+         cbRef, cbJupiterRef, m_pSimpleWrapper->IsPegged(), RCWWalker::IsGlobalPeggingOn(), m_pSimpleWrapper->IsHandleWeak()));
     LOG((LF_INTEROP, LL_INFO1000, "CCW 0x%p: IsWrapperActive returned %d\n", this, bIsWrapperActive));
     
     return bIsWrapperActive;    
index 70b1113..d8bb08d 100644 (file)
@@ -414,7 +414,7 @@ IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, REFIID iid, bool throwIfNoComI
 // GetObjectRefFromComIP
 // pUnk : input IUnknown
 // pMTClass : specifies the type of instance to be returned
-// NOTE:**  As per COM Rules, the IUnknown passed is shouldn't be AddRef'ed
+// NOTE:**  As per COM Rules, the IUnknown passed in shouldn't be AddRef'ed
 //+----------------------------------------------------------------------------
 void GetObjectRefFromComIP(OBJECTREF* pObjOut, IUnknown **ppUnk, MethodTable *pMTClass, MethodTable *pItfMT, DWORD dwFlags)
 {
@@ -456,24 +456,27 @@ void GetObjectRefFromComIP(OBJECTREF* pObjOut, IUnknown **ppUnk, MethodTable *pM
     if (pUnk != NULL)
     {
         // get CCW for IUnknown
-        ComCallWrapper* pWrap = GetCCWFromIUnknown(pUnk);
-        if (pWrap == NULL)
+        ComCallWrapper *ccw = GetCCWFromIUnknown(pUnk);
+        if (ccw == NULL)
         {
             // could be aggregated scenario
             HRESULT hr = SafeQueryInterface(pUnk, IID_IUnknown, &pOuter);
             LogInteropQI(pUnk, IID_IUnknown, hr, "GetObjectRefFromComIP: QI for Outer");
             IfFailThrow(hr);
-                
+
             // store the outer in the auto pointer
             pAutoOuterUnk = pOuter; 
-            pWrap = GetCCWFromIUnknown(pOuter);
+            ccw = GetCCWFromIUnknown(pOuter);
         }
 
-        if (pWrap != NULL)
-        {   // our tear-off
-            _ASSERTE(pWrap != NULL);
-            AppDomain* pCurrDomain = pThread->GetDomain();
-            *pObjOut = pWrap->GetObjectRef();
+        // If the CCW was activated via COM, do not unwrap it.
+        // Unwrapping a CCW would deliver the underlying OBJECTREF,
+        // but when a managed class is activated via COM it should
+        // remain a COM object and adhere to COM rules.
+        if (ccw != NULL
+            && !ccw->IsComActivated())
+        {
+            *pObjOut = ccw->GetObjectRef();
         }
 
         if (*pObjOut != NULL)
index 540536e..26c2a5c 100644 (file)
@@ -242,6 +242,12 @@ IUnknown *ComClassFactory::CreateInstanceFromClassFactory(IClassFactory *pClassF
             ThrowHRMsg(hr, IDS_EE_CREATEINSTANCE_LIC_FAILED);
     }
 
+    // If the activated COM class has a CCW, mark the
+    // CCW as being activated via COM.
+    ComCallWrapper *ccw = GetCCWFromIUnknown(pUnk);
+    if (ccw != NULL)
+        ccw->MarkComActivated();
+
     pUnk.SuppressRelease();
     RETURN pUnk;
 }
index 79e5598..c540e01 100644 (file)
         <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NETClients/Events/NETClientEvents/*">
             <Issue>22784</Issue>
         </ExcludeList>
+        <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NETClients/ConsumeNETServer/ConsumeNETServer/*">
+            <Issue>20682</Issue>
+        </ExcludeList>
     </ItemGroup>
 
     <!-- Windows arm64 specific excludes -->
index 182ba92..63df657 100644 (file)
@@ -30,7 +30,7 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="Program.cs" />
-    <Compile Include="../../ServerContracts/NativeServers.cs" />
+    <Compile Include="../../ServerContracts/Server.CoClasses.cs" />
     <Compile Include="../../ServerContracts/Server.Contracts.cs" />
     <Compile Include="../../ServerContracts/ServerGuids.cs" />
   </ItemGroup>
diff --git a/src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/App.manifest b/src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/App.manifest
new file mode 100644 (file)
index 0000000..58fc0a2
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity
+    type="win32" 
+    name="ConsumeNETServer"
+    version="1.0.0.0" />
+
+  <dependency>
+    <dependentAssembly>
+      <!-- RegFree COM to activate Managed Server -->
+      <assemblyIdentity
+          type="win32"
+          name="CoreShim.X"
+          version="1.0.0.0"/>
+    </dependentAssembly>
+  </dependency>
+
+</assembly>
diff --git a/src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/ConsumeNETServer.csproj b/src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/ConsumeNETServer.csproj
new file mode 100644 (file)
index 0000000..9202a97
--- /dev/null
@@ -0,0 +1,54 @@
+<?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>ConsumeNETServer</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{4BDB75BD-30D8-4603-98DB-C6CFDC5F6F0E}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{209912F9-0DA1-4184-9CC1-8D583BAF4A28};{87799F5D-CEBD-499D-BDBA-B2C6105CD766}</ProjectTypeGuids>
+    <ApplicationManifest>App.manifest</ApplicationManifest>
+    <CLRTestScriptLocalCoreShim>true</CLRTestScriptLocalCoreShim>
+    <RequiresMockHostPolicy>true</RequiresMockHostPolicy>
+
+    <!-- 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>
+    <!-- This test would require the runincontext.exe to include App.manifest describing the COM interfaces -->
+    <UnloadabilityIncompatible>true</UnloadabilityIncompatible>
+
+    <!--  Suppress warning about conflicting type names. This occurs because of the reference to NETServer.
+          The reference is only to ensure the project is built and properly copied. The test itself uses
+          COM to activate the server rather than typical class activation via 'new' -->
+    <NoWarn>$(NoWarn),0436</NoWarn>
+  </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="Program.cs" />
+    <Compile Include="../../ServerContracts/Server.CoClasses.cs" />
+    <Compile Include="../../ServerContracts/Server.Contracts.cs" />
+    <Compile Include="../../ServerContracts/ServerGuids.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="../../NETServer/NETServer.csproj" />
+    <ProjectReference Include="../../../../Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="CoreShim.X.manifest">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/CoreShim.X.manifest b/src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/CoreShim.X.manifest
new file mode 100644 (file)
index 0000000..abb39fb
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+
+<assemblyIdentity
+  type="win32"
+  name="CoreShim.X"
+  version="1.0.0.0" />
+
+<file name="CoreShim.dll">
+  <!-- ConsumeNETServerTesting -->
+  <comClass
+    clsid="{DE4ACF53-5957-4D31-8BE2-EA6C80683246}"
+    threadingModel="Both" />
+</file>
+
+</assembly>
diff --git a/src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/Program.cs b/src/coreclr/tests/src/Interop/COM/NETClients/ConsumeNETServer/Program.cs
new file mode 100644 (file)
index 0000000..35ce280
--- /dev/null
@@ -0,0 +1,122 @@
+// 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.
+
+namespace NetClient
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Runtime.CompilerServices;
+    using System.Runtime.InteropServices;
+
+    using TestLibrary;
+    using Server.Contract;
+
+    using CoClass = Server.Contract.Servers;
+
+    class Program
+    {
+        static void Validate_Activation()
+        {
+            Console.WriteLine($"{nameof(Validate_Activation)}...");
+
+            var test = new CoClass.ConsumeNETServerTesting();
+            test.ReleaseResources();
+
+            // The CoClass should be the activated type, _not_ the activation interface.
+            Assert.AreEqual(test.GetType(), typeof(CoClass.ConsumeNETServerTestingClass));
+        }
+
+        static void Validate_CCW_Wasnt_Unwrapped()
+        {
+            Console.WriteLine($"{nameof(Validate_CCW_Wasnt_Unwrapped)}...");
+
+            var test = new CoClass.ConsumeNETServerTesting();
+            test.ReleaseResources();
+
+            // The CoClass should be the activated type, _not_ the implementation class.
+            // This indicates the real implementation class is wrapped in its CCW and exposed
+            // to the runtime as an RCW.
+            Assert.AreNotEqual(test.GetType(), typeof(ConsumeNETServerTesting));
+        }
+
+        static void Validate_Client_CCW_RCW()
+        {
+            Console.WriteLine($"{nameof(Validate_Client_CCW_RCW)}...");
+
+            IntPtr ccw = IntPtr.Zero;
+
+            // Validate the client side view is consistent
+            var test = new CoClass.ConsumeNETServerTesting();
+            try
+            {
+                ccw = test.GetCCW();
+                object rcw = Marshal.GetObjectForIUnknown(ccw);
+                object inst = test.GetRCW();
+                Assert.AreEqual(rcw, inst);
+            }
+            finally
+            {
+                if (ccw != IntPtr.Zero)
+                {
+                    Marshal.Release(ccw);
+                }
+
+                test.ReleaseResources();
+            }
+        }
+
+        static void Validate_Server_CCW_RCW()
+        {
+            Console.WriteLine($"{nameof(Validate_Server_CCW_RCW)}...");
+
+            // Validate the server side view is consistent
+            var test = new CoClass.ConsumeNETServerTesting();
+            try
+            {
+                Assert.IsTrue(test.EqualByCCW(test));
+                Assert.IsTrue(test.NotEqualByRCW(test));
+            }
+            finally
+            {
+                test.ReleaseResources();
+            }
+        }
+
+        static int Main(string[] doNotUse)
+        {
+            // RegFree COM is not supported on Windows Nano
+            if (Utilities.IsWindowsNanoServer)
+            {
+                return 100;
+            }
+
+            // Initialize CoreShim and hostpolicymock
+            HostPolicyMock.Initialize(Environment.CurrentDirectory, null);
+            Environment.SetEnvironmentVariable("CORESHIM_COMACT_ASSEMBLYNAME", "NETServer");
+            Environment.SetEnvironmentVariable("CORESHIM_COMACT_TYPENAME", "ConsumeNETServerTesting");
+
+            try
+            {
+                using (HostPolicyMock.Mock_corehost_resolve_component_dependencies(
+                    0,
+                    string.Empty,
+                    string.Empty,
+                    string.Empty))
+                {
+                    Validate_Activation();
+                    Validate_CCW_Wasnt_Unwrapped();
+                    Validate_Client_CCW_RCW();
+                    Validate_Server_CCW_RCW();
+                }
+            }
+            catch (Exception e)
+            {
+                Console.WriteLine($"Test Failure: {e}");
+                return 101;
+            }
+
+            return 100;
+        }
+    }
+}
index fccbee9..f346a02 100644 (file)
@@ -30,7 +30,7 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="Program.cs" />
-    <Compile Include="../../ServerContracts/NativeServers.cs" />
+    <Compile Include="../../ServerContracts/Server.CoClasses.cs" />
     <Compile Include="../../ServerContracts/Server.Contracts.cs" />
     <Compile Include="../../ServerContracts/Server.Events.cs" />
     <Compile Include="../../ServerContracts/ServerGuids.cs" />
index 4fd95f6..a99416b 100644 (file)
@@ -30,7 +30,7 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="Program.cs" />
-    <Compile Include="../../ServerContracts/NativeServers.cs" />
+    <Compile Include="../../ServerContracts/Server.CoClasses.cs" />
     <Compile Include="../../ServerContracts/Server.Contracts.cs" />
     <Compile Include="../../ServerContracts/ServerGuids.cs" />
   </ItemGroup>
index 5215422..a46a2a2 100644 (file)
@@ -30,7 +30,7 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="Program.cs" />
-    <Compile Include="../../ServerContracts/NativeServers.cs" />
+    <Compile Include="../../ServerContracts/Server.CoClasses.cs" />
     <Compile Include="../../ServerContracts/Server.Contracts.cs" />
     <Compile Include="../../ServerContracts/ServerGuids.cs" />
   </ItemGroup>
index 81c06a3..c1f3857 100644 (file)
@@ -35,7 +35,7 @@
     <Compile Include="NumericTests.cs" />
     <Compile Include="StringTests.cs" />
     <Compile Include="ColorTests.cs" />
-    <Compile Include="../../ServerContracts/NativeServers.cs" />
+    <Compile Include="../../ServerContracts/Server.CoClasses.cs" />
     <Compile Include="../../ServerContracts/Server.Contracts.cs" />
     <Compile Include="../../ServerContracts/ServerGuids.cs" />
   </ItemGroup>
diff --git a/src/coreclr/tests/src/Interop/COM/NETServer/ConsumeNETServerTesting.cs b/src/coreclr/tests/src/Interop/COM/NETServer/ConsumeNETServerTesting.cs
new file mode 100644 (file)
index 0000000..d8dba73
--- /dev/null
@@ -0,0 +1,51 @@
+// 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.Runtime.InteropServices;
+
+[ComVisible(true)]
+[Guid(Server.Contract.Guids.ConsumeNETServerTesting)]
+public class ConsumeNETServerTesting : Server.Contract.IConsumeNETServer
+{
+    private IntPtr _ccw;
+    private object _rcwUnwrapped;
+
+    public ConsumeNETServerTesting()
+    {
+        _ccw = Marshal.GetIUnknownForObject(this);
+        _rcwUnwrapped = Marshal.GetObjectForIUnknown(_ccw);
+    }
+
+    public IntPtr GetCCW()
+    {
+        return _ccw;
+    }
+
+    public object GetRCW()
+    {
+        return _rcwUnwrapped;
+    }
+
+    public void ReleaseResources()
+    {
+        Marshal.Release(_ccw);
+        _ccw = IntPtr.Zero;
+        _rcwUnwrapped = null;
+    }
+
+    public bool EqualByCCW(object obj)
+    {
+        IntPtr ccwMaybe = Marshal.GetIUnknownForObject(obj);
+        bool areEqual = ccwMaybe == _ccw;
+        Marshal.Release(ccwMaybe);
+
+        return areEqual;
+    }
+
+    public bool NotEqualByRCW(object obj)
+    {
+        return _rcwUnwrapped != obj;
+    }
+}
index aa4e791..e820c0c 100644 (file)
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="ImportedTypes.cs" />
-    <Compile Include="NumericTesting.cs" />
-    <Compile Include="ArrayTesting.cs" />
-    <Compile Include="StringTesting.cs" />
-    <Compile Include="ErrorMarshalTesting.cs" />
-    <Compile Include="ColorTesting.cs" />
-    <Compile Include="LicenseTesting.cs" />
+    <Compile Include="*.cs" />
     <Compile Include="../ServerContracts/Server.Contracts.cs" />
     <Compile Include="../ServerContracts/ServerGuids.cs" />
   </ItemGroup>
@@ -183,6 +183,31 @@ namespace Server.Contract.Servers
     {
     }
 */
+
+    /// <summary>
+    /// Managed definition of CoClass
+    /// </summary>
+    /// <remarks>
+    /// This interface is used to test consumption of the NET server from a NET client only.
+    /// </remarks>
+    [ComImport]
+    [CoClass(typeof(ConsumeNETServerTestingClass))]
+    [Guid("CCBC1915-3252-4F6B-98AA-411CE6213D94")]
+    internal interface ConsumeNETServerTesting : Server.Contract.IConsumeNETServer
+    {
+    }
+
+    /// <summary>
+    /// Managed activation for CoClass
+    /// </summary>
+    /// <remarks>
+    /// This interface is used to test consumption of the NET server from a NET client only.
+    /// </remarks>
+    [ComImport]
+    [Guid(Server.Contract.Guids.ConsumeNETServerTesting)]
+    internal class ConsumeNETServerTestingClass
+    {
+    }
 }
 
 #pragma warning restore 618 // Must test deprecated features
index 7c198c7..f325184 100644 (file)
@@ -306,6 +306,22 @@ namespace Server.Contract
 
         void SetNextLicense([MarshalAs(UnmanagedType.LPWStr)] string lic);
     }
+
+    /// <remarks>
+    /// This interface is used to test consumption of the NET server from a NET client only.
+    /// </remarks>
+    [ComVisible(true)]
+    [Guid("CCBC1915-3252-4F6B-98AA-411CE6213D94")]
+    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+    public interface IConsumeNETServer
+    {
+        IntPtr GetCCW();
+        object GetRCW();
+        void ReleaseResources();
+
+        bool EqualByCCW(object obj);
+        bool NotEqualByRCW(object obj);
+    }
 }
 
 #pragma warning restore 618 // Must test deprecated features
index 98ed0ae..d03eacf 100644 (file)
@@ -19,5 +19,6 @@ namespace Server.Contract
         public const string ColorTesting = "C222F472-DA5A-4FC6-9321-92F4F7053A65";
         public const string LicenseTesting = "66DB7882-E2B0-471D-92C7-B2B52A0EA535";
         public const string DefaultInterfaceTesting = "FAEF42AE-C1A4-419F-A912-B768AC2679EA";
+        public const string ConsumeNETServerTesting = "DE4ACF53-5957-4D31-8BE2-EA6C80683246";
     }
 }