Skip to content

Commit 6551d55

Browse files
committed
Added DispatcherQueueProxyHandler2 type
1 parent 627110e commit 6551d55

File tree

3 files changed

+321
-33
lines changed

3 files changed

+321
-33
lines changed

CommunityToolkit.WinUI/Extensions/DispatcherQueueExtensions{T}.cs

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,21 @@ namespace CommunityToolkit.WinUI
1414
/// <summary>
1515
/// A callback that will be executed on the <see cref="DispatcherQueue"/> thread.
1616
/// </summary>
17-
/// <typeparam name="TState">The type of state to receive as input.</typeparam>
17+
/// <typeparam name="T">The type of state to receive as input.</typeparam>
1818
/// <param name="state">The input state for the callback.</param>
19-
public delegate void DispatcherQueueHandler<in TState>(TState state)
20-
where TState : class;
19+
public delegate void DispatcherQueueHandler<in T>(T state)
20+
where T : class;
21+
22+
/// <summary>
23+
/// A callback that will be executed on the <see cref="DispatcherQueue"/> thread.
24+
/// </summary>
25+
/// <typeparam name="T1">The type of the first state to receive as input.</typeparam>
26+
/// <typeparam name="T2">The type of the second state to receive as input.</typeparam>
27+
/// <param name="state1">The first input state for the callback.</param>
28+
/// <param name="state2">The second input state for the callback.</param>
29+
public delegate void DispatcherQueueHandler<in T1, in T2>(T1 state1, T2 state2)
30+
where T1 : class
31+
where T2 : class;
2132

2233
/// <summary>
2334
/// Helpers for executing code in a <see cref="DispatcherQueue"/>.
@@ -27,17 +38,17 @@ public static partial class DispatcherQueueExtensions
2738
/// <summary>
2839
/// Adds a task to the <see cref="DispatcherQueue"/> which will be executed on the thread associated with it.
2940
/// </summary>
30-
/// <typeparam name="TState">The type of state to capture.</typeparam>
41+
/// <typeparam name="T">The type of state to capture.</typeparam>
3142
/// <param name="dispatcherQueue">The target <see cref="DispatcherQueue"/> to invoke the code on.</param>
32-
/// <param name="callback">The input <see cref="DispatcherQueueHandler{TState}"/> callback to enqueue.</param>
43+
/// <param name="callback">The input <see cref="DispatcherQueueHandler{T}"/> callback to enqueue.</param>
3344
/// <param name="state">The input state to capture and pass to the callback.</param>
3445
/// <returns>Whether or not the task was added to the queue.</returns>
3546
/// <exception cref="Exception">Thrown when the enqueue operation fails.</exception>
36-
public static unsafe bool TryEnqueue<TState>(this DispatcherQueue dispatcherQueue, DispatcherQueueHandler<TState> callback, TState state)
37-
where TState : class
47+
public static unsafe bool TryEnqueue<T>(this DispatcherQueue dispatcherQueue, DispatcherQueueHandler<T> callback, T state)
48+
where T : class
3849
{
3950
IDispatcherQueue* dispatcherQueuePtr = (IDispatcherQueue*)((IWinRTObject)dispatcherQueue).NativeObject.ThisPtr;
40-
DispatcherQueueProxyHandler* dispatcherQueueHandlerPtr = DispatcherQueueProxyHandler.Create(callback, state);
51+
DispatcherQueueProxyHandler1* dispatcherQueueHandlerPtr = DispatcherQueueProxyHandler1.Create(callback, state);
4152

4253
bool success;
4354
int hResult;
@@ -64,18 +75,99 @@ public static unsafe bool TryEnqueue<TState>(this DispatcherQueue dispatcherQueu
6475
/// <summary>
6576
/// Adds a task to the <see cref="DispatcherQueue"/> which will be executed on the thread associated with it.
6677
/// </summary>
67-
/// <typeparam name="TState">The type of state to capture.</typeparam>
78+
/// <typeparam name="T">The type of state to capture.</typeparam>
6879
/// <param name="dispatcherQueue">The target <see cref="DispatcherQueue"/> to invoke the code on.</param>
6980
/// <param name="priority"> The desired priority for the callback to schedule.</param>
70-
/// <param name="callback">The input <see cref="DispatcherQueueHandler{TState}"/> callback to enqueue.</param>
81+
/// <param name="callback">The input <see cref="DispatcherQueueHandler{T}"/> callback to enqueue.</param>
7182
/// <param name="state">The input state to capture and pass to the callback.</param>
7283
/// <returns>Whether or not the task was added to the queue.</returns>
7384
/// <exception cref="Exception">Thrown when the enqueue operation fails.</exception>
74-
public static unsafe bool TryEnqueue<TState>(this DispatcherQueue dispatcherQueue, DispatcherQueuePriority priority, DispatcherQueueHandler<TState> callback, TState state)
75-
where TState : class
85+
public static unsafe bool TryEnqueue<T>(this DispatcherQueue dispatcherQueue, DispatcherQueuePriority priority, DispatcherQueueHandler<T> callback, T state)
86+
where T : class
87+
{
88+
IDispatcherQueue* dispatcherQueuePtr = (IDispatcherQueue*)((IWinRTObject)dispatcherQueue).NativeObject.ThisPtr;
89+
DispatcherQueueProxyHandler1* dispatcherQueueHandlerPtr = DispatcherQueueProxyHandler1.Create(callback, state);
90+
91+
bool success;
92+
int hResult;
93+
94+
try
95+
{
96+
hResult = dispatcherQueuePtr->TryEnqueueWithPriority(priority, dispatcherQueueHandlerPtr, (byte*)&success);
97+
98+
GC.KeepAlive(dispatcherQueue);
99+
}
100+
finally
101+
{
102+
dispatcherQueueHandlerPtr->Release();
103+
}
104+
105+
if (hResult != 0)
106+
{
107+
ExceptionHelpers.ThrowExceptionForHR(hResult);
108+
}
109+
110+
return success;
111+
}
112+
113+
/// <summary>
114+
/// Adds a task to the <see cref="DispatcherQueue"/> which will be executed on the thread associated with it.
115+
/// </summary>
116+
/// <typeparam name="T1">The type of the first state to capture.</typeparam>
117+
/// <typeparam name="T2">The type of the second state to capture.</typeparam>
118+
/// <param name="dispatcherQueue">The target <see cref="DispatcherQueue"/> to invoke the code on.</param>
119+
/// <param name="callback">The input <see cref="DispatcherQueueHandler{T}"/> callback to enqueue.</param>
120+
/// <param name="state1">The first input state to capture and pass to the callback.</param>
121+
/// <param name="state2">The second input state to capture and pass to the callback.</param>
122+
/// <returns>Whether or not the task was added to the queue.</returns>
123+
/// <exception cref="Exception">Thrown when the enqueue operation fails.</exception>
124+
public static unsafe bool TryEnqueue<T1, T2>(this DispatcherQueue dispatcherQueue, DispatcherQueueHandler<T1, T2> callback, T1 state1, T2 state2)
125+
where T1 : class
126+
where T2 : class
127+
{
128+
IDispatcherQueue* dispatcherQueuePtr = (IDispatcherQueue*)((IWinRTObject)dispatcherQueue).NativeObject.ThisPtr;
129+
DispatcherQueueProxyHandler2* dispatcherQueueHandlerPtr = DispatcherQueueProxyHandler2.Create(callback, state1, state2);
130+
131+
bool success;
132+
int hResult;
133+
134+
try
135+
{
136+
hResult = dispatcherQueuePtr->TryEnqueue(dispatcherQueueHandlerPtr, (byte*)&success);
137+
138+
GC.KeepAlive(dispatcherQueue);
139+
}
140+
finally
141+
{
142+
dispatcherQueueHandlerPtr->Release();
143+
}
144+
145+
if (hResult != 0)
146+
{
147+
ExceptionHelpers.ThrowExceptionForHR(hResult);
148+
}
149+
150+
return success;
151+
}
152+
153+
/// <summary>
154+
/// Adds a task to the <see cref="DispatcherQueue"/> which will be executed on the thread associated with it.
155+
/// </summary>
156+
/// <typeparam name="T1">The type of the first state to capture.</typeparam>
157+
/// <typeparam name="T2">The type of the second state to capture.</typeparam>
158+
/// <param name="dispatcherQueue">The target <see cref="DispatcherQueue"/> to invoke the code on.</param>
159+
/// <param name="priority"> The desired priority for the callback to schedule.</param>
160+
/// <param name="callback">The input <see cref="DispatcherQueueHandler{T}"/> callback to enqueue.</param>
161+
/// <param name="state1">The first input state to capture and pass to the callback.</param>
162+
/// <param name="state2">The second input state to capture and pass to the callback.</param>
163+
/// <returns>Whether or not the task was added to the queue.</returns>
164+
/// <exception cref="Exception">Thrown when the enqueue operation fails.</exception>
165+
public static unsafe bool TryEnqueue<T1, T2>(this DispatcherQueue dispatcherQueue, DispatcherQueuePriority priority, DispatcherQueueHandler<T1, T2> callback, T1 state1, T2 state2)
166+
where T1 : class
167+
where T2 : class
76168
{
77169
IDispatcherQueue* dispatcherQueuePtr = (IDispatcherQueue*)((IWinRTObject)dispatcherQueue).NativeObject.ThisPtr;
78-
DispatcherQueueProxyHandler* dispatcherQueueHandlerPtr = DispatcherQueueProxyHandler.Create(callback, state);
170+
DispatcherQueueProxyHandler2* dispatcherQueueHandlerPtr = DispatcherQueueProxyHandler2.Create(callback, state1, state2);
79171

80172
bool success;
81173
int hResult;

CommunityToolkit.WinUI/Extensions/Interop/DispatcherQueueProxyHandler.cs renamed to CommunityToolkit.WinUI/Extensions/Interop/DispatcherQueueProxyHandler1.cs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,30 @@ namespace CommunityToolkit.WinUI.Interop
1818
/// A custom <c>IDispatcherQueueHandler</c> object, that internally stores a captured <see cref="DispatcherQueueHandler{TState}"/> instance
1919
/// and the input captured state. This allows consumers to enqueue a state and a cached stateless delegate without any managed allocations.
2020
/// </summary>
21-
internal unsafe struct DispatcherQueueProxyHandler
21+
internal unsafe struct DispatcherQueueProxyHandler1
2222
{
2323
/// <summary>
24-
/// The shared vtable pointer for <see cref="DispatcherQueueProxyHandler"/> instances.
24+
/// The shared vtable pointer for <see cref="DispatcherQueueProxyHandler1"/> instances.
2525
/// </summary>
2626
private static readonly void** Vtbl = InitVtbl();
2727

2828
/// <summary>
29-
/// Setups the vtable pointer for <see cref="DispatcherQueueProxyHandler"/>.
29+
/// Setups the vtable pointer for <see cref="DispatcherQueueProxyHandler1"/>.
3030
/// </summary>
31-
/// <returns>The initialized vtable pointer for <see cref="DispatcherQueueProxyHandler"/>.</returns>
31+
/// <returns>The initialized vtable pointer for <see cref="DispatcherQueueProxyHandler1"/>.</returns>
3232
/// <remarks>
3333
/// The vtable itself is allocated with <see cref="RuntimeHelpers.AllocateTypeAssociatedMemory(Type, int)"/>,
3434
/// which allocates memory in the high frequency heap associated with the input runtime type. This will be
3535
/// automatically cleaned up when the type is unloaded, so there is no need to ever manually free this memory.
3636
/// </remarks>
3737
private static void** InitVtbl()
3838
{
39-
void** lpVtbl = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DispatcherQueueProxyHandler), sizeof(void*) * 4);
39+
void** lpVtbl = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DispatcherQueueProxyHandler1), sizeof(void*) * 4);
4040

41-
lpVtbl[0] = (delegate* unmanaged<DispatcherQueueProxyHandler*, Guid*, void**, int>)&Impl.QueryInterface;
42-
lpVtbl[1] = (delegate* unmanaged<DispatcherQueueProxyHandler*, uint>)&Impl.AddRef;
43-
lpVtbl[2] = (delegate* unmanaged<DispatcherQueueProxyHandler*, uint>)&Impl.Release;
44-
lpVtbl[3] = (delegate* unmanaged<DispatcherQueueProxyHandler*, int>)&Impl.Invoke;
41+
lpVtbl[0] = (delegate* unmanaged<DispatcherQueueProxyHandler1*, Guid*, void**, int>)&Impl.QueryInterface;
42+
lpVtbl[1] = (delegate* unmanaged<DispatcherQueueProxyHandler1*, uint>)&Impl.AddRef;
43+
lpVtbl[2] = (delegate* unmanaged<DispatcherQueueProxyHandler1*, uint>)&Impl.Release;
44+
lpVtbl[3] = (delegate* unmanaged<DispatcherQueueProxyHandler1*, int>)&Impl.Invoke;
4545

4646
return lpVtbl;
4747
}
@@ -67,16 +67,14 @@ internal unsafe struct DispatcherQueueProxyHandler
6767
private volatile uint referenceCount;
6868

6969
/// <summary>
70-
/// Creates a new <see cref="DispatcherQueueProxyHandler"/> instance for the input callback and state.
70+
/// Creates a new <see cref="DispatcherQueueProxyHandler1"/> instance for the input callback and state.
7171
/// </summary>
72-
/// <typeparam name="TState">The type of state to capture.</typeparam>
7372
/// <param name="handler">The input <see cref="DispatcherQueueHandler{TState}"/> callback to enqueue.</param>
7473
/// <param name="state">The input state to capture and pass to the callback.</param>
75-
/// <returns>A pointer to the newly initialized <see cref="DispatcherQueueProxyHandler"/> instance.</returns>
76-
public static DispatcherQueueProxyHandler* Create<TState>(DispatcherQueueHandler<TState> handler, TState state)
77-
where TState : class
74+
/// <returns>A pointer to the newly initialized <see cref="DispatcherQueueProxyHandler1"/> instance.</returns>
75+
public static DispatcherQueueProxyHandler1* Create(object handler, object state)
7876
{
79-
DispatcherQueueProxyHandler* @this = (DispatcherQueueProxyHandler*)Marshal.AllocHGlobal(sizeof(DispatcherQueueProxyHandler));
77+
DispatcherQueueProxyHandler1* @this = (DispatcherQueueProxyHandler1*)Marshal.AllocHGlobal(sizeof(DispatcherQueueProxyHandler1));
8078

8179
@this->lpVtbl = Vtbl;
8280
@this->callbackHandle = GCHandle.Alloc(handler);
@@ -99,14 +97,17 @@ public uint Release()
9997

10098
if (referenceCount == 0)
10199
{
100+
callbackHandle.Free();
101+
stateHandle.Free();
102+
102103
Marshal.FreeHGlobal((IntPtr)Unsafe.AsPointer(ref this));
103104
}
104105

105106
return referenceCount;
106107
}
107108

108109
/// <summary>
109-
/// A private type with the implementation of the unmanaged methods for <see cref="DispatcherQueueProxyHandler"/>.
110+
/// A private type with the implementation of the unmanaged methods for <see cref="DispatcherQueueProxyHandler1"/>.
110111
/// These methods will be set into the shared vtable and invoked by WinRT from the object passed to it as an interface.
111112
/// </summary>
112113
private static class Impl
@@ -115,7 +116,7 @@ private static class Impl
115116
/// Implements <c>IUnknown.QueryInterface(REFIID, void**)</c>.
116117
/// </summary>
117118
[UnmanagedCallersOnly]
118-
public static int QueryInterface(DispatcherQueueProxyHandler* @this, Guid* riid, void** ppvObject)
119+
public static int QueryInterface(DispatcherQueueProxyHandler1* @this, Guid* riid, void** ppvObject)
119120
{
120121
if (riid->Equals(IUnknown) ||
121122
riid->Equals(IAgileObject) ||
@@ -135,7 +136,7 @@ public static int QueryInterface(DispatcherQueueProxyHandler* @this, Guid* riid,
135136
/// Implements <c>IUnknown.AddRef()</c>.
136137
/// </summary>
137138
[UnmanagedCallersOnly]
138-
public static uint AddRef(DispatcherQueueProxyHandler* @this)
139+
public static uint AddRef(DispatcherQueueProxyHandler1* @this)
139140
{
140141
return Interlocked.Increment(ref @this->referenceCount);
141142
}
@@ -144,7 +145,7 @@ public static uint AddRef(DispatcherQueueProxyHandler* @this)
144145
/// Implements <c>IUnknown.Release()</c>.
145146
/// </summary>
146147
[UnmanagedCallersOnly]
147-
public static uint Release(DispatcherQueueProxyHandler* @this)
148+
public static uint Release(DispatcherQueueProxyHandler1* @this)
148149
{
149150
uint referenceCount = Interlocked.Decrement(ref @this->referenceCount);
150151

@@ -163,7 +164,7 @@ public static uint Release(DispatcherQueueProxyHandler* @this)
163164
/// Implements <c>IDispatcherQueueHandler.Invoke()</c>.
164165
/// </summary>
165166
[UnmanagedCallersOnly]
166-
public static int Invoke(DispatcherQueueProxyHandler* @this)
167+
public static int Invoke(DispatcherQueueProxyHandler1* @this)
167168
{
168169
object callback = @this->callbackHandle.Target!;
169170
object state = @this->stateHandle.Target!;

0 commit comments

Comments
 (0)