private static void UiThreadStart() { // Keep the window in the foreground. // Sometimes, when debugging in Visual Studio, the window sits in the background, // unnoticed, and prevents the application to shut down because it's not a background // thread. Setting TopMost again after a short while helps to bring it in the foreground // again. System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); timer.Tick += delegate(object sender, EventArgs args) { // Just to be sure, also set Visible, it doesn't hurt currentInstance.Visible = true; currentInstance.TopMost = true; // Come back, but not so soon timer.Interval *= 2; }; timer.Interval = 100; timer.Start(); Application.Run(currentInstance); // The window has been closed and the message loop was left. Should there still be a // timer event scheduled, it won't be executed anymore. Now clean up everything. timer.Stop(); timer.Dispose(); lock (syncLock) { currentInstance.Dispose(); currentInstance = null; } }
/// <summary> /// Shows the application error dialog. This is the only method that is called to show or /// update an error dialog. If a dialog is already open, the error is added to it. /// </summary> /// <param name="canContinue">Indicates whether the application can continue.</param> /// <param name="errorMsg">The error message to display.</param> /// <param name="ex">The <see cref="Exception"/> instance to display as details object.</param> /// <param name="terminateTimerEnabled">Indicates whether the termination safety timer has been started.</param> public static void ShowError(bool canContinue, string errorMsg, object ex, bool terminateTimerEnabled) { lock (syncLock) { try { if (currentInstance == null) { currentInstance = new AppErrorDialog(); currentInstance.SetCanContinue(canContinue); currentInstance.errorLabel.Text = errorMsg; currentInstance.grid.SelectedObject = ex; currentInstance.detailsLabel.Enabled = ex != null; if (terminateTimerEnabled) { currentInstance.EnableTerminateTimer(); } // Source: http://stackoverflow.com/a/3992635/143684 uiThread = new Thread(UiThreadStart); uiThread.Name = "FieldLog.AppErrorDialogUIThread"; uiThread.SetApartmentState(ApartmentState.STA); uiThread.Start(); } else { // Add next error to existing dialog // Wait until the window handle is created int count = 0; while (!currentInstance.IsHandleCreated) { if (count++ > 500) { throw new TimeoutException("Application error dialog was not created in reasonable time."); } Thread.Sleep(10); } currentInstance.Invoke(new AddErrorDelegate(currentInstance.AddError), canContinue, errorMsg, ex, terminateTimerEnabled); } } catch (Exception ex2) { FL.Critical(ex2, "FieldLog.Showing AppErrorDialog", false); FL.Flush(); MessageBox.Show( "Error showing the application error dialog. Details should be logged.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // Make sure we won't continue in this thread if it's not possible while (!canContinue) { Thread.Sleep(1000000); } // Slow down or halt the application as long as there are many pending errors. // The error dialog runs in its own thread so it will still respond to user input. :-) // (Unless, of course, should an error occur in the error dialog…) if (currentInstance != null && currentInstance.GetNextErrorsCount() >= 20) { Thread.Sleep(1000); } while (currentInstance != null && currentInstance.GetNextErrorsCount() >= 40) { Thread.Sleep(1000); } }
private static void UiThreadStart() { // Keep the window in the foreground. // Sometimes, when debugging in Visual Studio, the window sits in the background, // unnoticed, and prevents the application to shut down because it's not a background // thread. Setting TopMost again after a short while helps to bring it in the foreground // again. System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); timer.Tick += delegate (object sender, EventArgs args) { // Just to be sure, also set Visible, it doesn't hurt currentInstance.Visible = true; currentInstance.TopMost = true; // Come back, but not so soon timer.Interval *= 2; }; timer.Interval = 100; timer.Start(); Application.Run(currentInstance); // The window has been closed and the message loop was left. Should there still be a // timer event scheduled, it won't be executed anymore. Now clean up everything. timer.Stop(); timer.Dispose(); lock (syncLock) { currentInstance.Dispose(); currentInstance = null; } }
/// <summary> /// Shows the application error dialog. This is the only method that is called to show or /// update an error dialog. If a dialog is already open, the error is added to it. /// </summary> /// <param name="canContinue">Indicates whether the application can continue.</param> /// <param name="errorMsg">The error message to display.</param> /// <param name="ex">The <see cref="Exception"/> instance to display as details object.</param> /// <param name="terminateTimerEnabled">Indicates whether the termination safety timer has been started.</param> public static void ShowError(bool canContinue, string errorMsg, object ex, bool terminateTimerEnabled) { lock (syncLock) { try { if (currentInstance == null) { currentInstance = new AppErrorDialog(); currentInstance.SetCanContinue(canContinue); currentInstance.errorLabel.Text = errorMsg; currentInstance.grid.SelectedObject = ex; currentInstance.detailsLabel.Enabled = ex != null; if (terminateTimerEnabled) { currentInstance.EnableTerminateTimer(); } // Source: http://stackoverflow.com/a/3992635/143684 uiThread = new Thread(UiThreadStart); uiThread.Name = "FieldLog.AppErrorDialogUIThread"; uiThread.SetApartmentState(ApartmentState.STA); uiThread.Start(); } else { // Add next error to existing dialog // Wait until the window handle is created int count = 0; while (!currentInstance.IsHandleCreated) { if (count++ > 500) throw new TimeoutException("Application error dialog was not created in reasonable time."); Thread.Sleep(10); } currentInstance.Invoke(new AddErrorDelegate(currentInstance.AddError), canContinue, errorMsg, ex, terminateTimerEnabled); } } catch (Exception ex2) { FL.Critical(ex2, "FieldLog.Showing AppErrorDialog", false); FL.Flush(); MessageBox.Show( "Error showing the application error dialog. Details should be logged.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // Make sure we won't continue in this thread if it's not possible while (!canContinue) { Thread.Sleep(1000000); } // Slow down or halt the application as long as there are many pending errors. // The error dialog runs in its own thread so it will still respond to user input. :-) // (Unless, of course, should an error occur in the error dialog…) if (currentInstance != null && currentInstance.GetNextErrorsCount() >= 20) { Thread.Sleep(1000); } while (currentInstance != null && currentInstance.GetNextErrorsCount() >= 40) { Thread.Sleep(1000); } }