THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Browse Source

Improve exception handler message and log

master
Lukas Langrock 2 months ago
parent
commit
9815016034
6 changed files with 72 additions and 8 deletions
  1. + 1
    - 1
      README.md
  2. + 20
    - 1
      Shutdown Timer/Countdown.cs
  3. + 20
    - 4
      Shutdown Timer/Helpers/ExceptionHandler.cs
  4. + 8
    - 0
      Shutdown Timer/Helpers/Settings.cs
  5. + 4
    - 2
      Shutdown Timer/Helpers/WindowsAPIs.cs
  6. + 19
    - 0
      Shutdown Timer/Menu.cs

+ 1
- 1
README.md

@ -109,4 +109,4 @@ If you are running it in the background, then you can go to the notification are
## Logging and Privacy
The application is not connected to the internet and does not log user interactions or usage data. If there is an internal exception it will save a stack trace to the users desktop (since v1.2.0) and notify the user but it will not automatically share any data with anyone. If you are using the Microsoft Store release, then the Store will also monitor basic usage and crashes and (depending on your Windows privacy settings) share this data with Microsoft and me.
The application is **not connected to the internet** and does **not collect usage data**. If there is an internal exception it will save a stack trace and simple event log to the users desktop (since v1.2.0) and notify the user but it will not automatically share any data with anyone. If you are using the Microsoft Store release, then the Store will also monitor basic usage and crashes and (depending on your Windows privacy settings) share this data with Microsoft and me.

+ 20
- 1
Shutdown Timer/Countdown.cs

@ -35,10 +35,14 @@ namespace ShutdownTimer
// entrypoint
private void Countdown_Load(object sender, EventArgs e)
{
ExceptionHandler.LogEvent("[Countdown] Start stopwatch");
// Setup clock
stopwatch = new Stopwatch();
stopwatch.Start();
ExceptionHandler.LogEvent("[Countdown] Prepare UI");
// Set trayIcon icon to the opposite of the selected theme
bool lighttheme;
if (SettingsProvider.SettingsLoaded)
@ -74,9 +78,11 @@ namespace ShutdownTimer
Hide();
}
ExceptionHandler.LogEvent("[Countdown] Set UI");
UpdateUI(CountdownTimeSpan);
if (PreventSystemSleep) { ExecutionState.SetThreadExecutionState(ExecutionState.EXECUTION_STATE.ES_CONTINUOUS | ExecutionState.EXECUTION_STATE.ES_SYSTEM_REQUIRED); } // give the system some coffee so it stays awake when tired using some fancy EXECUTION_STATE flags
if (PreventSystemSleep) { ExceptionHandler.LogEvent("[Countdown] Preventing sleep"); ExecutionState.SetThreadExecutionState(ExecutionState.EXECUTION_STATE.ES_CONTINUOUS | ExecutionState.EXECUTION_STATE.ES_SYSTEM_REQUIRED); } // give the system some coffee so it stays awake when tired using some fancy EXECUTION_STATE flags
}
/// <summary>
@ -126,6 +132,8 @@ namespace ShutdownTimer
/// </summary>
private void ExitApplication()
{
ExceptionHandler.LogEvent("[Countdown] Exit application");
ignoreClose = false;
allowClose = true;
stopwatch.Stop();
@ -142,6 +150,8 @@ namespace ShutdownTimer
/// </summary>
private void RestartApplication()
{
ExceptionHandler.LogEvent("[Countdown] Restart application");
ignoreClose = false;
allowClose = true;
stopwatch.Stop();
@ -156,6 +166,8 @@ namespace ShutdownTimer
/// </summary>
private void RestartTimer()
{
ExceptionHandler.LogEvent("[Countdown] Restart timer");
stopwatch.Stop();
stopwatch = new Stopwatch();
stopwatch.Start();
@ -168,6 +180,8 @@ namespace ShutdownTimer
/// </summary>
private void HideUI()
{
ExceptionHandler.LogEvent("[Countdown] Hide UI");
timerUIHideMenuItem.Enabled = false;
timerUIShowMenuItem.Enabled = true;
TopMost = false;
@ -187,6 +201,8 @@ namespace ShutdownTimer
/// </summary>
private void ShowUI()
{
ExceptionHandler.LogEvent("[Countdown] Show UI");
timerUIHideMenuItem.Enabled = true;
timerUIShowMenuItem.Enabled = false;
TopMost = true;
@ -322,6 +338,9 @@ namespace ShutdownTimer
private void ExecutePowerAction(string ChoosenAction)
{
ExceptionHandler.LogEvent("[Countdown] Execute power action");
throw new NotImplementedException();
ignoreClose = false; // do not ignore close event
allowClose = true; // disable close question

+ 20
- 4
Shutdown Timer/Helpers/ExceptionHandler.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
@ -8,6 +9,8 @@ namespace ShutdownTimer.Helpers
{
public static class ExceptionHandler
{
private static Stack<string> eventLog; // storage for important event logs
public static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception)args.ExceptionObject;
@ -34,18 +37,27 @@ namespace ShutdownTimer.Helpers
"Please create an issue on GitHub and include the contents of this log file to help identify and fix the issue.\n\n" +
$"Log file location: {filepath}\n" +
$"GitHub: github.com/lukaslangrock/ShutdownTimerClassic/issues\n" +
$"Email: lukas.langrock@outlook.de";
MessageBox.Show(message, "Shutdown Timer Classic crashed!", MessageBoxButtons.OK, MessageBoxIcon.Error);
$"Email: lukas.langrock@outlook.de\n\n" +
$"The application experienced a critical error and may very well be broken. It is not recommended to keep using this instance of the application!\n" +
$"Would you like to terminate the application?";
DialogResult dialogResult = MessageBox.Show(message, "Shutdown Timer Classic crashed!", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
Process.Start(filepath); // Show log to user
DialogResult dialogResult = MessageBox.Show("Would you like to terminate the application?\n\nThe application experienced a critical error and may very well be broken. It is not recommended to keep using this instance of the application!",
"Terminate the application?", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
if (dialogResult == DialogResult.Yes)
{
Process.GetCurrentProcess().Kill();
}
}
// Add a new log to the event log stack
public static void LogEvent(string data)
{
if (eventLog is null) { eventLog = new Stack<string>(); }
eventLog.Push(data);
}
// Logs application, process, exception and environemnt details and returns log filepath
private static string LogException(Exception e)
{
@ -96,6 +108,10 @@ namespace ShutdownTimer.Helpers
log.Append($"Message: {e.Message}\n");
log.Append($"Stack Trace:\n {e.StackTrace}\n");
log.Append("\n\n---- Internal Event Log ----\n");
foreach (var item in eventLog)
log.Append(item + "\n");
log.Append("\n\n---- End of Log ----");
string filepath = $"{Environment.GetFolderPath(Environment.SpecialFolder.Desktop)}\\ShutdownTimerClassic Exception-Log [{DateTime.Now.Ticks}].txt";

+ 8
- 0
Shutdown Timer/Helpers/Settings.cs

@ -14,6 +14,8 @@ namespace ShutdownTimer.Helpers
public static void Load()
{
ExceptionHandler.LogEvent("[Settings] Loading settings.json");
// make sure respective appdata dir exists
if (!Directory.Exists(settingsDirectory))
{
@ -36,6 +38,8 @@ namespace ShutdownTimer.Helpers
private static void CheckSettings()
{
ExceptionHandler.LogEvent("[Settings] Checking settings object");
Settings.AppVersion = Application.ProductVersion;
Settings.SettingsVersion = 1; // increases whenever there are breaking changes to the settings system
if (Settings.DefaultTimer is null)
@ -56,6 +60,8 @@ namespace ShutdownTimer.Helpers
public static void ClearSettings()
{
ExceptionHandler.LogEvent("[Settings] Clearing settings");
Settings = new SettingsData();
CheckSettings();
Save();
@ -63,6 +69,8 @@ namespace ShutdownTimer.Helpers
public static void Save()
{
ExceptionHandler.LogEvent("[Settings] Saving settings.json");
if (SettingsLoaded)
{
string settingsJson = JsonConvert.SerializeObject(Settings, Formatting.Indented);

+ 4
- 2
Shutdown Timer/Helpers/WindowsAPIs.cs

@ -12,6 +12,8 @@ namespace ShutdownTimer.Helpers
/// <returns>true: Light theme; false: Dark theme</returns>
public static bool GetWindowsLightTheme()
{
ExceptionHandler.LogEvent("[WindowsAPIs] Get windows theme");
bool lighttheme = false; // default if all checks fail (may happen when not on Windows 10)
try // Get app theme as fallback
@ -20,7 +22,7 @@ namespace ShutdownTimer.Helpers
if (winTheme.ToString() == "#FFFFFFFF") { lighttheme = true; }
else if (winTheme.ToString() == "#FF000000") { lighttheme = false; }
}
catch (Exception) { }
catch (Exception) { ExceptionHandler.LogEvent("[WindowsAPIs] Failed to get winTheme"); }
try // Get actual default Windows theme which (the same as the taskbar)
{
@ -28,7 +30,7 @@ namespace ShutdownTimer.Helpers
if (key == 0) { lighttheme = false; }
else if (key == 1) { lighttheme = true; }
}
catch (Exception) { }
catch (Exception) { ExceptionHandler.LogEvent("[WindowsAPIs] Failed to read registry theme value"); }
return lighttheme;
}

+ 19
- 0
Shutdown Timer/Menu.cs

@ -19,6 +19,8 @@ namespace ShutdownTimer
private void Menu_Load(object sender, EventArgs e)
{
ExceptionHandler.LogEvent("[Menu] Load menu");
versionLabel.Text = "v" + Application.ProductVersion.Remove(Application.ProductVersion.LastIndexOf(".")); // Display current version
infoToolTip.SetToolTip(gracefulCheckBox, "Applications that do not exit when prompted automatically get terminated by default to ensure a successful shutdown." +
"\n\nA graceful shutdown on the other hand will wait for all applications to exit before continuing with the shutdown." +
@ -74,6 +76,8 @@ namespace ShutdownTimer
private void StartButton_Click(object sender, EventArgs e)
{
ExceptionHandler.LogEvent("[Menu] Prepare start countdown");
if (RunChecks())
{
// Disable controls
@ -88,6 +92,7 @@ namespace ShutdownTimer
}
else
{
ExceptionHandler.LogEvent("[Menu] Invalid countdown");
MessageBox.Show("The following error(s) occurred:\n\n" + checkResult + "Please try to resolve the(se) problem(s) and try again.", "There seems to be a problem!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
@ -100,6 +105,8 @@ namespace ShutdownTimer
/// <returns>Result of checks</returns>
private bool RunChecks()
{
ExceptionHandler.LogEvent("[Menu] Run checks");
bool errTracker = true; // if anything goes wrong the tracker will be set to false
string errMessage = null; // error messages will append to this
@ -151,6 +158,8 @@ namespace ShutdownTimer
/// </summary>
private void ProcessArgs()
{
ExceptionHandler.LogEvent("[Menu] Process args");
string timeArg = null;
string controlMode = "Recommend"; // Use recommend control mode by default
//Control Modes:
@ -212,6 +221,7 @@ namespace ShutdownTimer
switch (count)
{
case 0:
ExceptionHandler.LogEvent("[Menu] Invalid time args");
MessageBox.Show("StartupArgs Error: Please provide a valid argument after /SetTime", "Invalid argument", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
@ -235,10 +245,12 @@ namespace ShutdownTimer
switch (controlMode)
{
case "Takeover":
ExceptionHandler.LogEvent("[Menu] Takeover mode");
StartCountdown("Externally initiated countdown!");
break;
case "Lock":
ExceptionHandler.LogEvent("[Menu] Lock mode");
settingsButton.Enabled = false;
actionGroupBox.Enabled = false;
timeGroupBox.Enabled = false;
@ -247,6 +259,7 @@ namespace ShutdownTimer
break;
case "Recommend":
ExceptionHandler.LogEvent("[Menu] Recommend mode");
settingsButton.Enabled = false;
actionGroupBox.Enabled = true;
timeGroupBox.Enabled = true;
@ -261,6 +274,8 @@ namespace ShutdownTimer
/// </summary>
private void LoadSettings()
{
ExceptionHandler.LogEvent("[Menu] Load settings");
SettingsProvider.Load();
actionComboBox.Text = SettingsProvider.Settings.DefaultTimer.Action;
@ -277,6 +292,8 @@ namespace ShutdownTimer
/// </summary>
private void SaveSettings()
{
ExceptionHandler.LogEvent("[Menu] Save settings");
if (SettingsProvider.SettingsLoaded)
{
if (SettingsProvider.Settings.RememberLastState)
@ -299,6 +316,8 @@ namespace ShutdownTimer
/// </summary>
private void StartCountdown(string pStatus = null)
{
ExceptionHandler.LogEvent("[Menu] Start countdown");
// Calculate TimeSpan
TimeSpan timeSpan = new TimeSpan(Convert.ToInt32(hoursNumericUpDown.Value), Convert.ToInt32(minutesNumericUpDown.Value), Convert.ToInt32(secondsNumericUpDown.Value));

Loading…
Cancel
Save