I have a method that doing Chromium browser initialization. After running two asynchronous method, the program automatically close. Did I use the Task.WhenAll
wrong?
Here is the entry point of the program:
Start.cs
static class Start
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Initializer.Start();
}
}
Initializer.cs
public static class Initializer
{
public static async void Start()
{
// The task that automatically close the program.
Task<bool> chromiumInitTask = ChromiumInfo.InitializeAsync();
await chromiumInitTask;
Task webviewInitTask = WebViewInfo.InitializeAsync();
Task guiInitTask = GUIInfo.InitializeAsync();
HardwareManager.Initialize();
await webviewInitTask;
await guiInitTask;
GUIInfo.Layout.ChangeMainDisplay(ChromiumInfo.Browser);
Application.Run(GUIInfo.Layout.GetLayoutForm());
}
}
ChromiumInfo.cs
public static class ChromiumInfo
{
private static CefSettings _settings;
private static ChromiumWebBrowser _browser;
private static BrowserSettings _browserSettings;
private static Dictionary<string, bool> _initTasks = new Dictionary<string, bool>()
{
{ "Settings", false },
{ "Browser Settings", false },
{ "Browser", false },
{ "Cef", false }
};
private static KeyboardHandler _keyboardHandler;
/// <summary>
/// An setting which using on initialize.
/// </summary>
public static CefSettings Setting => _settings;
/// <summary>
/// Representing a browser which can be show in UI.
/// </summary>
public static ChromiumWebBrowser Browser => _browser;
/// <summary>
/// A keyboard handler which handle various keyboard events.
/// </summary>
public static KeyboardHandler KeyboardHandler => _keyboardHandler;
/// <summary>
/// Occur when request to change the browser.
/// </summary>
public static event RequestChangeBrowserEventHandler RequestChangeBrowser;
/// <summary>
/// Initialize all Chromium components in asynchronously.
/// </summary>
/// <param name="initAllScripts">Indicate should initialize all scripts contain in the root script directory.</param>
/// <returns>Return a task can be awaited. True means sucess. Otherwise, return false.</returns>
public static async Task<bool> InitializeAsync(bool initAllScripts = true)
{
Task settingInit = SettingsInitializeAsync();
Task browserSettingInit = BrowserSettingsInitializeAsync();
// The below line that automatically close the program.
await Task.WhenAll(settingInit, browserSettingInit);
Task cefInit = Cef.InitializeAsync(_settings);
await cefInit;
_initTasks["Cef"] = true;
Task browserInit = BrowserInitializeAsync();
await browserInit;
Task eventInit = EventInitializeAsync();
await eventInit;
Task scriptInit = ScriptInitializeAsync();
await scriptInit;
return _initTasks.Values.Where(it => it).Count() == _initTasks.Count;
}
private static async Task SettingsInitializeAsync()
{
try
{
_settings = new CefSettings();
_settings.CommandLineArgsDisabled = false;
_settings.CefCommandLineArgs.Clear();
_settings.CefCommandLineArgs.Add("enable-3d-apis", "1");
_settings.CefCommandLineArgs.Add("enable-webgl-draft-extensions", "1");
_settings.CefCommandLineArgs.Add("enable-gpu", "1");
_settings.CefCommandLineArgs.Add("enable-webgl", "1");
_settings.CefCommandLineArgs.Add("gpu_compositing", "1");
_settings.CefCommandLineArgs.Add("ignore-gpu-blocklist", "1");
await Task.Delay(1000).ConfigureAwait(false);
_initTasks["Settings"] = true;
}
catch (Exception e)
{
SystemLog.Write(e);
}
}
private static async Task BrowserSettingsInitializeAsync()
{
try
{
_browserSettings = new BrowserSettings();
_browserSettings.WebGl = CefState.Enabled;
await Task.Delay(1000).ConfigureAwait(false);
_initTasks["Browser Settings"] = true;
}
catch (Exception e)
{
SystemLog.Write(e);
}
}
private static async Task BrowserInitializeAsync()
{
try
{
_browser = new ChromiumWebBrowser(Properties.Settings.Default.DefaultURL);
_browser.BrowserSettings = _browserSettings;
_browser.Dock = System.Windows.Forms.DockStyle.Fill;
await Task.Delay(1000).ConfigureAwait(false);
_initTasks["Browser"] = true;
}
catch (Exception e)
{
SystemLog.Write(e);
}
}
private static async Task EventInitializeAsync()
{
KeyboardHandler keyboardHandler = new KeyboardHandler();
WebCommandHandler commandHandler = new WebCommandHandler();
_browser.ConsoleMessage = keyboardHandler.Handle;
_browser.ConsoleMessage = commandHandler.Handle;
_browser.AddressChanged = Custom_AddressChanged;
_keyboardHandler = keyboardHandler;
await Task.Delay(1000).ConfigureAwait(false);
}
private static async Task ScriptInitializeAsync()
{
string scriptPath = $@"{ProgramInfo.RootPath}\scripts";
if (Directory.Exists(scriptPath))
{
var files = Directory.GetFiles(scriptPath, "*.js");
files?.ToList().ForEach(f => _browser.GetMainFrame().ExecuteJavaScriptAsync(f, _browser.Address));
}
await Task.Delay(1000).ConfigureAwait(false);
}
private static void Custom_AddressChanged(object sender, AddressChangedEventArgs e)
{
var wv2SiteCount = Properties.Settings.Default.WebToWV2.Cast<string>()
.Where(s => s.IndexOf(e.Address) >= 0).Count();
if (wv2SiteCount > 0)
{
WebViewInfo.Navigate(e.Address);
RequestChangeBrowser?.Invoke(null, new RequestChangeBrowserEventArgs(WebViewInfo.Browser));
}
}
}
CodePudding user response:
This is because of how await
works. When await
acts on a Task
that is incomplete, it returns. So your program is working like this:
Main
runs, and callsInitializer.Start()
Initializer.Start()
runs and callsChromiumInfo.InitializeAsync()
ChromiumInfo.InitializeAsync()
runs until it callsawait Task.WhenAll(settingInit, browserSettingInit)
- Because
Task.WhenAll
returns an incompleteTask
,ChromiumInfo.InitializeAsync()
returns its own incompleteTask
and execution returns toInitializer.Start()
. - The
await
inInitializer.Start()
sees the incompleteTask
and returns its own incompleteTask
and execution returns toMain()
. - Because
Main()
doesn't act on theTask
returned byInitializer.Start()
, execution continues to the next line, which is the end of the program.
The solution is fairly simple: Change your Main
method to async
and use await
.
public static async Task Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
await Initializer.Start();
}
The ability to make Main()
async
is a feature introduced in C# 7.1.