@@ -29,6 +29,21 @@ internal struct LICINFO
29
29
public bool fLicVerified ;
30
30
}
31
31
32
+ [ ComImport ]
33
+ [ ComVisible ( false ) ]
34
+ [ Guid ( "00000001-0000-0000-C000-000000000046" ) ]
35
+ [ InterfaceType ( ComInterfaceType . InterfaceIsIUnknown ) ]
36
+ internal interface IClassFactory
37
+ {
38
+ [ RequiresUnreferencedCode ( "Built-in COM support is not trim compatible" , Url = "https://aka.ms/dotnet-illink/com" ) ]
39
+ void CreateInstance (
40
+ [ MarshalAs ( UnmanagedType . Interface ) ] object ? pUnkOuter ,
41
+ ref Guid riid ,
42
+ out IntPtr ppvObject ) ;
43
+
44
+ void LockServer ( [ MarshalAs ( UnmanagedType . Bool ) ] bool fLock ) ;
45
+ }
46
+
32
47
[ ComImport ]
33
48
[ ComVisible ( false ) ]
34
49
[ Guid ( "B196B28F-BAB4-101A-B69C-00AA00341D07" ) ]
@@ -57,9 +72,17 @@ void CreateInstanceLic(
57
72
out IntPtr ppvObject ) ;
58
73
}
59
74
60
- internal partial struct ComActivationContext
75
+ [ StructLayout ( LayoutKind . Sequential ) ]
76
+ internal struct ComActivationContext
61
77
{
62
- public static unsafe ComActivationContext Create ( ref ComActivationContextInternal cxtInt )
78
+ public Guid ClassId ;
79
+ public Guid InterfaceId ;
80
+ public string AssemblyPath ;
81
+ public string AssemblyName ;
82
+ public string TypeName ;
83
+ public bool IsolatedContext ;
84
+
85
+ public static unsafe ComActivationContext Create ( ref ComActivationContextInternal cxtInt , bool isolatedContext )
63
86
{
64
87
if ( ! Marshal . IsBuiltInComSupported )
65
88
{
@@ -72,7 +95,8 @@ public static unsafe ComActivationContext Create(ref ComActivationContextInterna
72
95
InterfaceId = cxtInt . InterfaceId ,
73
96
AssemblyPath = Marshal . PtrToStringUni ( new IntPtr ( cxtInt . AssemblyPathBuffer ) ) ! ,
74
97
AssemblyName = Marshal . PtrToStringUni ( new IntPtr ( cxtInt . AssemblyNameBuffer ) ) ! ,
75
- TypeName = Marshal . PtrToStringUni ( new IntPtr ( cxtInt . TypeNameBuffer ) ) !
98
+ TypeName = Marshal . PtrToStringUni ( new IntPtr ( cxtInt . TypeNameBuffer ) ) ! ,
99
+ IsolatedContext = isolatedContext
76
100
} ;
77
101
}
78
102
}
@@ -84,6 +108,9 @@ internal static class ComActivator
84
108
// unloadable COM server ALCs, this will need to be changed.
85
109
private static readonly Dictionary < string , AssemblyLoadContext > s_assemblyLoadContexts = new Dictionary < string , AssemblyLoadContext > ( StringComparer . InvariantCultureIgnoreCase ) ;
86
110
111
+ // COM component assembly paths loaded in the default ALC
112
+ private static readonly HashSet < string > s_loadedInDefaultContext = new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase ) ;
113
+
87
114
/// <summary>
88
115
/// Entry point for unmanaged COM activation API from managed code
89
116
/// </summary>
@@ -107,7 +134,7 @@ private static object GetClassFactoryForType(ComActivationContext cxt)
107
134
throw new ArgumentException ( null , nameof ( cxt ) ) ;
108
135
}
109
136
110
- Type classType = FindClassType ( cxt . ClassId , cxt . AssemblyPath , cxt . AssemblyName , cxt . TypeName ) ;
137
+ Type classType = FindClassType ( cxt ) ;
111
138
112
139
if ( LicenseInteropProxy . HasLicense ( classType ) )
113
140
{
@@ -145,7 +172,7 @@ private static void ClassRegistrationScenarioForType(ComActivationContext cxt, b
145
172
throw new ArgumentException ( null , nameof ( cxt ) ) ;
146
173
}
147
174
148
- Type classType = FindClassType ( cxt . ClassId , cxt . AssemblyPath , cxt . AssemblyName , cxt . TypeName ) ;
175
+ Type classType = FindClassType ( cxt ) ;
149
176
150
177
Type ? currentType = classType ;
151
178
bool calledFunction = false ;
@@ -213,17 +240,45 @@ private static void ClassRegistrationScenarioForType(ComActivationContext cxt, b
213
240
}
214
241
215
242
/// <summary>
216
- /// Internal entry point for unmanaged COM activation API from native code
243
+ /// Gets a class factory for COM activation in an isolated load context
217
244
/// </summary>
218
245
/// <param name="pCxtInt">Pointer to a <see cref="ComActivationContextInternal"/> instance</param>
219
246
[ UnmanagedCallersOnly ]
220
247
private static unsafe int GetClassFactoryForTypeInternal ( ComActivationContextInternal * pCxtInt )
221
248
{
222
249
if ( ! Marshal . IsBuiltInComSupported )
223
- {
224
250
throw new NotSupportedException ( SR . NotSupported_COM ) ;
225
- }
226
251
252
+ #pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
253
+ return GetClassFactoryForTypeImpl ( pCxtInt , isolatedContext : true ) ;
254
+ #pragma warning restore IL2026
255
+ }
256
+
257
+ /// <summary>
258
+ /// Gets a class factory for COM activation in the specified load context
259
+ /// </summary>
260
+ /// <param name="pCxtInt">Pointer to a <see cref="ComActivationContextInternal"/> instance</param>
261
+ /// <param name="loadContext">Load context - currently must be IntPtr.Zero (default context) or -1 (isolated context)</param>
262
+ [ UnmanagedCallersOnly ]
263
+ private static unsafe int GetClassFactoryForTypeInContext ( ComActivationContextInternal * pCxtInt , IntPtr loadContext )
264
+ {
265
+ if ( ! Marshal . IsBuiltInComSupported )
266
+ throw new NotSupportedException ( SR . NotSupported_COM ) ;
267
+
268
+ if ( loadContext != IntPtr . Zero && loadContext != ( IntPtr ) ( - 1 ) )
269
+ throw new ArgumentOutOfRangeException ( nameof ( loadContext ) ) ;
270
+
271
+ return GetClassFactoryForTypeLocal ( pCxtInt , isolatedContext : loadContext != IntPtr . Zero ) ;
272
+
273
+ // Use a local function for a targeted suppression of the requires unreferenced code warning
274
+ [ UnconditionalSuppressMessage ( "ReflectionAnalysis" , "IL2026:RequiresUnreferencedCode" ,
275
+ Justification = "The same feature switch applies to GetClassFactoryForTypeInternal and this function. We rely on the warning from GetClassFactoryForTypeInternal." ) ]
276
+ static int GetClassFactoryForTypeLocal ( ComActivationContextInternal * pCxtInt , bool isolatedContext ) => GetClassFactoryForTypeImpl ( pCxtInt , isolatedContext ) ;
277
+ }
278
+
279
+ [ RequiresUnreferencedCode ( "Built-in COM support is not trim compatible" , Url = "https://aka.ms/dotnet-illink/com" ) ]
280
+ private static unsafe int GetClassFactoryForTypeImpl ( ComActivationContextInternal * pCxtInt , bool isolatedContext )
281
+ {
227
282
ref ComActivationContextInternal cxtInt = ref * pCxtInt ;
228
283
229
284
if ( IsLoggingEnabled ( ) )
@@ -240,10 +295,8 @@ private static unsafe int GetClassFactoryForTypeInternal(ComActivationContextInt
240
295
241
296
try
242
297
{
243
- var cxt = ComActivationContext . Create ( ref cxtInt ) ;
244
- #pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
298
+ var cxt = ComActivationContext . Create ( ref cxtInt , isolatedContext ) ;
245
299
object cf = GetClassFactoryForType ( cxt ) ;
246
- #pragma warning restore IL2026
247
300
IntPtr nativeIUnknown = Marshal . GetIUnknownForObject ( cf ) ;
248
301
Marshal . WriteIntPtr ( cxtInt . ClassFactoryDest , nativeIUnknown ) ;
249
302
}
@@ -256,17 +309,37 @@ private static unsafe int GetClassFactoryForTypeInternal(ComActivationContextInt
256
309
}
257
310
258
311
/// <summary>
259
- /// Internal entry point for registering a managed COM server API from native code
312
+ /// Registers a managed COM server in an isolated load context
260
313
/// </summary>
261
314
/// <param name="pCxtInt">Pointer to a <see cref="ComActivationContextInternal"/> instance</param>
262
315
[ UnmanagedCallersOnly ]
263
316
private static unsafe int RegisterClassForTypeInternal ( ComActivationContextInternal * pCxtInt )
264
317
{
265
318
if ( ! Marshal . IsBuiltInComSupported )
266
- {
267
319
throw new NotSupportedException ( SR . NotSupported_COM ) ;
268
- }
269
320
321
+ return RegisterClassForTypeImpl ( pCxtInt , isolatedContext : true ) ;
322
+ }
323
+
324
+ /// <summary>
325
+ /// Registers a managed COM server in the specified load context
326
+ /// </summary>
327
+ /// <param name="pCxtInt">Pointer to a <see cref="ComActivationContextInternal"/> instance</param>
328
+ /// <param name="loadContext">Load context - currently must be IntPtr.Zero (default context) or -1 (isolated context)</param>
329
+ [ UnmanagedCallersOnly ]
330
+ private static unsafe int RegisterClassForTypeInContext ( ComActivationContextInternal * pCxtInt , IntPtr loadContext )
331
+ {
332
+ if ( ! Marshal . IsBuiltInComSupported )
333
+ throw new NotSupportedException ( SR . NotSupported_COM ) ;
334
+
335
+ if ( loadContext != IntPtr . Zero && loadContext != ( IntPtr ) ( - 1 ) )
336
+ throw new ArgumentOutOfRangeException ( nameof ( loadContext ) ) ;
337
+
338
+ return RegisterClassForTypeImpl ( pCxtInt , isolatedContext : loadContext != IntPtr . Zero ) ;
339
+ }
340
+
341
+ private static unsafe int RegisterClassForTypeImpl ( ComActivationContextInternal * pCxtInt , bool isolatedContext )
342
+ {
270
343
ref ComActivationContextInternal cxtInt = ref * pCxtInt ;
271
344
272
345
if ( IsLoggingEnabled ( ) )
@@ -289,7 +362,7 @@ private static unsafe int RegisterClassForTypeInternal(ComActivationContextInter
289
362
290
363
try
291
364
{
292
- var cxt = ComActivationContext . Create ( ref cxtInt ) ;
365
+ var cxt = ComActivationContext . Create ( ref cxtInt , isolatedContext ) ;
293
366
ClassRegistrationScenarioForTypeLocal ( cxt , register : true ) ;
294
367
}
295
368
catch ( Exception e )
@@ -306,16 +379,36 @@ private static unsafe int RegisterClassForTypeInternal(ComActivationContextInter
306
379
}
307
380
308
381
/// <summary>
309
- /// Internal entry point for unregistering a managed COM server API from native code
382
+ /// Unregisters a managed COM server in an isolated load context
310
383
/// </summary>
311
384
[ UnmanagedCallersOnly ]
312
385
private static unsafe int UnregisterClassForTypeInternal ( ComActivationContextInternal * pCxtInt )
313
386
{
314
387
if ( ! Marshal . IsBuiltInComSupported )
315
- {
316
388
throw new NotSupportedException ( SR . NotSupported_COM ) ;
317
- }
318
389
390
+ return UnregisterClassForTypeImpl ( pCxtInt , isolatedContext : true ) ;
391
+ }
392
+
393
+ /// <summary>
394
+ /// Unregisters a managed COM server in the specified load context
395
+ /// </summary>
396
+ /// <param name="pCxtInt">Pointer to a <see cref="ComActivationContextInternal"/> instance</param>
397
+ /// <param name="loadContext">Load context - currently must be IntPtr.Zero (default context) or -1 (isolated context)</param>
398
+ [ UnmanagedCallersOnly ]
399
+ private static unsafe int UnregisterClassForTypeInContext ( ComActivationContextInternal * pCxtInt , IntPtr loadContext )
400
+ {
401
+ if ( ! Marshal . IsBuiltInComSupported )
402
+ throw new NotSupportedException ( SR . NotSupported_COM ) ;
403
+
404
+ if ( loadContext != IntPtr . Zero && loadContext != ( IntPtr ) ( - 1 ) )
405
+ throw new ArgumentOutOfRangeException ( nameof ( loadContext ) ) ;
406
+
407
+ return UnregisterClassForTypeImpl ( pCxtInt , isolatedContext : loadContext != IntPtr . Zero ) ;
408
+ }
409
+
410
+ private static unsafe int UnregisterClassForTypeImpl ( ComActivationContextInternal * pCxtInt , bool isolatedContext )
411
+ {
319
412
ref ComActivationContextInternal cxtInt = ref * pCxtInt ;
320
413
321
414
if ( IsLoggingEnabled ( ) )
@@ -338,7 +431,7 @@ private static unsafe int UnregisterClassForTypeInternal(ComActivationContextInt
338
431
339
432
try
340
433
{
341
- var cxt = ComActivationContext . Create ( ref cxtInt ) ;
434
+ var cxt = ComActivationContext . Create ( ref cxtInt , isolatedContext ) ;
342
435
ClassRegistrationScenarioForTypeLocal ( cxt , register : false ) ;
343
436
}
344
437
catch ( Exception e )
@@ -370,14 +463,14 @@ private static void Log(string fmt, params object[] args)
370
463
}
371
464
372
465
[ RequiresUnreferencedCode ( "Built-in COM support is not trim compatible" , Url = "https://aka.ms/dotnet-illink/com" ) ]
373
- private static Type FindClassType ( Guid clsid , string assemblyPath , string assemblyName , string typeName )
466
+ private static Type FindClassType ( ComActivationContext cxt )
374
467
{
375
468
try
376
469
{
377
- AssemblyLoadContext alc = GetALC ( assemblyPath ) ;
378
- var assemblyNameLocal = new AssemblyName ( assemblyName ) ;
470
+ AssemblyLoadContext alc = GetALC ( cxt . AssemblyPath , cxt . IsolatedContext ) ;
471
+ var assemblyNameLocal = new AssemblyName ( cxt . AssemblyName ) ;
379
472
Assembly assem = alc . LoadFromAssemblyName ( assemblyNameLocal ) ;
380
- Type ? t = assem . GetType ( typeName ) ;
473
+ Type ? t = assem . GetType ( cxt . TypeName ) ;
381
474
if ( t != null )
382
475
{
383
476
return t ;
@@ -387,7 +480,7 @@ private static Type FindClassType(Guid clsid, string assemblyPath, string assemb
387
480
{
388
481
if ( IsLoggingEnabled ( ) )
389
482
{
390
- Log ( $ "COM Activation of { clsid } failed. { e } ") ;
483
+ Log ( $ "COM Activation of { cxt . ClassId } failed. { e } ") ;
391
484
}
392
485
}
393
486
@@ -396,16 +489,39 @@ private static Type FindClassType(Guid clsid, string assemblyPath, string assemb
396
489
}
397
490
398
491
[ RequiresUnreferencedCode ( "The trimmer might remove types which are needed by the assemblies loaded in this method." ) ]
399
- private static AssemblyLoadContext GetALC ( string assemblyPath )
492
+ private static AssemblyLoadContext GetALC ( string assemblyPath , bool isolatedContext )
400
493
{
401
494
AssemblyLoadContext ? alc ;
402
-
403
- lock ( s_assemblyLoadContexts )
495
+ if ( isolatedContext )
496
+ {
497
+ lock ( s_assemblyLoadContexts )
498
+ {
499
+ if ( ! s_assemblyLoadContexts . TryGetValue ( assemblyPath , out alc ) )
500
+ {
501
+ alc = new IsolatedComponentLoadContext ( assemblyPath ) ;
502
+ s_assemblyLoadContexts . Add ( assemblyPath , alc ) ;
503
+ }
504
+ }
505
+ }
506
+ else
404
507
{
405
- if ( ! s_assemblyLoadContexts . TryGetValue ( assemblyPath , out alc ) )
508
+ alc = AssemblyLoadContext . Default ;
509
+ lock ( s_loadedInDefaultContext )
406
510
{
407
- alc = new IsolatedComponentLoadContext ( assemblyPath ) ;
408
- s_assemblyLoadContexts . Add ( assemblyPath , alc ) ;
511
+ if ( ! s_loadedInDefaultContext . Contains ( assemblyPath ) )
512
+ {
513
+ var resolver = new AssemblyDependencyResolver ( assemblyPath ) ;
514
+ AssemblyLoadContext . Default . Resolving +=
515
+ ( context , assemblyName ) =>
516
+ {
517
+ string ? assemblyPath = resolver . ResolveAssemblyToPath ( assemblyName ) ;
518
+ return assemblyPath != null
519
+ ? context . LoadFromAssemblyPath ( assemblyPath )
520
+ : null ;
521
+ } ;
522
+
523
+ s_loadedInDefaultContext . Add ( assemblyPath ) ;
524
+ }
409
525
}
410
526
}
411
527
0 commit comments