况洋洋
2025-07-04 0d247bd2a17e0f99f3609774a1ce54ae00857997
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
// Copyright © 2020 The CefSharp Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
 
using System;
using System.Threading.Tasks;
 
namespace CefSharp.Internals
{
    /// <summary>
    /// To access the CEF threads we expose a TaskFactory, as this requires managed vc++ this
    /// exists in CefSharp.Core it cannot be directly accessed in CefSharp.dll. When
    /// Cef.Initialized is called we pass a reference to the TaskFactory here so we
    /// can write methods (typically extension methods) in this assembly.
    /// </summary>
    /// TODO: This can likely be removed and code that depends on this can be moved
    /// to CefSharp.Core and interact directly with the C++ api
    public static class CefThread
    {
        private static readonly object LockObj = new object();
 
        /// <summary>
        /// TaskFactory will be null before Cef.Initialize is called
        /// and null after Cef.Shutdown is called.
        /// </summary>
        public static TaskFactory UiThreadTaskFactory { get; private set; }
 
        /// <summary>
        /// Event fired after Cef.Initialze has been called, we can now start
        /// posting Tasks to the CEF UI Thread.
        /// </summary>
        public static event EventHandler Initialized;
 
        /// <summary>
        /// Delegate used to wrap the native call to CefCurrentlyOn(CefThreadId::TID_UI).
        /// </summary>
        public static Func<bool> CurrentOnUiThreadDelegate { get; private set; }
 
        /// <summary>
        /// true if we have a reference to the UiThreadTaskFactory
        /// TaskFactory, otherwise false
        /// </summary>
        /// <remarks>
        /// The current implementation isn't thread safe, generally speaking this shouldn't be a problem
        /// </remarks>
        public static bool CanExecuteOnUiThread
        {
            get
            {
                return UiThreadTaskFactory != null;
            }
        }
 
        /// <summary>
        /// Currently on the CEF UI Thread
        /// </summary>
        public static bool CurrentlyOnUiThread
        {
            get
            {
                var d = CurrentOnUiThreadDelegate;
                if (d == null)
                {
                    return false;
                }
 
                return d();
            }
        }
 
        /// <summary>
        /// returns true if Cef.Shutdown been called, otherwise false.
        /// </summary>
        public static bool HasShutdown { get; private set; }
 
        /// <summary>
        /// Execute the provided function on the CEF UI Thread
        /// </summary>
        /// <typeparam name="TResult">result</typeparam>
        /// <param name="function">function</param>
        /// <returns>Task{Result}</returns>
        public static Task<TResult> ExecuteOnUiThread<TResult>(Func<TResult> function)
        {
            lock (LockObj)
            {
                if (HasShutdown)
                {
                    throw new Exception("Cef.Shutdown has already been called, it's no longer possible to execute on the CEF UI Thread. Check CefThread.HasShutdown to guard against this execption");
                }
 
                var taskFactory = UiThreadTaskFactory;
 
                if (taskFactory == null)
                {
                    //We don't have a task factory yet, so we'll queue for execution.
                    return QueueForExcutionWhenUiThreadCreated(function);
                }
 
                return taskFactory.StartNew(function);
            }
        }
 
        /// <summary>
        /// Execute the provided action on the CEF UI Thread
        /// </summary>
        /// <param name="action">action</param>
        /// <returns>Task</returns>
        public static Task ExecuteOnUiThread(Action action)
        {
            lock (LockObj)
            {
                if (HasShutdown)
                {
                    throw new Exception("Cef.Shutdown has already been called, it's no longer possible to execute on the CEF UI Thread. Check CefThread.HasShutdown to guard against this execption");
                }
 
                var taskFactory = UiThreadTaskFactory;
 
                if (taskFactory == null)
                {
                    //We don't have a task factory yet, so we'll queue for execution.
                    return QueueForExcutionWhenUiThreadCreated(action);
                }
 
                return taskFactory.StartNew(action);
            }
        }
 
        /// <summary>
        /// Wait for CEF to Initialize, continuation happens on
        /// the CEF UI Thraed.
        /// </summary>
        /// <returns>Task that can be awaited</returns>
        private static Task QueueForExcutionWhenUiThreadCreated(Action action)
        {
            var tcs = new TaskCompletionSource<bool>();
 
            EventHandler handler = null;
 
            handler = (s, args) =>
            {
                Initialized -= handler;
 
                try
                {
                    //TODO: Should this call UiThreadTaskFactory.StartNew?
                    action();
 
                    tcs.TrySetResult(true);
                }
                catch(Exception ex)
                {
                    tcs.TrySetException(ex);
                }
            };
 
            Initialized += handler;
 
            return tcs.Task;
        }
 
        /// <summary>
        /// Wait for CEF to Initialize, continuation happens on
        /// the CEF UI Thraed.
        /// </summary>
        /// <returns>Task that can be awaited</returns>
        private static Task<T> QueueForExcutionWhenUiThreadCreated<T>(Func<T> func)
        {
            var tcs = new TaskCompletionSource<T>();
 
            EventHandler handler = null;
 
            handler = (s, args) =>
            {
                Initialized -= handler;
 
                try
                {
                    //TODO: Should this call UiThreadTaskFactory.StartNew?
                    var result = func();
 
                    tcs.TrySetResult(result);
                }
                catch(Exception ex)
                {
                    tcs.TrySetException(ex);
                }
            };
 
            Initialized += handler;
 
            return tcs.Task;
        }
 
        /// <summary>
        /// Called when the CEF UI Thread is a
        /// </summary>
        public static void Initialize(TaskFactory uiThreadTaskFactory, Func<bool> currentOnUiThreadDelegate)
        {
            lock (LockObj)
            {
                Initialized?.Invoke(null, EventArgs.Empty);
 
                UiThreadTaskFactory = uiThreadTaskFactory;
                CurrentOnUiThreadDelegate = currentOnUiThreadDelegate;
            }
        }
 
        /// <summary>
        /// !!WARNING!! DO NOT CALL THIS YOURSELF, THIS WILL BE CALLED INTERNALLY.
        /// Called when Cef.Shutdown is called to cleanup our references
        /// and release any event handlers.
        /// </summary>
        public static void Shutdown()
        {
            lock (LockObj)
            {
                CurrentOnUiThreadDelegate = null;
                Initialized = null;
                UiThreadTaskFactory = null;
                HasShutdown = true;
            }
        }
    }
}