-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathMainThread.cs
More file actions
184 lines (161 loc) · 4.62 KB
/
Copy pathMainThread.cs
File metadata and controls
184 lines (161 loc) · 4.62 KB
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
using System;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace BinaryNinja
{
internal static partial class NativeDelegates
{
[UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
internal delegate void BNMainThreadActionCallback(IntPtr context);
}
/// <summary>
/// Integrates an application event loop with Binary Ninja main-thread actions.
/// </summary>
public abstract class MainThreadActionHandler
{
private static MainThreadActionHandler? registeredHandler;
private readonly NativeDelegates.BNMainThreadAddAction addActionCallback;
protected MainThreadActionHandler()
{
this.addActionCallback = new NativeDelegates.BNMainThreadAddAction(
this.InvokeAddAction
);
}
/// <summary>Registers this handler as the application's main-thread dispatcher.</summary>
public void Register()
{
BNMainThreadCallbacks callbacks = new BNMainThreadCallbacks();
callbacks.context = IntPtr.Zero;
callbacks.addAction = Marshal.GetFunctionPointerForDelegate(
this.addActionCallback
);
MainThreadActionHandler.registeredHandler = this;
NativeMethods.BNRegisterMainThread(in callbacks);
}
/// <summary>Adds an action to the application's main-thread event queue.</summary>
public abstract void AddMainThreadAction(MainThreadAction action);
private void InvokeAddAction(IntPtr context, IntPtr actionHandle)
{
try
{
MainThreadAction action = MainThreadAction.MustTakeHandle(actionHandle);
this.AddMainThreadAction(action);
}
catch (Exception exception)
{
Core.LogError("Unhandled exception in main-thread action handler: {0}", exception);
}
}
}
public static partial class Core
{
private sealed class MainThreadInvocation
{
internal MainThreadInvocation(Action action, bool waitsForCompletion)
{
this.Action = action;
this.WaitsForCompletion = waitsForCompletion;
}
internal Action Action { get; }
internal bool WaitsForCompletion { get; }
internal Exception? Exception { get; set; }
internal int ContextReleased;
}
private static readonly NativeDelegates.BNMainThreadActionCallback
mainThreadActionCallback = new NativeDelegates.BNMainThreadActionCallback(
Core.InvokeMainThreadAction
);
/// <summary>Schedules an action on the Binary Ninja main thread.</summary>
public static MainThreadAction? ExecuteOnMainThread(Action action)
{
if (null == action)
{
throw new ArgumentNullException(nameof(action));
}
MainThreadInvocation invocation = new MainThreadInvocation(action, false);
GCHandle context = GCHandle.Alloc(invocation);
IntPtr contextPointer = GCHandle.ToIntPtr(context);
IntPtr callbackPointer = Marshal.GetFunctionPointerForDelegate(
Core.mainThreadActionCallback
);
try
{
return MainThreadAction.TakeHandle(
NativeMethods.BNExecuteOnMainThread(contextPointer, callbackPointer)
);
}
catch
{
Core.ReleaseMainThreadInvocation(invocation, context);
throw;
}
}
/// <summary>Runs an action on the Binary Ninja main thread and waits for it.</summary>
public static void ExecuteOnMainThreadAndWait(Action action)
{
if (null == action)
{
throw new ArgumentNullException(nameof(action));
}
MainThreadInvocation invocation = new MainThreadInvocation(action, true);
GCHandle context = GCHandle.Alloc(invocation);
try
{
NativeMethods.BNExecuteOnMainThreadAndWait(
GCHandle.ToIntPtr(context),
Marshal.GetFunctionPointerForDelegate(Core.mainThreadActionCallback)
);
}
finally
{
Core.ReleaseMainThreadInvocation(invocation, context);
}
if (null != invocation.Exception)
{
ExceptionDispatchInfo.Capture(invocation.Exception).Throw();
}
}
private static void InvokeMainThreadAction(IntPtr contextPointer)
{
GCHandle context = GCHandle.FromIntPtr(contextPointer);
MainThreadInvocation? invocation = context.Target as MainThreadInvocation;
if (null == invocation)
{
return;
}
try
{
invocation.Action();
}
catch (Exception exception)
{
if (invocation.WaitsForCompletion)
{
invocation.Exception = exception;
}
else
{
Core.LogError("Unhandled exception in main-thread action: {0}", exception);
}
}
finally
{
if (!invocation.WaitsForCompletion)
{
Core.ReleaseMainThreadInvocation(invocation, context);
}
}
}
private static void ReleaseMainThreadInvocation(
MainThreadInvocation invocation,
GCHandle context
)
{
if (0 == Interlocked.Exchange(ref invocation.ContextReleased, 1))
{
context.Free();
}
}
}
}