Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit fc33780

Browse files
Basic implementation for testing of COM activation of a .NET class (#19760)
* Rough outline of managed implementation for COM activation in SPCL * Add property for finding interop common Add property to exclude default assertion file Display exe ExeLaunchProgram class is going to launch * Add a native client for the NETServer Consume the ExeLauncherProgram.cs file as a wrapper for the native test * Update COM Server contracts to use 'int' instead of 'long' * Complete symmetric testing coverage for .NET server and native client. * Block EXE launch from running on non-Windows machines * Disable COM testing in helix since it has issues on Windows Nano and there is no way to determine that is the platform. * Update tests based on CLSID mapping manifest approach.
1 parent efd7220 commit fc33780

40 files changed

+3196
-232
lines changed

build-test.cmd

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,8 @@ for /l %%G in (1, 1, %__BuildLoopCount%) do (
324324
set __MsbuildErr=/flp2:ErrorsOnly;LogFile="%__BuildErr%";Append=!__AppendToLog!
325325

326326
set TestBuildSlice=%%G
327-
call %__DotnetHost% msbuild %__ProjectDir%\tests\build.proj !__MsbuildLog! !__MsbuildWrn! !__MsbuildErr! %__msbuildArgs% %__BuildAgainstPackagesMsbuildArg% !__PriorityMsbuildArg! %__UnprocessedBuildArgs%
327+
echo Running: msbuild %__ProjectDir%\tests\build.proj !__MsbuildLog! !__MsbuildWrn! !__MsbuildErr! %__msbuildArgs% %__BuildAgainstPackagesMsbuildArg% !__PriorityMsbuildArg! %__UnprocessedBuildArgs%
328+
call msbuild %__ProjectDir%\tests\build.proj !__MsbuildLog! !__MsbuildWrn! !__MsbuildErr! %__msbuildArgs% %__BuildAgainstPackagesMsbuildArg% !__PriorityMsbuildArg! %__UnprocessedBuildArgs%
328329

329330
if errorlevel 1 (
330331
echo %__MsgPrefix%Error: build failed. Refer to the build log files for details:
@@ -342,7 +343,8 @@ for /l %%G in (1, 1, %__BuildLoopCount%) do (
342343
REM Check that we've built about as many tests as we expect. This is primarily intended to prevent accidental changes that cause us to build
343344
REM drastically fewer Pri-1 tests than expected.
344345
echo %__MsgPrefix%Check the managed tests build
345-
call %__DotnetHost% msbuild %__ProjectDir%\tests\runtest.proj /t:CheckTestBuild /p:CLRTestPriorityToBuild=%__Priority% %__msbuildArgs% %__unprocessedBuildArgs%
346+
echo Running: msbuild %__ProjectDir%\tests\runtest.proj /t:CheckTestBuild /p:CLRTestPriorityToBuild=%__Priority% %__msbuildArgs% %__unprocessedBuildArgs%
347+
call msbuild %__ProjectDir%\tests\runtest.proj /t:CheckTestBuild /p:CLRTestPriorityToBuild=%__Priority% %__msbuildArgs% %__unprocessedBuildArgs%
346348
if errorlevel 1 (
347349
echo %__MsgPrefix%Error: build failed.
348350
exit /b 1

src/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@
158158
<Compile Condition="'$(FeatureCominterop)' != 'true'" Include="$(BclSourcesRoot)\System\Runtime\InteropServices\NonPortable.cs" />
159159
<Compile Condition="'$(FeatureCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\Runtime\InteropServices\DispatchWrapper.cs" />
160160
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ICustomFactory.cs" />
161+
<Compile Condition="'$(FeatureCominteropUnmanagedActivation)' == 'true'" Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComActivator.cs" />
161162
</ItemGroup>
162163
<ItemGroup>
163164
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\Expando\IExpando.cs" />
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Diagnostics;
7+
using System.IO;
8+
using System.Reflection;
9+
10+
namespace System.Runtime.InteropServices
11+
{
12+
[ComImport]
13+
[ComVisible(false)]
14+
[Guid("00000001-0000-0000-C000-000000000046")]
15+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
16+
internal interface IClassFactory
17+
{
18+
void CreateInstance(
19+
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
20+
ref Guid riid,
21+
[MarshalAs(UnmanagedType.Interface)] out object ppvObject);
22+
23+
void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
24+
}
25+
26+
[StructLayout(LayoutKind.Sequential)]
27+
internal struct LICINFO
28+
{
29+
public int cbLicInfo;
30+
31+
[MarshalAs(UnmanagedType.Bool)]
32+
public bool fRuntimeKeyAvail;
33+
34+
[MarshalAs(UnmanagedType.Bool)]
35+
public bool fLicVerified;
36+
}
37+
38+
[ComImport]
39+
[ComVisible(false)]
40+
[Guid("B196B28F-BAB4-101A-B69C-00AA00341D07")]
41+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
42+
internal interface IClassFactory2 : IClassFactory
43+
{
44+
new void CreateInstance(
45+
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
46+
ref Guid riid,
47+
[MarshalAs(UnmanagedType.Interface)] out object ppvObject);
48+
49+
new void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
50+
51+
void GetLicInfo(ref LICINFO pLicInfo);
52+
53+
void RequestLicKey(
54+
int dwReserved,
55+
[MarshalAs(UnmanagedType.BStr)] out string pBstrKey);
56+
57+
void CreateInstanceLic(
58+
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
59+
[MarshalAs(UnmanagedType.Interface)] object pUnkReserved,
60+
ref Guid riid,
61+
[MarshalAs(UnmanagedType.BStr)] string bstrKey,
62+
[MarshalAs(UnmanagedType.Interface)] out object ppvObject);
63+
}
64+
65+
[StructLayout(LayoutKind.Sequential)]
66+
public struct ComActivationContext
67+
{
68+
public Guid ClassId;
69+
public Guid InterfaceId;
70+
public string AssemblyName;
71+
public string TypeName;
72+
}
73+
74+
[StructLayout(LayoutKind.Sequential)]
75+
public struct ComActivationContextInternal
76+
{
77+
public Guid ClassId;
78+
public Guid InterfaceId;
79+
public IntPtr AssemblyNameBuffer;
80+
public IntPtr TypeNameBuffer;
81+
public IntPtr ClassFactoryDest;
82+
}
83+
84+
public static class ComActivator
85+
{
86+
/// <summary>
87+
/// Entry point for unmanaged COM activation API from managed code
88+
/// </summary>
89+
/// <param name="cxt">Reference to a <see cref="ComActivationContext"/> instance</param>
90+
public static object GetClassFactoryForType(ComActivationContext cxt)
91+
{
92+
if (cxt.InterfaceId != typeof(IClassFactory).GUID
93+
&& cxt.InterfaceId != typeof(IClassFactory2).GUID)
94+
{
95+
throw new NotSupportedException();
96+
}
97+
98+
Type classType = FindClassType(cxt.ClassId, cxt.AssemblyName, cxt.TypeName);
99+
return new BasicClassFactory(cxt.ClassId, classType);
100+
}
101+
102+
/// <summary>
103+
/// Internal entry point for unmanaged COM activation API from native code
104+
/// </summary>
105+
/// <param name="cxtInt">Reference to a <see cref="ComActivationContextInternal"/> instance</param>
106+
public static int GetClassFactoryForTypeInternal(ref ComActivationContextInternal cxtInt)
107+
{
108+
if (IsLoggingEnabled())
109+
{
110+
Log(
111+
$@"{nameof(GetClassFactoryForTypeInternal)} arguments:
112+
{cxtInt.ClassId}
113+
{cxtInt.InterfaceId}
114+
0x{cxtInt.AssemblyNameBuffer.ToInt64():x}
115+
0x{cxtInt.TypeNameBuffer.ToInt64():x}
116+
0x{cxtInt.ClassFactoryDest.ToInt64():x}");
117+
}
118+
119+
try
120+
{
121+
var cxt = new ComActivationContext()
122+
{
123+
ClassId = cxtInt.ClassId,
124+
InterfaceId = cxtInt.InterfaceId,
125+
AssemblyName = Marshal.PtrToStringUTF8(cxtInt.AssemblyNameBuffer),
126+
TypeName = Marshal.PtrToStringUTF8(cxtInt.TypeNameBuffer)
127+
};
128+
129+
object cf = GetClassFactoryForType(cxt);
130+
IntPtr nativeIUnknown = Marshal.GetIUnknownForObject(cf);
131+
Marshal.WriteIntPtr(cxtInt.ClassFactoryDest, nativeIUnknown);
132+
}
133+
catch (Exception e)
134+
{
135+
return e.HResult;
136+
}
137+
138+
return 0;
139+
}
140+
141+
private static bool IsLoggingEnabled()
142+
{
143+
#if COM_ACTIVATOR_DEBUG
144+
return true;
145+
#else
146+
return false;
147+
#endif
148+
}
149+
150+
private static void Log(string fmt, params object[] args)
151+
{
152+
// [TODO] Use FrameworkEventSource in release builds
153+
154+
Debug.WriteLine(fmt, args);
155+
}
156+
157+
private static Type FindClassType(Guid clsid, string assemblyName, string typeName)
158+
{
159+
try
160+
{
161+
Assembly assem = Assembly.LoadFrom(assemblyName);
162+
Type t = assem.GetType(typeName);
163+
if (t != null)
164+
{
165+
return t;
166+
}
167+
}
168+
catch (Exception e)
169+
{
170+
if (IsLoggingEnabled())
171+
{
172+
Log($"COM Activation of {clsid} failed. {e}");
173+
}
174+
}
175+
176+
const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)0x80040111);
177+
throw new COMException(string.Empty, CLASS_E_CLASSNOTAVAILABLE);
178+
}
179+
180+
[ComVisible(true)]
181+
internal class BasicClassFactory : IClassFactory2
182+
{
183+
private readonly Guid classId;
184+
private readonly Type classType;
185+
186+
public BasicClassFactory(Guid clsid, Type classType)
187+
{
188+
this.classId = clsid;
189+
this.classType = classType;
190+
}
191+
192+
public void CreateInstance(
193+
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
194+
ref Guid riid,
195+
[MarshalAs(UnmanagedType.Interface)] out object ppvObject)
196+
{
197+
if (riid != Marshal.IID_IUnknown)
198+
{
199+
bool found = false;
200+
201+
// Verify the class implements the desired interface
202+
foreach (Type i in this.classType.GetInterfaces())
203+
{
204+
if (i.GUID == riid)
205+
{
206+
found = true;
207+
break;
208+
}
209+
}
210+
211+
if (!found)
212+
{
213+
// E_NOINTERFACE
214+
throw new InvalidCastException();
215+
}
216+
}
217+
218+
ppvObject = Activator.CreateInstance(this.classType);
219+
if (pUnkOuter != null)
220+
{
221+
try
222+
{
223+
IntPtr outerPtr = Marshal.GetIUnknownForObject(pUnkOuter);
224+
IntPtr innerPtr = Marshal.CreateAggregatedObject(outerPtr, ppvObject);
225+
ppvObject = Marshal.GetObjectForIUnknown(innerPtr);
226+
}
227+
finally
228+
{
229+
// Decrement the above 'Marshal.GetIUnknownForObject()'
230+
Marshal.ReleaseComObject(pUnkOuter);
231+
}
232+
}
233+
}
234+
235+
public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock)
236+
{
237+
// nop
238+
}
239+
240+
public void GetLicInfo(ref LICINFO pLicInfo)
241+
{
242+
throw new NotImplementedException();
243+
}
244+
245+
public void RequestLicKey(int dwReserved, [MarshalAs(UnmanagedType.BStr)] out string pBstrKey)
246+
{
247+
throw new NotImplementedException();
248+
}
249+
250+
public void CreateInstanceLic(
251+
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
252+
[MarshalAs(UnmanagedType.Interface)] object pUnkReserved,
253+
ref Guid riid,
254+
[MarshalAs(UnmanagedType.BStr)] string bstrKey,
255+
[MarshalAs(UnmanagedType.Interface)] out object ppvObject)
256+
{
257+
throw new NotImplementedException();
258+
}
259+
}
260+
}
261+
}

src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,15 @@ public enum CustomQueryInterfaceMode
2828
/// </summary>
2929
public static partial class Marshal
3030
{
31+
#if FEATURE_COMINTEROP
32+
internal static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
33+
#endif //FEATURE_COMINTEROP
34+
3135
private const int LMEM_FIXED = 0;
3236
private const int LMEM_MOVEABLE = 2;
3337
#if !FEATURE_PAL
3438
private const long HiWordMask = unchecked((long)0xffffffffffff0000L);
3539
#endif //!FEATURE_PAL
36-
#if FEATURE_COMINTEROP
37-
private static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
38-
#endif //FEATURE_COMINTEROP
3940

4041
// Win32 has the concept of Atoms, where a pointer can either be a pointer
4142
// or an int. If it's less than 64K, this is guaranteed to NOT be a

src/coreclr/hosts/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ include_directories(inc)
33
if(WIN32)
44
add_subdirectory(corerun)
55
add_subdirectory(coreconsole)
6+
add_subdirectory(coreshim)
67
else(WIN32)
78
add_subdirectory(unixcoreruncommon)
89
add_subdirectory(unixcorerun)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
project (CoreShim)
2+
3+
set(CMAKE_INCLUDE_CURRENT_DIR ON)
4+
5+
set(CoreShim_SOURCES
6+
CoreShim.cpp
7+
ComActivation.cpp
8+
Exports.def)
9+
10+
add_library_clr(CoreShim
11+
SHARED
12+
${CoreShim_SOURCES}
13+
)
14+
15+
target_link_libraries(CoreShim
16+
utilcodestaticnohost
17+
advapi32.lib
18+
oleaut32.lib
19+
uuid.lib
20+
user32.lib
21+
${STATIC_MT_CRT_LIB}
22+
${STATIC_MT_VCRT_LIB}
23+
)
24+
25+
install_clr(CoreShim)

0 commit comments

Comments
 (0)