-
Notifications
You must be signed in to change notification settings - Fork 107
/
ComWrappersSupport.net5.cs
724 lines (627 loc) · 31.2 KB
/
ComWrappersSupport.net5.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using WinRT.Interop;
using static System.Runtime.InteropServices.ComWrappers;
namespace WinRT
{
#if EMBED
internal
#else
public
#endif
static partial class ComWrappersSupport
{
// Instance field and property for Singleton pattern: ComWrappers `set` method should be idempotent
private static DefaultComWrappers _instance;
private static DefaultComWrappers DefaultComWrappersInstance
{
get
{
if (_instance == null)
{
_instance = new DefaultComWrappers();
}
return _instance;
}
}
internal static readonly ConditionalWeakTable<Type, InspectableInfo> InspectableInfoTable = new();
[ThreadStatic]
internal static Type CreateRCWType;
private static ComWrappers _comWrappers;
private static readonly object _comWrappersLock = new object();
private static ComWrappers ComWrappers
{
get
{
if (_comWrappers is null)
{
lock (_comWrappersLock)
{
if (_comWrappers is null)
{
var comWrappersToSet = DefaultComWrappersInstance;
#if !EMBED
ComWrappers.RegisterForTrackerSupport(comWrappersToSet);
#endif
_comWrappers = comWrappersToSet;
}
}
}
return _comWrappers;
}
set
{
lock (_comWrappersLock)
{
if (value == null && _comWrappers == DefaultComWrappersInstance)
{
return;
}
var comWrappersToSet = value ?? DefaultComWrappersInstance;
#if !EMBED
ComWrappers.RegisterForTrackerSupport(comWrappersToSet);
#endif
_comWrappers = comWrappersToSet;
}
}
}
internal static unsafe InspectableInfo GetInspectableInfo(IntPtr pThis)
{
var _this = FindObject<object>(pThis);
return InspectableInfoTable.GetValue(_this.GetType(), o => PregenerateNativeTypeInformation(o).inspectableInfo);
}
public static T CreateRcwForComObject<T>(IntPtr ptr)
{
return CreateRcwForComObject<T>(ptr, true);
}
private static T CreateRcwForComObject<T>(IntPtr ptr, bool tryUseCache)
{
if (ptr == IntPtr.Zero)
{
return default;
}
// CreateRCWType is a thread local which is set here to communicate the statically known type
// when we are called by the ComWrappers API to create the object. We can't pass this through the
// ComWrappers API surface, so we are achieving it via a thread local. We unset it after in case
// there is other calls to it via other means.
CreateRCWType = typeof(T);
var flags = tryUseCache ? CreateObjectFlags.TrackerObject : CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance;
var rcw = ComWrappers.GetOrCreateObjectForComInstance(ptr, flags);
CreateRCWType = null;
// Because .NET will de-duplicate strings and WinRT doesn't,
// our RCW factory returns a wrapper of our string instance.
// This ensures that ComWrappers never sees the same managed object for two different
// native pointers. We unwrap here to ensure that the user-experience is expected
// and consumers get a string object for a Windows.Foundation.IReference<String>.
// We need to do the same thing for System.Type because there can be multiple WUX.Interop.TypeName's
// for a single System.Type.
return rcw switch
{
ABI.System.Nullable nt => (T)nt.Value,
T castRcw => castRcw,
_ when tryUseCache => CreateRcwForComObject<T>(ptr, false),
_ => throw new ArgumentException(string.Format("Unable to create a wrapper object. The WinRT object {0} has type {1} which cannot be assigned to type {2}", ptr, rcw.GetType(), typeof(T)))
};
}
public static bool TryUnwrapObject(object o, out IObjectReference objRef)
{
// The unwrapping here needs to be an exact type match in case the user
// has implemented a WinRT interface or inherited from a WinRT class
// in a .NET (non-projected) type.
if (o is Delegate del)
{
return TryUnwrapObject(del.Target, out objRef);
}
if (o is IWinRTObject winrtObj && winrtObj.HasUnwrappableNativeObject)
{
objRef = winrtObj.NativeObject;
return true;
}
objRef = null;
return false;
}
public static void RegisterObjectForInterface(object obj, IntPtr thisPtr, CreateObjectFlags createObjectFlags) =>
ComWrappers.GetOrRegisterObjectForComInstance(thisPtr, createObjectFlags, obj);
internal static void RegisterObjectForInterface(object obj, IntPtr thisPtr, IntPtr inner, CreateObjectFlags createObjectFlags) =>
ComWrappers.GetOrRegisterObjectForComInstance(thisPtr, createObjectFlags, obj, inner);
public static void RegisterObjectForInterface(object obj, IntPtr thisPtr) =>
TryRegisterObjectForInterface(obj, thisPtr);
public static object TryRegisterObjectForInterface(object obj, IntPtr thisPtr)
{
return ComWrappers.GetOrRegisterObjectForComInstance(thisPtr, CreateObjectFlags.TrackerObject, obj);
}
internal static IntPtr CreateCCWForObjectUnsafe(object obj)
{
return ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport);
}
public static IObjectReference CreateCCWForObject(object obj)
{
IntPtr ccw = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport);
return ObjectReference<IUnknownVftbl>.Attach(ref ccw, IID.IID_IUnknown);
}
internal static IntPtr CreateCCWForObjectForABI(object obj, Guid iid)
{
IntPtr ccw = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport);
int hr = Marshal.QueryInterface(ccw, ref iid, out var iidCcw);
Marshal.Release(ccw);
if (hr < 0)
{
[MethodImpl(MethodImplOptions.NoInlining)]
static void ThrowException(object obj, Guid iid, int hr)
{
// Special case 'E_NOINTERFACE' to provide a better exception message
if (hr == ExceptionHelpers.E_NOINTERFACE)
{
throw new InvalidCastException($"Failed to create a CCW for object of type '{obj.GetType()}' for interface with IID '{iid.ToString().ToUpperInvariant()}': the specified cast is not valid.");
}
ExceptionHelpers.ThrowExceptionForHR(hr);
}
ThrowException(obj, iid, hr);
}
return iidCcw;
}
public static unsafe T FindObject<T>(IntPtr ptr)
where T : class => ComInterfaceDispatch.GetInstance<T>((ComInterfaceDispatch*)ptr);
public static IUnknownVftbl IUnknownVftbl => DefaultComWrappers.IUnknownVftbl;
public static IntPtr IUnknownVftblPtr => DefaultComWrappers.IUnknownVftblPtr;
public static IntPtr AllocateVtableMemory(Type vtableType, int size) => RuntimeHelpers.AllocateTypeAssociatedMemory(vtableType, size);
/// <summary>
/// Initialize the global <see cref="System.Runtime.InteropServices.ComWrappers"/> instance to use for WinRT.
/// </summary>
/// <param name="wrappers">The wrappers instance to use, or the default if null.</param>
/// <remarks>
/// A custom ComWrappers instance can be supplied to enable programs to fast-track some type resolution
/// instead of using reflection when the full type closure is known.
/// </remarks>
public static void InitializeComWrappers(ComWrappers wrappers = null)
{
ComWrappers = wrappers;
}
internal static Func<IInspectable, object> GetTypedRcwFactory(Type implementationType) => TypedObjectFactoryCacheForType.GetOrAdd(implementationType, CreateTypedRcwFactory);
public static bool RegisterTypedRcwFactory(Type implementationType, Func<IInspectable, object> rcwFactory) => TypedObjectFactoryCacheForType.TryAdd(implementationType, rcwFactory);
private static Func<IInspectable, object> CreateFactoryForImplementationType(string runtimeClassName, Type implementationType)
{
if (implementationType.IsGenericType)
{
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
throw new NotSupportedException($"Cannot create an RCW factory for implementation type '{implementationType}'.");
}
#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273
Type genericImplType = GetGenericImplType(implementationType);
#pragma warning restore IL3050
if (genericImplType != null)
{
var createRcw = genericImplType.GetMethod("CreateRcw", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(IInspectable) }, null);
return (Func<IInspectable, object>)createRcw.CreateDelegate(typeof(Func<IInspectable, object>));
}
}
if (implementationType.IsInterface)
{
return obj => obj;
}
// We never look for attributes on base types, since each [WinRTImplementationTypeRcwFactory] type acts as
// a factory type specifically for the annotated implementation type, so it has to be on that derived type.
var attribute = implementationType.GetCustomAttribute<WinRTImplementationTypeRcwFactoryAttribute>(inherit: false);
// For update projections, get the derived [WinRTImplementationTypeRcwFactory]
// attribute instance and use its overridden 'CreateInstance' method as factory.
if (attribute is not null)
{
return attribute.CreateInstance;
}
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
throw new NotSupportedException(
$"Cannot create an RCW factory for implementation type '{implementationType}', because it doesn't have " +
"a [WinRTImplementationTypeRcwFactory] derived attribute on it. The fallback path for older projections " +
"is not trim-safe, and isn't supported in AOT environments. Make sure to reference updated projections.");
}
return CreateRcwFallback(implementationType);
[UnconditionalSuppressMessage("Trimming", "IL2070", Justification = "This fallback path is not trim-safe by design (to avoid annotations).")]
static Func<IInspectable, object> CreateRcwFallback(Type implementationType)
{
var constructor = implementationType.GetConstructor(
bindingAttr: BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance,
binder: null,
types: new[] { typeof(IObjectReference) },
modifiers: null);
return (IInspectable obj) => constructor.Invoke(new[] { obj.ObjRef });
}
#if NET8_0_OR_GREATER
[RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)]
#endif
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
static Type GetGenericImplType(Type implementationType)
{
var genericType = implementationType.GetGenericTypeDefinition();
Type genericImplType = null;
if (genericType == typeof(IList<>))
{
genericImplType = typeof(IListImpl<>);
}
else if (genericType == typeof(IDictionary<,>))
{
genericImplType = typeof(IDictionaryImpl<,>);
}
else if (genericType == typeof(IReadOnlyDictionary<,>))
{
genericImplType = typeof(IReadOnlyDictionaryImpl<,>);
}
else if (genericType == typeof(IReadOnlyList<>))
{
genericImplType = typeof(IReadOnlyListImpl<>);
}
else if (genericType == typeof(IEnumerable<>))
{
genericImplType = typeof(IEnumerableImpl<>);
}
else if (genericType == typeof(IEnumerator<>))
{
genericImplType = typeof(IEnumeratorImpl<>);
}
else
{
return null;
}
return genericImplType.MakeGenericType(implementationType.GetGenericArguments());
}
}
private static readonly List<Func<Type, ComInterfaceEntry[]>> ComInterfaceEntriesLookup = new();
public static void RegisterTypeComInterfaceEntriesLookup(Func<Type, ComInterfaceEntry[]> comInterfaceEntriesLookup)
{
ComInterfaceEntriesLookup.Add(comInterfaceEntriesLookup);
}
internal static ComInterfaceEntry[] GetComInterfaceEntriesForTypeFromLookupTable(Type type)
{
// Using for loop to avoid exception from list changing when using for each.
// List is only added to and if any are added while looping, we can ignore those.
int count = ComInterfaceEntriesLookup.Count;
for (int i = 0; i < count; i++)
{
var comInterfaceEntries = ComInterfaceEntriesLookup[i](type);
if (comInterfaceEntries != null)
{
return comInterfaceEntries;
}
}
return null;
}
private static readonly List<Func<Type, string>> RuntimeClassNameForNonWinRTTypeLookup = new();
public static void RegisterTypeRuntimeClassNameLookup(Func<Type, string> runtimeClassNameLookup)
{
RuntimeClassNameForNonWinRTTypeLookup.Add(runtimeClassNameLookup);
}
internal static string GetRuntimeClassNameForNonWinRTTypeFromLookupTable(Type type)
{
// Using for loop to avoid exception from list changing when using for each.
// List is only added to and if any are added while looping, we can ignore those.
int count = RuntimeClassNameForNonWinRTTypeLookup.Count;
for (int i = 0; i < count; i++)
{
var runtimeClasName = RuntimeClassNameForNonWinRTTypeLookup[i](type);
if (!string.IsNullOrEmpty(runtimeClasName))
{
return runtimeClasName;
}
}
return null;
}
private static readonly ConcurrentDictionary<Type, ComInterfaceEntry[]> ComInterfaceEntriesForType = new();
public static void RegisterComInterfaceEntries(Type implementationType, ComInterfaceEntry[] comInterfaceEntries) => ComInterfaceEntriesForType.TryAdd(implementationType, comInterfaceEntries);
}
#if EMBED
internal
#else
public
#endif
class ComWrappersHelper
{
public unsafe static void Init(
bool isAggregation,
object thisInstance,
IntPtr newInstance,
IntPtr inner,
out IObjectReference objRef)
{
// To keep pre-existing behavior when we don't know the IID, passing it as IUnknown
// as we would have done before.
Init(isAggregation, thisInstance, newInstance, inner, IID.IID_IUnknown, out objRef);
}
public unsafe static void Init(
bool isAggregation,
object thisInstance,
IntPtr newInstance,
IntPtr inner,
Guid iidForNewInstance,
out IObjectReference objRef)
{
objRef = ComWrappersSupport.GetObjectReferenceForInterface(
isAggregation ? inner : newInstance,
isAggregation ? IID.IID_IInspectable : iidForNewInstance,
requireQI: false);
IntPtr referenceTracker;
{
// Determine if the instance supports IReferenceTracker (e.g. WinUI).
// Acquiring this interface is useful for:
// 1) Providing an indication of what value to pass during RCW creation.
// 2) Informing the Reference Tracker runtime during non-aggregation
// scenarios about new references.
//
// If aggregation, query the inner since that will have the implementation
// otherwise the new instance will be used. Since the inner was composed
// it should answer immediately without going through the outer. Either way
// the reference count will go to the new instance.
int hr = Marshal.QueryInterface(objRef.ThisPtr, ref Unsafe.AsRef(in IID.IID_IReferenceTracker), out referenceTracker);
if (hr != 0)
{
referenceTracker = default;
}
}
{
// Determine flags needed for native object wrapper (i.e. RCW) creation.
var createObjectFlags = CreateObjectFlags.None;
IntPtr instanceToWrap = newInstance;
// The instance supports IReferenceTracker.
if (referenceTracker != default)
{
createObjectFlags |= CreateObjectFlags.TrackerObject;
}
// Create a native object wrapper (i.e. RCW).
//
// Note this function will call QueryInterface() on the supplied instance,
// therefore it is important that the enclosing CCW forwards to its inner
// if aggregation is involved. This is typically accomplished through an
// implementation of ICustomQueryInterface.
if (isAggregation)
{
// Indicate the scenario is aggregation
createObjectFlags |= CreateObjectFlags.Aggregation;
// The instance supports IReferenceTracker.
if (referenceTracker != default)
{
// IReferenceTracker is not needed in aggregation scenarios.
// It is not needed because all QueryInterface() calls on an
// object are followed by an immediately release of the returned
// pointer - see below for details.
Marshal.Release(referenceTracker);
ComWrappersSupport.RegisterObjectForInterface(thisInstance, instanceToWrap, inner, createObjectFlags);
}
else
{
ComWrappersSupport.RegisterObjectForInterface(thisInstance, instanceToWrap, createObjectFlags);
}
}
else
{
ComWrappersSupport.RegisterObjectForInterface(thisInstance, instanceToWrap, createObjectFlags);
}
}
// The following sets up the object reference to correctly handle AddRefs and releases
// based on the scenario.
if (isAggregation)
{
// Aggregation scenarios should avoid calling AddRef() on the
// newInstance value. This is due to the semantics of COM Aggregation
// and the fact that calling an AddRef() on the instance will increment
// the CCW which in turn will ensure it cannot be cleaned up. Calling
// AddRef() on the instance when passed to unmanaged code is correct
// since unmanaged code is required to call Release() at some point.
// A pointer to the inner that should be queried for
// additional interfaces. Immediately after a QueryInterface()
// a Release() should be called on the returned pointer but the
// pointer can be retained and used. This is determined by the
// IsAggregated and PreventReleaseOnDispose properties on IObjectReference.
objRef.IsAggregated = true;
// In WinUI scenario don't release inner
objRef.PreventReleaseOnDispose = referenceTracker != default;
}
else
{
if (referenceTracker != default)
{
// WinUI scenario
// This instance should be used to tell the
// Reference Tracker runtime whenever an AddRef()/Release()
// is performed on newInstance.
objRef.ReferenceTrackerPtr = referenceTracker;
// This instance is already AddRefFromTrackerSource by the CLR,
// so it would also ReleaseFromTrackerSource on destruction.
objRef.PreventReleaseFromTrackerSourceOnDispose = true;
Marshal.Release(referenceTracker);
}
Marshal.Release(newInstance);
}
}
public unsafe static void Init(IObjectReference objRef, bool addRefFromTrackerSource = true)
{
if (objRef.ReferenceTrackerPtr == IntPtr.Zero)
{
int hr = Marshal.QueryInterface(objRef.ThisPtr, ref Unsafe.AsRef(in IID.IID_IReferenceTracker), out var referenceTracker);
if (hr == 0)
{
// WinUI scenario
// This instance should be used to tell the
// Reference Tracker runtime whenever an AddRef()/Release()
// is performed on newInstance.
objRef.ReferenceTrackerPtr = referenceTracker;
if (addRefFromTrackerSource)
{
objRef.AddRefFromTrackerSource(); // ObjRef instance
}
else
{
objRef.PreventReleaseFromTrackerSourceOnDispose = true;
}
Marshal.Release(referenceTracker);
}
}
}
}
#if EMBED
internal
#else
public
#endif
class DefaultComWrappers : ComWrappers
{
private static readonly ConditionalWeakTable<Type, VtableEntries> TypeVtableEntryTable = new();
public static unsafe IUnknownVftbl IUnknownVftbl => Unsafe.AsRef<IUnknownVftbl>(IUnknownVftblPtr.ToPointer());
internal static IntPtr IUnknownVftblPtr { get; }
static unsafe DefaultComWrappers()
{
GetIUnknownImpl(out var qi, out var addRef, out var release);
IUnknownVftblPtr = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IUnknownVftbl), sizeof(IUnknownVftbl));
(*(IUnknownVftbl*)IUnknownVftblPtr) = new IUnknownVftbl
{
QueryInterface = (delegate* unmanaged[Stdcall]<IntPtr, Guid*, IntPtr*, int>)qi,
AddRef = (delegate* unmanaged[Stdcall]<IntPtr, uint>)addRef,
Release = (delegate* unmanaged[Stdcall]<IntPtr, uint>)release,
};
}
protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count)
{
var vtableEntries = TypeVtableEntryTable.GetValue(obj.GetType(), static (type) =>
{
if (IsRuntimeImplementedRCW(type))
{
// If the object is a runtime-implemented RCW, let the runtime create a CCW.
return new VtableEntries();
}
return new VtableEntries(ComWrappersSupport.GetInterfaceTableEntries(type), type);
});
count = vtableEntries.Count;
if (count != 0 && !flags.HasFlag(CreateComInterfaceFlags.CallerDefinedIUnknown))
{
// The vtable list unconditionally has the last entry as IUnknown, but it should
// only be included if the flag is set. We achieve that by excluding the last entry
// from the count if the flag isn't set.
count -= 1;
}
return vtableEntries.Data;
}
private static unsafe bool IsRuntimeImplementedRCW(Type objType)
{
// Built-in COM interop isn't supported in AOT environments,
// so this method can only ever return false. Just inline it.
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
return false;
}
bool isRcw = objType.IsCOMObject;
if (objType.IsGenericType)
{
foreach (var arg in objType.GetGenericArguments())
{
if (arg.IsCOMObject)
{
isRcw = true;
break;
}
}
}
return isRcw;
}
private static object CreateObject(IntPtr externalComObject)
{
Guid inspectableIID = IID.IID_IInspectable;
Guid weakReferenceIID = IID.IID_IWeakReference;
IntPtr ptr = IntPtr.Zero;
try
{
if (ComWrappersSupport.CreateRCWType != null && ComWrappersSupport.CreateRCWType.IsDelegate())
{
return ComWrappersSupport.GetOrCreateDelegateFactory(ComWrappersSupport.CreateRCWType)(externalComObject);
}
else if (Marshal.QueryInterface(externalComObject, ref inspectableIID, out ptr) == 0)
{
var inspectableObjRef = ComWrappersSupport.GetObjectReferenceForInterface<IUnknownVftbl>(ptr, IID.IID_IInspectable, false);
ComWrappersHelper.Init(inspectableObjRef);
IInspectable inspectable = new(inspectableObjRef);
if (ComWrappersSupport.CreateRCWType != null
&& ComWrappersSupport.CreateRCWType.IsSealed)
{
return ComWrappersSupport.GetTypedRcwFactory(ComWrappersSupport.CreateRCWType)(inspectable);
}
Type runtimeClassType = ComWrappersSupport.GetRuntimeClassForTypeCreation(inspectable, ComWrappersSupport.CreateRCWType);
if (runtimeClassType == null)
{
// If the external IInspectable has not implemented GetRuntimeClassName,
// we use the Inspectable wrapper directly.
return inspectable;
}
return ComWrappersSupport.GetTypedRcwFactory(runtimeClassType)(inspectable);
}
else if (Marshal.QueryInterface(externalComObject, ref weakReferenceIID, out ptr) == 0)
{
// IWeakReference is IUnknown-based, so implementations of it may not (and likely won't) implement
// IInspectable. As a result, we need to check for them explicitly.
var iunknownObjRef = ComWrappersSupport.GetObjectReferenceForInterface<IUnknownVftbl>(ptr, weakReferenceIID, false);
ComWrappersHelper.Init(iunknownObjRef);
return new SingleInterfaceOptimizedObject(typeof(IWeakReference), iunknownObjRef, false);
}
else
{
// If the external COM object isn't IInspectable or IWeakReference, we can't handle it.
// If we're registered globally, we want to let the runtime fall back for IUnknown and IDispatch support.
// Return null so the runtime can fall back gracefully in IUnknown and IDispatch scenarios.
return null;
}
}
finally
{
MarshalExtensions.ReleaseIfNotNull(ptr);
}
}
protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags)
{
var obj = CreateObject(externalComObject);
if (obj is IWinRTObject winrtObj && winrtObj.HasUnwrappableNativeObject && winrtObj.NativeObject != null)
{
// Handle the scenario where the CLR has already done an AddRefFromTrackerSource on the instance
// stored by the RCW type. We handle it by releasing the AddRef we did and not doing an release
// on destruction as the CLR would do it.
winrtObj.NativeObject.ReleaseFromTrackerSource();
winrtObj.NativeObject.PreventReleaseFromTrackerSourceOnDispose = true;
}
return obj;
}
protected override void ReleaseObjects(IEnumerable objects)
{
foreach (var obj in objects)
{
if (ComWrappersSupport.TryUnwrapObject(obj, out var objRef))
{
objRef.Dispose();
}
else
{
throw new InvalidOperationException("Cannot release objects that are not runtime wrappers of native WinRT objects.");
}
}
}
unsafe sealed class VtableEntries
{
public ComInterfaceEntry* Data { get; }
public int Count { get; }
public VtableEntries()
{
Data = null;
Count = 0;
}
public VtableEntries(List<ComInterfaceEntry> entries, Type type)
{
Data = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(type, sizeof(ComInterfaceEntry) * entries.Count);
CollectionsMarshal.AsSpan(entries).CopyTo(new Span<ComInterfaceEntry>(Data, entries.Count));
Count = entries.Count;
}
}
}
}