diff options
author | Aaron Robinson <arobins@microsoft.com> | 2019-04-09 15:07:34 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-09 15:07:34 -0700 |
commit | a6b0eef9d4a61e3ef5c3879a5016931f8ca0cf99 (patch) | |
tree | fc4ab7c05721f8dcfb667cd522737f2c1cd8f12f | |
parent | 5608b4ff0f81b99a5d436dec1e23b393503a4e07 (diff) | |
download | coreclr-a6b0eef9d4a61e3ef5c3879a5016931f8ca0cf99.tar.gz coreclr-a6b0eef9d4a61e3ef5c3879a5016931f8ca0cf99.tar.bz2 coreclr-a6b0eef9d4a61e3ef5c3879a5016931f8ca0cf99.zip |
Alter CCW wrapping semantics (#23709)
* 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
22 files changed, 469 insertions, 92 deletions
diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index 42aa3009ea..e1864c81f7 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -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)) diff --git a/src/coreclr/hosts/coreshim/CoreShim.cpp b/src/coreclr/hosts/coreshim/CoreShim.cpp index 7a5c3a1d1c..238e40fb87 100644 --- a/src/coreclr/hosts/coreshim/CoreShim.cpp +++ b/src/coreclr/hosts/coreshim/CoreShim.cpp @@ -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(); diff --git a/src/coreclr/hosts/coreshim/CoreShim.h b/src/coreclr/hosts/coreshim/CoreShim.h index d4c8b0aa6b..5875b457bc 100644 --- a/src/coreclr/hosts/coreshim/CoreShim.h +++ b/src/coreclr/hosts/coreshim/CoreShim.h @@ -174,6 +174,7 @@ public: private: AutoModule _hmod; + bool _attached; void *_clrInst; uint32_t _appDomainId; diff --git a/src/vm/comcallablewrapper.cpp b/src/vm/comcallablewrapper.cpp index 713d7c17b5..3eb9851206 100644 --- a/src/vm/comcallablewrapper.cpp +++ b/src/vm/comcallablewrapper.cpp @@ -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(); } //-------------------------------------------------------------------------- diff --git a/src/vm/comcallablewrapper.h b/src/vm/comcallablewrapper.h index 8ec126fbfc..5773a9d8c8 100644 --- a/src/vm/comcallablewrapper.h +++ b/src/vm/comcallablewrapper.h @@ -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; diff --git a/src/vm/interopconverter.cpp b/src/vm/interopconverter.cpp index 70b1113cd3..d8bb08d53d 100644 --- a/src/vm/interopconverter.cpp +++ b/src/vm/interopconverter.cpp @@ -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) diff --git a/src/vm/runtimecallablewrapper.cpp b/src/vm/runtimecallablewrapper.cpp index 540536ef5f..26c2a5cd5d 100644 --- a/src/vm/runtimecallablewrapper.cpp +++ b/src/vm/runtimecallablewrapper.cpp @@ -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; } diff --git a/tests/issues.targets b/tests/issues.targets index 79e559838d..c540e01dde 100644 --- a/tests/issues.targets +++ b/tests/issues.targets @@ -329,6 +329,9 @@ <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 --> diff --git a/tests/src/Interop/COM/NETClients/Aggregation/NETClientAggregation.csproj b/tests/src/Interop/COM/NETClients/Aggregation/NETClientAggregation.csproj index 182ba923c9..63df657451 100644 --- a/tests/src/Interop/COM/NETClients/Aggregation/NETClientAggregation.csproj +++ b/tests/src/Interop/COM/NETClients/Aggregation/NETClientAggregation.csproj @@ -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/tests/src/Interop/COM/NETClients/ConsumeNETServer/App.manifest b/tests/src/Interop/COM/NETClients/ConsumeNETServer/App.manifest new file mode 100644 index 0000000000..58fc0a2781 --- /dev/null +++ b/tests/src/Interop/COM/NETClients/ConsumeNETServer/App.manifest @@ -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/tests/src/Interop/COM/NETClients/ConsumeNETServer/ConsumeNETServer.csproj b/tests/src/Interop/COM/NETClients/ConsumeNETServer/ConsumeNETServer.csproj new file mode 100644 index 0000000000..9202a9757a --- /dev/null +++ b/tests/src/Interop/COM/NETClients/ConsumeNETServer/ConsumeNETServer.csproj @@ -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/tests/src/Interop/COM/NETClients/ConsumeNETServer/CoreShim.X.manifest b/tests/src/Interop/COM/NETClients/ConsumeNETServer/CoreShim.X.manifest new file mode 100644 index 0000000000..abb39fbb21 --- /dev/null +++ b/tests/src/Interop/COM/NETClients/ConsumeNETServer/CoreShim.X.manifest @@ -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/tests/src/Interop/COM/NETClients/ConsumeNETServer/Program.cs b/tests/src/Interop/COM/NETClients/ConsumeNETServer/Program.cs new file mode 100644 index 0000000000..35ce2803a3 --- /dev/null +++ b/tests/src/Interop/COM/NETClients/ConsumeNETServer/Program.cs @@ -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; + } + } +} diff --git a/tests/src/Interop/COM/NETClients/Events/NETClientEvents.csproj b/tests/src/Interop/COM/NETClients/Events/NETClientEvents.csproj index fccbee9258..f346a0227e 100644 --- a/tests/src/Interop/COM/NETClients/Events/NETClientEvents.csproj +++ b/tests/src/Interop/COM/NETClients/Events/NETClientEvents.csproj @@ -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" /> diff --git a/tests/src/Interop/COM/NETClients/IDispatch/NETClientIDispatch.csproj b/tests/src/Interop/COM/NETClients/IDispatch/NETClientIDispatch.csproj index 4fd95f6694..a99416ba28 100644 --- a/tests/src/Interop/COM/NETClients/IDispatch/NETClientIDispatch.csproj +++ b/tests/src/Interop/COM/NETClients/IDispatch/NETClientIDispatch.csproj @@ -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/tests/src/Interop/COM/NETClients/Licensing/NETClientLicense.csproj b/tests/src/Interop/COM/NETClients/Licensing/NETClientLicense.csproj index 52154225c4..a46a2a2619 100644 --- a/tests/src/Interop/COM/NETClients/Licensing/NETClientLicense.csproj +++ b/tests/src/Interop/COM/NETClients/Licensing/NETClientLicense.csproj @@ -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/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitives.csproj b/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitives.csproj index 81c06a3f3c..c1f38576e3 100644 --- a/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitives.csproj +++ b/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitives.csproj @@ -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/tests/src/Interop/COM/NETServer/ConsumeNETServerTesting.cs b/tests/src/Interop/COM/NETServer/ConsumeNETServerTesting.cs new file mode 100644 index 0000000000..d8dba73982 --- /dev/null +++ b/tests/src/Interop/COM/NETServer/ConsumeNETServerTesting.cs @@ -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; + } +} diff --git a/tests/src/Interop/COM/NETServer/NETServer.csproj b/tests/src/Interop/COM/NETServer/NETServer.csproj index aa4e791a6c..e820c0c63f 100644 --- a/tests/src/Interop/COM/NETServer/NETServer.csproj +++ b/tests/src/Interop/COM/NETServer/NETServer.csproj @@ -17,13 +17,7 @@ <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> diff --git a/tests/src/Interop/COM/ServerContracts/NativeServers.cs b/tests/src/Interop/COM/ServerContracts/Server.CoClasses.cs index 724f7e2bcc..25432a27db 100644 --- a/tests/src/Interop/COM/ServerContracts/NativeServers.cs +++ b/tests/src/Interop/COM/ServerContracts/Server.CoClasses.cs @@ -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 diff --git a/tests/src/Interop/COM/ServerContracts/Server.Contracts.cs b/tests/src/Interop/COM/ServerContracts/Server.Contracts.cs index 7c198c7138..f32518485c 100644 --- a/tests/src/Interop/COM/ServerContracts/Server.Contracts.cs +++ b/tests/src/Interop/COM/ServerContracts/Server.Contracts.cs @@ -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 diff --git a/tests/src/Interop/COM/ServerContracts/ServerGuids.cs b/tests/src/Interop/COM/ServerContracts/ServerGuids.cs index 98ed0aed85..d03eacf708 100644 --- a/tests/src/Interop/COM/ServerContracts/ServerGuids.cs +++ b/tests/src/Interop/COM/ServerContracts/ServerGuids.cs @@ -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"; } } |