summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Robinson <arobins@microsoft.com>2019-04-09 15:07:34 -0700
committerGitHub <noreply@github.com>2019-04-09 15:07:34 -0700
commita6b0eef9d4a61e3ef5c3879a5016931f8ca0cf99 (patch)
treefc4ab7c05721f8dcfb667cd522737f2c1cd8f12f
parent5608b4ff0f81b99a5d436dec1e23b393503a4e07 (diff)
downloadcoreclr-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
-rw-r--r--src/coreclr/hosts/corerun/corerun.cpp36
-rw-r--r--src/coreclr/hosts/coreshim/CoreShim.cpp18
-rw-r--r--src/coreclr/hosts/coreshim/CoreShim.h1
-rw-r--r--src/vm/comcallablewrapper.cpp75
-rw-r--r--src/vm/comcallablewrapper.h78
-rw-r--r--src/vm/interopconverter.cpp23
-rw-r--r--src/vm/runtimecallablewrapper.cpp6
-rw-r--r--tests/issues.targets3
-rw-r--r--tests/src/Interop/COM/NETClients/Aggregation/NETClientAggregation.csproj2
-rw-r--r--tests/src/Interop/COM/NETClients/ConsumeNETServer/App.manifest18
-rw-r--r--tests/src/Interop/COM/NETClients/ConsumeNETServer/ConsumeNETServer.csproj54
-rw-r--r--tests/src/Interop/COM/NETClients/ConsumeNETServer/CoreShim.X.manifest16
-rw-r--r--tests/src/Interop/COM/NETClients/ConsumeNETServer/Program.cs122
-rw-r--r--tests/src/Interop/COM/NETClients/Events/NETClientEvents.csproj2
-rw-r--r--tests/src/Interop/COM/NETClients/IDispatch/NETClientIDispatch.csproj2
-rw-r--r--tests/src/Interop/COM/NETClients/Licensing/NETClientLicense.csproj2
-rw-r--r--tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitives.csproj2
-rw-r--r--tests/src/Interop/COM/NETServer/ConsumeNETServerTesting.cs51
-rw-r--r--tests/src/Interop/COM/NETServer/NETServer.csproj8
-rw-r--r--tests/src/Interop/COM/ServerContracts/Server.CoClasses.cs (renamed from tests/src/Interop/COM/ServerContracts/NativeServers.cs)25
-rw-r--r--tests/src/Interop/COM/ServerContracts/Server.Contracts.cs16
-rw-r--r--tests/src/Interop/COM/ServerContracts/ServerGuids.cs1
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";
}
}