Skip to content

Commit a7d1332

Browse files
kant2002jkotas
andauthored
Consolidate ComWrappers implementation across platforms (#47982)
* Consolidate ComWrappers implementation across platforms This should help in implementing dotnet/runtimelab#306 * Fix incorrect and missing partial declarations * Update src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent a66b4e3 commit a7d1332

File tree

4 files changed

+136
-150
lines changed

4 files changed

+136
-150
lines changed

src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs

Lines changed: 1 addition & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -10,66 +10,6 @@
1010

1111
namespace System.Runtime.InteropServices
1212
{
13-
/// <summary>
14-
/// Enumeration of flags for <see cref="ComWrappers.GetOrCreateComInterfaceForObject(object, CreateComInterfaceFlags)"/>.
15-
/// </summary>
16-
[Flags]
17-
public enum CreateComInterfaceFlags
18-
{
19-
None = 0,
20-
21-
/// <summary>
22-
/// The caller will provide an IUnknown Vtable.
23-
/// </summary>
24-
/// <remarks>
25-
/// This is useful in scenarios when the caller has no need to rely on an IUnknown instance
26-
/// that is used when running managed code is not possible (i.e. during a GC). In traditional
27-
/// COM scenarios this is common, but scenarios involving <see href="https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/nn-windows-ui-xaml-hosting-referencetracker-ireferencetrackertarget">Reference Tracker hosting</see>
28-
/// calling of the IUnknown API during a GC is possible.
29-
/// </remarks>
30-
CallerDefinedIUnknown = 1,
31-
32-
/// <summary>
33-
/// Flag used to indicate the COM interface should implement <see href="https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/nn-windows-ui-xaml-hosting-referencetracker-ireferencetrackertarget">IReferenceTrackerTarget</see>.
34-
/// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown
35-
/// and as such none should be supplied by the caller.
36-
/// </summary>
37-
TrackerSupport = 2,
38-
}
39-
40-
/// <summary>
41-
/// Enumeration of flags for <see cref="ComWrappers.GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags)"/>.
42-
/// </summary>
43-
[Flags]
44-
public enum CreateObjectFlags
45-
{
46-
None = 0,
47-
48-
/// <summary>
49-
/// Indicate if the supplied external COM object implements the <see href="https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/nn-windows-ui-xaml-hosting-referencetracker-ireferencetracker">IReferenceTracker</see>.
50-
/// </summary>
51-
TrackerObject = 1,
52-
53-
/// <summary>
54-
/// Ignore any internal caching and always create a unique instance.
55-
/// </summary>
56-
UniqueInstance = 2,
57-
58-
/// <summary>
59-
/// Defined when COM aggregation is involved (that is an inner instance supplied).
60-
/// </summary>
61-
Aggregation = 4,
62-
63-
/// <summary>
64-
/// Check if the supplied instance is actually a wrapper and if so return the underlying
65-
/// managed object rather than creating a new wrapper.
66-
/// </summary>
67-
/// <remarks>
68-
/// This matches the built-in RCW semantics for COM interop.
69-
/// </remarks>
70-
Unwrap = 8,
71-
}
72-
7313
/// <summary>
7414
/// Internal enumeration used by the runtime to indicate the scenario for which ComWrappers is being used.
7515
/// </summary>
@@ -83,33 +23,13 @@ internal enum ComWrappersScenario
8323
/// <summary>
8424
/// Class for managing wrappers of COM IUnknown types.
8525
/// </summary>
86-
[SupportedOSPlatform("windows")]
87-
[CLSCompliant(false)]
8826
public abstract partial class ComWrappers
8927
{
90-
/// <summary>
91-
/// Interface type and pointer to targeted VTable.
92-
/// </summary>
93-
public struct ComInterfaceEntry
94-
{
95-
/// <summary>
96-
/// Interface IID.
97-
/// </summary>
98-
public Guid IID;
99-
100-
/// <summary>
101-
/// Memory must have the same lifetime as the memory returned from the call to <see cref="ComputeVtables(object, CreateComInterfaceFlags, out int)"/>.
102-
/// </summary>
103-
public IntPtr Vtable;
104-
}
105-
10628
/// <summary>
10729
/// ABI for function dispatch of a COM interface.
10830
/// </summary>
109-
public struct ComInterfaceDispatch
31+
public partial struct ComInterfaceDispatch
11032
{
111-
public IntPtr Vtable;
112-
11333
/// <summary>
11434
/// Given a <see cref="System.IntPtr"/> from a generated Vtable, convert to the target type.
11535
/// </summary>
@@ -186,22 +106,6 @@ private static bool TryGetOrCreateComInterfaceForObjectInternal(ComWrappers impl
186106
[DllImport(RuntimeHelpers.QCall)]
187107
private static extern bool TryGetOrCreateComInterfaceForObjectInternal(ObjectHandleOnStack comWrappersImpl, long wrapperId, ObjectHandleOnStack instance, CreateComInterfaceFlags flags, out IntPtr retValue);
188108

189-
/// <summary>
190-
/// Compute the desired Vtable for <paramref name="obj"/> respecting the values of <paramref name="flags"/>.
191-
/// </summary>
192-
/// <param name="obj">Target of the returned Vtables.</param>
193-
/// <param name="flags">Flags used to compute Vtables.</param>
194-
/// <param name="count">The number of elements contained in the returned memory.</param>
195-
/// <returns><see cref="ComInterfaceEntry" /> pointer containing memory for all COM interface entries.</returns>
196-
/// <remarks>
197-
/// All memory returned from this function must either be unmanaged memory, pinned managed memory, or have been
198-
/// allocated with the <see cref="System.Runtime.CompilerServices.RuntimeHelpers.AllocateTypeAssociatedMemory(Type, int)"/> API.
199-
///
200-
/// If the interface entries cannot be created and a negative <paramref name="count" /> or <code>null</code> and a non-zero <paramref name="count" /> are returned,
201-
/// the call to <see cref="ComWrappers.GetOrCreateComInterfaceForObject(object, CreateComInterfaceFlags)"/> will throw a <see cref="System.ArgumentException"/>.
202-
/// </remarks>
203-
protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count);
204-
205109
// Called by the runtime to execute the abstract instance function
206110
internal static unsafe void* CallComputeVtables(ComWrappersScenario scenario, ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count)
207111
{
@@ -248,17 +152,6 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb
248152
return obj!;
249153
}
250154

251-
/// <summary>
252-
/// Create a managed object for the object pointed at by <paramref name="externalComObject"/> respecting the values of <paramref name="flags"/>.
253-
/// </summary>
254-
/// <param name="externalComObject">Object to import for usage into the .NET runtime.</param>
255-
/// <param name="flags">Flags used to describe the external object.</param>
256-
/// <returns>Returns a managed object associated with the supplied external COM object.</returns>
257-
/// <remarks>
258-
/// If the object cannot be created and <code>null</code> is returned, the call to <see cref="ComWrappers.GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags)"/> will throw a <see cref="System.ArgumentNullException"/>.
259-
/// </remarks>
260-
protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags);
261-
262155
// Called by the runtime to execute the abstract instance function.
263156
internal static object? CallCreateObject(ComWrappersScenario scenario, ComWrappers? comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags)
264157
{
@@ -361,12 +254,6 @@ private static bool TryGetOrCreateObjectForComInstanceInternal(
361254
[DllImport(RuntimeHelpers.QCall)]
362255
private static extern bool TryGetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, long wrapperId, IntPtr externalComObject, IntPtr innerMaybe, CreateObjectFlags flags, ObjectHandleOnStack wrapper, ObjectHandleOnStack retValue);
363256

364-
/// <summary>
365-
/// Called when a request is made for a collection of objects to be released outside of normal object or COM interface lifetime.
366-
/// </summary>
367-
/// <param name="objects">Collection of objects to release.</param>
368-
protected abstract void ReleaseObjects(IEnumerable objects);
369-
370257
// Call to execute the virtual instance function
371258
internal static void CallReleaseObjects(ComWrappers? comWrappersImpl, IEnumerable objects)
372259
=> (comWrappersImpl ?? s_globalInstanceForTrackerSupport!).ReleaseObjects(objects);

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,7 @@
745745
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ComTypes\ITypeLib.cs" />
746746
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ComTypes\ITypeLib2.cs" />
747747
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ComVisibleAttribute.cs" />
748+
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ComWrappers.cs" />
748749
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\CriticalHandle.cs" />
749750
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\CULong.cs" />
750751
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\CurrencyWrapper.cs" />

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,10 @@
66

77
namespace System.Runtime.InteropServices
88
{
9-
[Flags]
10-
public enum CreateComInterfaceFlags
9+
public abstract partial class ComWrappers
1110
{
12-
None = 0,
13-
CallerDefinedIUnknown = 1,
14-
TrackerSupport = 2,
15-
}
16-
17-
[Flags]
18-
public enum CreateObjectFlags
19-
{
20-
None = 0,
21-
TrackerObject = 1,
22-
UniqueInstance = 2,
23-
Aggregation = 4,
24-
Unwrap = 8,
25-
}
26-
27-
[SupportedOSPlatform("windows")]
28-
[CLSCompliant(false)]
29-
public abstract class ComWrappers
30-
{
31-
public struct ComInterfaceEntry
11+
public partial struct ComInterfaceDispatch
3212
{
33-
public Guid IID;
34-
public IntPtr Vtable;
35-
}
36-
37-
public struct ComInterfaceDispatch
38-
{
39-
public IntPtr Vtable;
40-
4113
public static unsafe T GetInstance<T>(ComInterfaceDispatch* dispatchPtr) where T : class
4214
{
4315
throw new PlatformNotSupportedException();
@@ -49,15 +21,11 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa
4921
throw new PlatformNotSupportedException();
5022
}
5123

52-
protected abstract unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count);
53-
5424
public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags)
5525
{
5626
throw new PlatformNotSupportedException();
5727
}
5828

59-
protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags);
60-
6129
public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object wrapper)
6230
{
6331
throw new PlatformNotSupportedException();
@@ -68,8 +36,6 @@ public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, Create
6836
throw new PlatformNotSupportedException();
6937
}
7038

71-
protected abstract void ReleaseObjects(IEnumerable objects);
72-
7339
public static void RegisterForTrackerSupport(ComWrappers instance)
7440
{
7541
throw new PlatformNotSupportedException();
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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+
4+
using System.Collections;
5+
using System.Runtime.Versioning;
6+
7+
namespace System.Runtime.InteropServices
8+
{
9+
/// <summary>
10+
/// Enumeration of flags for <see cref="ComWrappers.GetOrCreateComInterfaceForObject(object, CreateComInterfaceFlags)"/>.
11+
/// </summary>
12+
[Flags]
13+
public enum CreateComInterfaceFlags
14+
{
15+
None = 0,
16+
17+
/// <summary>
18+
/// The caller will provide an IUnknown Vtable.
19+
/// </summary>
20+
/// <remarks>
21+
/// This is useful in scenarios when the caller has no need to rely on an IUnknown instance
22+
/// that is used when running managed code is not possible (i.e. during a GC). In traditional
23+
/// COM scenarios this is common, but scenarios involving <see href="https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/nn-windows-ui-xaml-hosting-referencetracker-ireferencetrackertarget">Reference Tracker hosting</see>
24+
/// calling of the IUnknown API during a GC is possible.
25+
/// </remarks>
26+
CallerDefinedIUnknown = 1,
27+
28+
/// <summary>
29+
/// Flag used to indicate the COM interface should implement <see href="https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/nn-windows-ui-xaml-hosting-referencetracker-ireferencetrackertarget">IReferenceTrackerTarget</see>.
30+
/// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown
31+
/// and as such none should be supplied by the caller.
32+
/// </summary>
33+
TrackerSupport = 2,
34+
}
35+
36+
/// <summary>
37+
/// Enumeration of flags for <see cref="ComWrappers.GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags)"/>.
38+
/// </summary>
39+
[Flags]
40+
public enum CreateObjectFlags
41+
{
42+
None = 0,
43+
44+
/// <summary>
45+
/// Indicate if the supplied external COM object implements the <see href="https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/nn-windows-ui-xaml-hosting-referencetracker-ireferencetracker">IReferenceTracker</see>.
46+
/// </summary>
47+
TrackerObject = 1,
48+
49+
/// <summary>
50+
/// Ignore any internal caching and always create a unique instance.
51+
/// </summary>
52+
UniqueInstance = 2,
53+
54+
/// <summary>
55+
/// Defined when COM aggregation is involved (that is an inner instance supplied).
56+
/// </summary>
57+
Aggregation = 4,
58+
59+
/// <summary>
60+
/// Check if the supplied instance is actually a wrapper and if so return the underlying
61+
/// managed object rather than creating a new wrapper.
62+
/// </summary>
63+
/// <remarks>
64+
/// This matches the built-in RCW semantics for COM interop.
65+
/// </remarks>
66+
Unwrap = 8,
67+
}
68+
69+
/// <summary>
70+
/// Class for managing wrappers of COM IUnknown types.
71+
/// </summary>
72+
[SupportedOSPlatform("windows")]
73+
[CLSCompliant(false)]
74+
public abstract partial class ComWrappers
75+
{
76+
/// <summary>
77+
/// Interface type and pointer to targeted VTable.
78+
/// </summary>
79+
public struct ComInterfaceEntry
80+
{
81+
/// <summary>
82+
/// Interface IID.
83+
/// </summary>
84+
public Guid IID;
85+
86+
/// <summary>
87+
/// Memory must have the same lifetime as the memory returned from the call to <see cref="ComputeVtables(object, CreateComInterfaceFlags, out int)"/>.
88+
/// </summary>
89+
public IntPtr Vtable;
90+
}
91+
/// <summary>
92+
/// ABI for function dispatch of a COM interface.
93+
/// </summary>
94+
public partial struct ComInterfaceDispatch
95+
{
96+
public IntPtr Vtable;
97+
}
98+
99+
/// <summary>
100+
/// Compute the desired Vtable for <paramref name="obj"/> respecting the values of <paramref name="flags"/>.
101+
/// </summary>
102+
/// <param name="obj">Target of the returned Vtables.</param>
103+
/// <param name="flags">Flags used to compute Vtables.</param>
104+
/// <param name="count">The number of elements contained in the returned memory.</param>
105+
/// <returns><see cref="ComInterfaceEntry" /> pointer containing memory for all COM interface entries.</returns>
106+
/// <remarks>
107+
/// All memory returned from this function must either be unmanaged memory, pinned managed memory, or have been
108+
/// allocated with the <see cref="System.Runtime.CompilerServices.RuntimeHelpers.AllocateTypeAssociatedMemory(Type, int)"/> API.
109+
///
110+
/// If the interface entries cannot be created and a negative <paramref name="count" /> or <code>null</code> and a non-zero <paramref name="count" /> are returned,
111+
/// the call to <see cref="ComWrappers.GetOrCreateComInterfaceForObject(object, CreateComInterfaceFlags)"/> will throw a <see cref="System.ArgumentException"/>.
112+
/// </remarks>
113+
protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count);
114+
115+
/// <summary>
116+
/// Create a managed object for the object pointed at by <paramref name="externalComObject"/> respecting the values of <paramref name="flags"/>.
117+
/// </summary>
118+
/// <param name="externalComObject">Object to import for usage into the .NET runtime.</param>
119+
/// <param name="flags">Flags used to describe the external object.</param>
120+
/// <returns>Returns a managed object associated with the supplied external COM object.</returns>
121+
/// <remarks>
122+
/// If the object cannot be created and <code>null</code> is returned, the call to <see cref="ComWrappers.GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags)"/> will throw a <see cref="System.ArgumentNullException"/>.
123+
/// </remarks>
124+
protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags);
125+
126+
/// <summary>
127+
/// Called when a request is made for a collection of objects to be released outside of normal object or COM interface lifetime.
128+
/// </summary>
129+
/// <param name="objects">Collection of objects to release.</param>
130+
protected abstract void ReleaseObjects(IEnumerable objects);
131+
}
132+
}

0 commit comments

Comments
 (0)