3
3
// See the LICENSE file in the project root for more information.
4
4
5
5
using System ;
6
- using System . Runtime . CompilerServices ;
7
- using System . Runtime . InteropServices ;
8
- using System . Threading ;
6
+ using CommunityToolkit . WinUI . Interop ;
9
7
using Microsoft . UI . Dispatching ;
10
8
using WinRT ;
11
9
12
10
#nullable enable
13
11
14
- #pragma warning disable SA1000 , SA1023
15
-
16
12
namespace CommunityToolkit . WinUI
17
13
{
18
14
/// <summary>
@@ -102,218 +98,5 @@ public static unsafe bool TryEnqueue<TState>(this DispatcherQueue dispatcherQueu
102
98
103
99
return success ;
104
100
}
105
-
106
- /// <summary>
107
- /// A struct mapping the native WinRT <c>IDispatcherQueue</c> interface.
108
- /// </summary>
109
- private unsafe struct IDispatcherQueue
110
- {
111
- private readonly void * * lpVtbl ;
112
-
113
- /// <summary>
114
- /// Native API for <see cref="DispatcherQueue.TryEnqueue(DispatcherQueueHandler)"/>.
115
- /// </summary>
116
- /// <param name="callback">A pointer to an <c>IDispatcherQueueHandler</c> object.</param>
117
- /// <param name="result">The result of the operation (the <see cref="bool"/> WinRT retval).</param>
118
- /// <returns>The HRESULT for the operation.</returns>
119
- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
120
- public int TryEnqueue ( void * callback , byte * result )
121
- {
122
- return ( ( delegate * unmanaged< IDispatcherQueue * , void * , byte * , int > ) lpVtbl [ 7 ] ) ( ( IDispatcherQueue * ) Unsafe . AsPointer ( ref this ) , callback , result ) ;
123
- }
124
-
125
- /// <summary>
126
- /// Native API for <see cref="DispatcherQueue.TryEnqueue(DispatcherQueuePriority, DispatcherQueueHandler)"/>.
127
- /// </summary>
128
- /// <param name="priority">The priority for the input callback.</param>
129
- /// <param name="callback">A pointer to an <c>IDispatcherQueueHandler</c> object.</param>
130
- /// <param name="result">The result of the operation (the <see cref="bool"/> WinRT retval).</param>
131
- /// <returns>The HRESULT for the operation.</returns>
132
- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
133
- public int TryEnqueueWithPriority ( DispatcherQueuePriority priority , void * callback , byte * result )
134
- {
135
- return ( ( delegate * unmanaged< IDispatcherQueue * , DispatcherQueuePriority , void * , byte * , int > ) lpVtbl [ 8 ] ) ( ( IDispatcherQueue * ) Unsafe . AsPointer ( ref this ) , priority , callback , result ) ;
136
- }
137
- }
138
-
139
- /// <summary>
140
- /// A custom <c>IDispatcherQueueHandler</c> object, that internally stores a captured <see cref="DispatcherQueueHandler{TState}"/> instance
141
- /// and the input captured state. This allows consumers to enqueue a state and a cached stateless delegate without any managed allocations.
142
- /// </summary>
143
- private unsafe struct DispatcherQueueProxyHandler
144
- {
145
- private const int S_OK = 0 ;
146
- private const int E_NOINTERFACE = unchecked ( ( int ) 0x80004002 ) ;
147
-
148
- private static readonly Guid IUnknown = new ( 0x00000000 , 0x0000 , 0x0000 , 0xC0 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x46 ) ;
149
- private static readonly Guid IAgileObject = new ( 0x94EA2B94 , 0xE9CC , 0x49E0 , 0xC0 , 0xFF , 0xEE , 0x64 , 0xCA , 0x8F , 0x5B , 0x90 ) ;
150
- private static readonly Guid IDispatcherQueueHandler = new ( 0x2E0872A9 , 0x4E29 , 0x5F14 , 0xB6 , 0x88 , 0xFB , 0x96 , 0xD5 , 0xF9 , 0xD5 , 0xF8 ) ;
151
-
152
- /// <summary>
153
- /// The shared vtable pointer for <see cref="DispatcherQueueProxyHandler"/> instances.
154
- /// </summary>
155
- private static readonly void * * Vtbl = InitVtbl ( ) ;
156
-
157
- /// <summary>
158
- /// Setups the vtable pointer for <see cref="DispatcherQueueProxyHandler"/>.
159
- /// </summary>
160
- /// <returns>The initialized vtable pointer for <see cref="DispatcherQueueProxyHandler"/>.</returns>
161
- /// <remarks>
162
- /// The vtable itself is allocated with <see cref="RuntimeHelpers.AllocateTypeAssociatedMemory(Type, int)"/>,
163
- /// which allocates memory in the high frequency heap associated with the input runtime type. This will be
164
- /// automatically cleaned up when the type is unloaded, so there is no need to ever manually free this memory.
165
- /// </remarks>
166
- private static void * * InitVtbl ( )
167
- {
168
- void * * lpVtbl = ( void * * ) RuntimeHelpers . AllocateTypeAssociatedMemory ( typeof ( DispatcherQueueProxyHandler ) , sizeof ( void * ) * 4 ) ;
169
-
170
- lpVtbl [ 0 ] = ( delegate * unmanaged< DispatcherQueueProxyHandler * , Guid * , void * * , int > ) & Impl . QueryInterface ;
171
- lpVtbl [ 1 ] = ( delegate * unmanaged< DispatcherQueueProxyHandler * , uint > ) & Impl . AddRef ;
172
- lpVtbl [ 2 ] = ( delegate * unmanaged< DispatcherQueueProxyHandler * , uint > ) & Impl . Release ;
173
- lpVtbl [ 3 ] = ( delegate * unmanaged< DispatcherQueueProxyHandler * , int > ) & Impl . Invoke ;
174
-
175
- return lpVtbl ;
176
- }
177
-
178
- /// <summary>
179
- /// The vtable pointer for the current instance.
180
- /// </summary>
181
- private void * * lpVtbl ;
182
-
183
- /// <summary>
184
- /// The <see cref="GCHandle"/> to the captured <see cref="DispatcherQueueHandler{TState}"/> (for some unknown <c>TState</c> type).
185
- /// </summary>
186
- private GCHandle callbackHandle ;
187
-
188
- /// <summary>
189
- /// The <see cref="GCHandle"/> to the captured state (with an unknown <c>TState</c> type).
190
- /// </summary>
191
- private GCHandle stateHandle ;
192
-
193
- /// <summary>
194
- /// The current reference count for the object (from <c>IUnknown</c>).
195
- /// </summary>
196
- private volatile uint referenceCount ;
197
-
198
- /// <summary>
199
- /// Creates a new <see cref="DispatcherQueueProxyHandler"/> instance for the input callback and state.
200
- /// </summary>
201
- /// <typeparam name="TState">The type of state to capture.</typeparam>
202
- /// <param name="handler">The input <see cref="DispatcherQueueHandler{TState}"/> callback to enqueue.</param>
203
- /// <param name="state">The input state to capture and pass to the callback.</param>
204
- /// <returns>A pointer to the newly initialized <see cref="DispatcherQueueProxyHandler"/> instance.</returns>
205
- public static DispatcherQueueProxyHandler * Create < TState > ( DispatcherQueueHandler < TState > handler , TState state )
206
- where TState : class
207
- {
208
- DispatcherQueueProxyHandler * @this = ( DispatcherQueueProxyHandler * ) Marshal . AllocHGlobal ( sizeof ( DispatcherQueueProxyHandler ) ) ;
209
-
210
- @this ->lpVtbl = Vtbl ;
211
- @this ->callbackHandle = GCHandle . Alloc ( handler ) ;
212
- @this ->stateHandle = GCHandle . Alloc ( state ) ;
213
- @this ->referenceCount = 1 ;
214
-
215
- return @this ;
216
- }
217
-
218
- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
219
- public uint AddRef ( )
220
- {
221
- return Interlocked . Increment ( ref referenceCount ) ;
222
- }
223
-
224
- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
225
- public uint Release ( )
226
- {
227
- uint referenceCount = Interlocked . Decrement ( ref this . referenceCount ) ;
228
-
229
- if ( referenceCount == 0 )
230
- {
231
- Marshal . FreeHGlobal ( ( IntPtr ) Unsafe . AsPointer ( ref this ) ) ;
232
- }
233
-
234
- return referenceCount ;
235
- }
236
-
237
- /// <summary>
238
- /// A private type with the implementation of the unmanaged methods for <see cref="DispatcherQueueProxyHandler"/>.
239
- /// These methods will be set into the shared vtable and invoked by WinRT from the object passed to it as an interface.
240
- /// </summary>
241
- private static class Impl
242
- {
243
- /// <summary>
244
- /// Implements <c>IUnknown.QueryInterface(REFIID, void**)</c>.
245
- /// </summary>
246
- [ UnmanagedCallersOnly ]
247
- public static int QueryInterface ( DispatcherQueueProxyHandler * @this , Guid * riid , void * * ppvObject )
248
- {
249
- if ( riid ->Equals ( IUnknown ) ||
250
- riid ->Equals ( IAgileObject ) ||
251
- riid ->Equals ( IDispatcherQueueHandler ) )
252
- {
253
- @this ->AddRef ( ) ;
254
-
255
- * ppvObject = @this ;
256
-
257
- return S_OK ;
258
- }
259
-
260
- return E_NOINTERFACE ;
261
- }
262
-
263
- /// <summary>
264
- /// Implements <c>IUnknown.AddRef()</c>.
265
- /// </summary>
266
- [ UnmanagedCallersOnly ]
267
- public static uint AddRef ( DispatcherQueueProxyHandler * @this )
268
- {
269
- return Interlocked . Increment ( ref @this ->referenceCount ) ;
270
- }
271
-
272
- /// <summary>
273
- /// Implements <c>IUnknown.Release()</c>.
274
- /// </summary>
275
- [ UnmanagedCallersOnly ]
276
- public static uint Release ( DispatcherQueueProxyHandler * @this )
277
- {
278
- uint referenceCount = Interlocked . Decrement ( ref @this ->referenceCount ) ;
279
-
280
- if ( referenceCount == 0 )
281
- {
282
- @this ->callbackHandle . Free ( ) ;
283
- @this ->stateHandle . Free ( ) ;
284
-
285
- Marshal . FreeHGlobal ( ( IntPtr ) @this ) ;
286
- }
287
-
288
- return referenceCount ;
289
- }
290
-
291
- /// <summary>
292
- /// Implements <c>IDispatcherQueueHandler.Invoke()</c>.
293
- /// </summary>
294
- [ UnmanagedCallersOnly ]
295
- public static int Invoke ( DispatcherQueueProxyHandler * @this )
296
- {
297
- object callback = @this ->callbackHandle . Target ! ;
298
- object state = @this ->stateHandle . Target ! ;
299
-
300
- try
301
- {
302
- // We do an unsafe cast here to treat the captured delegate as if the contravariant
303
- // input type was actually declared as covariant. This is valid because the type
304
- // parameter is constrained to be a reference type, and due to how the proxy handler
305
- // is constructed we know that the captured state will always match the actual type
306
- // of the captured handler at this point. This lets this whole method work without the
307
- // need to make the proxy type itself generic, so without knowing the actual type argument.
308
- Unsafe . As < DispatcherQueueHandler < object > > ( callback ) ( state ) ;
309
- }
310
- catch
311
- {
312
- }
313
-
314
- return S_OK ;
315
- }
316
- }
317
- }
318
101
}
319
102
}
0 commit comments