diff --git a/Dalamud/Service/LoadingDialog.cs b/Dalamud/Service/LoadingDialog.cs index f788ffb71..424087743 100644 --- a/Dalamud/Service/LoadingDialog.cs +++ b/Dalamud/Service/LoadingDialog.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; +using System.Threading.Tasks; using CheapLoc; @@ -31,15 +32,13 @@ namespace Dalamud; "StyleCop.CSharp.LayoutRules", "SA1519:Braces should not be omitted from multi-line child statement", Justification = "Multiple fixed blocks")] -internal sealed unsafe class LoadingDialog +internal sealed class LoadingDialog { private readonly RollingList logs = new(20); + private readonly TaskCompletionSource hwndTaskDialog = new(); private Thread? thread; - private HWND hwndTaskDialog; private DateTime firstShowTime; - private State currentState = State.LoadingDalamud; - private bool canHide; /// /// Enum representing the state of the dialog. @@ -72,35 +71,13 @@ public enum State /// /// Gets or sets the current state of the dialog. /// - public State CurrentState - { - get => this.currentState; - set - { - if (this.currentState == value) - return; - - this.currentState = value; - this.UpdateMainInstructionText(); - } - } + public State CurrentState { get; set; } = State.LoadingDalamud; /// /// Gets or sets a value indicating whether the dialog can be hidden by the user. /// /// Thrown if called before the dialog has been created. - public bool CanHide - { - get => this.canHide; - set - { - if (this.canHide == value) - return; - - this.canHide = value; - this.UpdateButtonEnabled(); - } - } + public bool CanHide { get; set; } /// /// Show the dialog. @@ -110,7 +87,7 @@ public void Show() if (IsGloballyHidden) return; - if (this.thread?.IsAlive == true) + if (this.thread is not null) return; this.thread = new Thread(this.ThreadStart) @@ -126,22 +103,28 @@ public void Show() /// /// Hide the dialog. /// - public void HideAndJoin() + /// A representing the asynchronous operation. + public async Task HideAndJoin() { IsGloballyHidden = true; - if (this.thread?.IsAlive is not true) + if (this.hwndTaskDialog.TrySetCanceled() || this.hwndTaskDialog.Task.IsCanceled) return; - SendMessageW(this.hwndTaskDialog, WM.WM_CLOSE, default, default); - this.thread.Join(); + try + { + SendMessageW(await this.hwndTaskDialog.Task, WM.WM_CLOSE, default, default); + } + catch (OperationCanceledException) + { + // ignore + } + + this.thread?.Join(); } - private void UpdateMainInstructionText() + private unsafe void UpdateMainInstructionText(HWND hwnd) { - if (this.hwndTaskDialog == default) - return; - - fixed (void* pszText = this.currentState switch + fixed (void* pszText = this.CurrentState switch { State.LoadingDalamud => Loc.Localize( "LoadingDialogMainInstructionLoadingDalamud", @@ -156,18 +139,15 @@ private void UpdateMainInstructionText() }) { SendMessageW( - this.hwndTaskDialog, + hwnd, (uint)TASKDIALOG_MESSAGES.TDM_SET_ELEMENT_TEXT, (WPARAM)(int)TASKDIALOG_ELEMENTS.TDE_MAIN_INSTRUCTION, (LPARAM)pszText); } } - private void UpdateContentText() + private unsafe void UpdateContentText(HWND hwnd) { - if (this.hwndTaskDialog == default) - return; - var contentBuilder = new StringBuilder( Loc.Localize( "LoadingDialogContentInfo", @@ -213,14 +193,14 @@ private void UpdateContentText() fixed (void* pszText = contentBuilder.ToString()) { SendMessageW( - this.hwndTaskDialog, + hwnd, (uint)TASKDIALOG_MESSAGES.TDM_SET_ELEMENT_TEXT, (WPARAM)(int)TASKDIALOG_ELEMENTS.TDE_CONTENT, (LPARAM)pszText); } } - private void UpdateExpandedInformation() + private unsafe void UpdateExpandedInformation(HWND hwnd) { const int maxCharactersPerLine = 80; @@ -261,57 +241,51 @@ private void UpdateExpandedInformation() fixed (void* pszText = sb.ToString()) { SendMessageW( - this.hwndTaskDialog, + hwnd, (uint)TASKDIALOG_MESSAGES.TDM_SET_ELEMENT_TEXT, (WPARAM)(int)TASKDIALOG_ELEMENTS.TDE_EXPANDED_INFORMATION, (LPARAM)pszText); } } - private void UpdateButtonEnabled() - { - if (this.hwndTaskDialog == default) - return; - - SendMessageW(this.hwndTaskDialog, (uint)TASKDIALOG_MESSAGES.TDM_ENABLE_BUTTON, IDOK, this.canHide ? 1 : 0); - } + private void UpdateButtonEnabled(HWND hwnd) => + SendMessageW(hwnd, (uint)TASKDIALOG_MESSAGES.TDM_ENABLE_BUTTON, IDOK, this.CanHide ? 1 : 0); private HRESULT TaskDialogCallback(HWND hwnd, uint msg, WPARAM wParam, LPARAM lParam) { switch ((TASKDIALOG_NOTIFICATIONS)msg) { case TASKDIALOG_NOTIFICATIONS.TDN_CREATED: - this.hwndTaskDialog = hwnd; + if (!this.hwndTaskDialog.TrySetResult(hwnd)) + return E.E_FAIL; - this.UpdateMainInstructionText(); - this.UpdateContentText(); - this.UpdateExpandedInformation(); - this.UpdateButtonEnabled(); + this.UpdateMainInstructionText(hwnd); + this.UpdateContentText(hwnd); + this.UpdateExpandedInformation(hwnd); + this.UpdateButtonEnabled(hwnd); SendMessageW(hwnd, (int)TASKDIALOG_MESSAGES.TDM_SET_PROGRESS_BAR_MARQUEE, 1, 0); // Bring to front + ShowWindow(hwnd, SW.SW_SHOW); SetWindowPos(hwnd, HWND.HWND_TOPMOST, 0, 0, 0, 0, SWP.SWP_NOSIZE | SWP.SWP_NOMOVE); SetWindowPos(hwnd, HWND.HWND_NOTOPMOST, 0, 0, 0, 0, SWP.SWP_NOSIZE | SWP.SWP_NOMOVE); - ShowWindow(hwnd, SW.SW_SHOW); SetForegroundWindow(hwnd); SetFocus(hwnd); SetActiveWindow(hwnd); return S.S_OK; - case TASKDIALOG_NOTIFICATIONS.TDN_DESTROYED: - this.hwndTaskDialog = default; - return S.S_OK; - case TASKDIALOG_NOTIFICATIONS.TDN_TIMER: - this.UpdateContentText(); - this.UpdateExpandedInformation(); + this.UpdateMainInstructionText(hwnd); + this.UpdateContentText(hwnd); + this.UpdateExpandedInformation(hwnd); + this.UpdateButtonEnabled(hwnd); return S.S_OK; } return S.S_OK; } - private void ThreadStart() + private unsafe void ThreadStart() { // We don't have access to the asset service here. var workingDirectory = Service.Get().StartInfo.WorkingDirectory; @@ -386,7 +360,7 @@ private void ThreadStart() gch = GCHandle.Alloc((Func)this.TaskDialogCallback); taskDialogConfig.lpCallbackData = GCHandle.ToIntPtr(gch); - TaskDialogIndirect(&taskDialogConfig, null, null, null).ThrowOnError(); + TaskDialogIndirect(&taskDialogConfig, null, null, null); } catch (Exception e) { diff --git a/Dalamud/Service/ServiceManager.cs b/Dalamud/Service/ServiceManager.cs index 29016bc69..206b24736 100644 --- a/Dalamud/Service/ServiceManager.cs +++ b/Dalamud/Service/ServiceManager.cs @@ -280,11 +280,8 @@ await WaitWithTimeoutConsent( Log.Error(e, "Failed resolving blocking services"); } - finally - { - loadingDialog.HideAndJoin(); - } + await loadingDialog.HideAndJoin(); return; async Task WaitWithTimeoutConsent(IEnumerable tasksEnumerable, LoadingDialog.State state) @@ -414,13 +411,14 @@ async Task WaitWithTimeoutConsent(IEnumerable tasksEnumerable, LoadingDial try { BlockingServicesLoadedTaskCompletionSource.SetException(e); - loadingDialog.HideAndJoin(); } catch (Exception) { // don't care, as this means task result/exception has already been set } + await loadingDialog.HideAndJoin(); + while (tasks.Any()) { await Task.WhenAny(tasks);