<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Recent changes to Lifecycle</title><link>https://sourceforge.net/p/sucrose/wiki/Lifecycle/</link><description>Recent changes to Lifecycle</description><atom:link href="https://sourceforge.net/p/sucrose/wiki/Lifecycle/feed" rel="self"/><language>en</language><lastBuildDate>Sat, 04 Jul 2026 04:15:46 -0000</lastBuildDate><atom:link href="https://sourceforge.net/p/sucrose/wiki/Lifecycle/feed" rel="self" type="application/rss+xml"/><item><title>Lifecycle modified by Bayram Emekli</title><link>https://sourceforge.net/p/sucrose/wiki/Lifecycle/</link><description>&lt;div class="markdown_content"&gt;&lt;pre&gt;--- v2
+++ v3
@@ -74,12 +74,12 @@

 ## Common per-process configuration

-Every process's constructor or `Main` runs the same two configuration helpers before anything else:
+Every process's constructor or `Main` wires the global exception handlers first, then sets its thread culture, and finally runs the same two configuration helpers:


 - `SSDHR.Configure()` (`Shared.Dependency.Helper.Runtime`) — **RELEASE-only.** Points the process at the **bundled private .NET runtime** by setting environment variables `DOTNET_ROOT` (and the `(x86)`/`(arm64)` variants), `DOTNET_MULTILEVEL_LOOKUP=0`, `DOTNET_ROLL_FORWARD=LatestMajor`, and prepending the runtime folder `&amp;lt;parent&amp;gt;/Sucrose.Runtime` plus the app dir to `PATH`. (Debug builds use the machine's installed runtime — the path manipulation happens only in `RELEASE`.)
 - `SSDHG.Configure()` (`Shared.Dependency.Helper.Graphic`) — calls `EnsureHighPerformance(exe)` so the process is registered for the high-performance GPU on multi-GPU laptops.

-Then each process sets its thread culture from the `Culture` setting. WPF apps set `ShutdownMode`; the console apps (Commandog, Backgroundog, Reportdog) additionally set `Console.InputEncoding/OutputEncoding = UTF8` so the `✔` (U+2714) / `✖` (U+2716) command delimiters are not mangled.
+The thread culture is set from the `Culture` setting (`SHC.All = new CultureInfo(Culture, true)`) *before* the two `Configure()` helpers above, and the global exception handlers are wired earlier still. WPF apps set `ShutdownMode`; the console apps (Commandog, Backgroundog, Reportdog) additionally set `Console.InputEncoding/OutputEncoding = UTF8` so the `✔` (U+2714) / `✖` (U+2716) command delimiters are not mangled.

 Build-level runtime/GC config from `Directory.Build.targets` (all non-library outputs): `ThreadPoolMinThreads=10`, `ThreadPoolMaxThreads=500`, `UseWindowsThreadPool=true`; `ServerGarbageCollection=true`, `ConcurrentGarbageCollection=true`, `RetainVMGarbageCollection=false`; `TieredCompilation=true`, `InvariantGlobalization=false`.

@@ -126,7 +126,7 @@
          -&amp;gt; spawn Commandog "✔Watchdog✖&amp;lt;watchdog.exe&amp;gt;✖&amp;lt;base64(app✖ex✖show✖log)&amp;gt;"
             -&amp;gt; Watchdog: write Cache/Report/&amp;lt;guid&amp;gt;.json -&amp;gt; Kill(app) -&amp;gt; (if show) Dark/Light error dialog
     ... later ...

-    Reportdog loop -&amp;gt; upload Cache/Report/*.json   (only if ExceptionData / TelemetryData enabled)
+    Reportdog loop -&amp;gt; upload Cache/Report/*.json   (only if ExceptionData enabled)

 Global exception handling is wired identically in Launcher / Portal / Watchdog / Property (handlers for `ThreadException`, `FirstChanceException`, `UnhandledException`, `UnobservedTaskException`, `DispatcherUnhandledException`). See [Crash Reporting](Crash-Reporting).

&amp;lt;/guid&amp;gt;&amp;lt;/base64(app✖ex✖show✖log)&amp;gt;&amp;lt;/watchdog.exe&amp;gt;&amp;lt;/parent&amp;gt;&lt;/pre&gt;
&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bayram Emekli</dc:creator><pubDate>Sat, 04 Jul 2026 04:15:46 -0000</pubDate><guid>https://sourceforge.nete6395326f1c9457069650e9cbe9e51fc2abc406c</guid></item><item><title>Lifecycle modified by Bayram Emekli</title><link>https://sourceforge.net/p/sucrose/wiki/Lifecycle/</link><description>&lt;div class="markdown_content"&gt;&lt;pre&gt;--- v1
+++ v2
@@ -2,7 +2,24 @@

 This page traces the runtime lifecycle of the Sucrose process stack: the cold start from `Sucrose.Launcher.exe` (single-instance gate → `Configure()` → tray icon + signal channel + Discord), how the wallpaper auto-restores on launch, the configuration helpers every process runs first, and the two teardown paths — "Exit" (leave the wallpaper running) versus "Close" (full stack teardown). It is a developer reference and complements the [Architecture Overview](Architecture-Overview) (who exists) with *when and in what order things happen*.

-&amp;gt; 🖼️ **Diagram needed:** A timeline/sequence diagram of the cold start: Launcher `OnStartup` → `Instance.Basic` → `Configure()` → `TrayIconManager.Start()` (engine auto-restore, Reportdog, update check) → signal channel → Discord.

+    :::mermaid
+    sequenceDiagram
+        actor User
+        participant L as Launcher
+        participant Tray as TrayIconManager
+        participant Cmd as Commandog
+        participant Eng as Live engine
+        User-&amp;gt;&amp;gt;L: run Sucrose.Launcher.exe
+        L-&amp;gt;&amp;gt;L: OnStartup, SetLanguage(Culture)
+        L-&amp;gt;&amp;gt;L: Instance.Basic mutex gate (first instance)
+        L-&amp;gt;&amp;gt;L: Configure()
+        L-&amp;gt;&amp;gt;Tray: TrayIconManager.Start()
+        Tray-&amp;gt;&amp;gt;Cmd: Engine.Command(false) auto-restore
+        Cmd-&amp;gt;&amp;gt;Eng: spawn Backgroundog, then Live engine
+        Tray-&amp;gt;&amp;gt;Cmd: Reportdog.Command() flush reports
+        Tray-&amp;gt;&amp;gt;Cmd: silent Update check (if Auto)
+        L-&amp;gt;&amp;gt;L: LauncherManager.StartChannel (signal)
+        L-&amp;gt;&amp;gt;L: Discord.Initialize() 5s timer

 ## Contents

&lt;/pre&gt;
&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bayram Emekli</dc:creator><pubDate>Sat, 04 Jul 2026 04:15:43 -0000</pubDate><guid>https://sourceforge.netb9c9db291f8f416f1a442716f07c29f3acd117f6</guid></item><item><title>Lifecycle modified by Bayram Emekli</title><link>https://sourceforge.net/p/sucrose/wiki/Lifecycle/</link><description>&lt;div class="markdown_content"&gt;&lt;h1 id="h-lifecycle"&gt;Lifecycle&lt;/h1&gt;
&lt;p&gt;This page traces the runtime lifecycle of the Sucrose process stack: the cold start from &lt;code&gt;Sucrose.Launcher.exe&lt;/code&gt; (single-instance gate → &lt;code&gt;Configure()&lt;/code&gt; → tray icon + signal channel + Discord), how the wallpaper auto-restores on launch, the configuration helpers every process runs first, and the two teardown paths — "Exit" (leave the wallpaper running) versus "Close" (full stack teardown). It is a developer reference and complements the &lt;a href="./Architecture-Overview"&gt;Architecture Overview&lt;/a&gt; (who exists) with &lt;em&gt;when and in what order things happen&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🖼️ &lt;strong&gt;Diagram needed:&lt;/strong&gt; A timeline/sequence diagram of the cold start: Launcher &lt;code&gt;OnStartup&lt;/code&gt; → &lt;code&gt;Instance.Basic&lt;/code&gt; → &lt;code&gt;Configure()&lt;/code&gt; → &lt;code&gt;TrayIconManager.Start()&lt;/code&gt; (engine auto-restore, Reportdog, update check) → signal channel → Discord.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="h-contents"&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-launcher-is-the-entry-point"&gt;The Launcher is the entry point&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#cold-start-sequence"&gt;Cold start sequence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tray-start-side-effects-auto-restore"&gt;Tray-start side effects (auto-restore)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#common-per-process-configuration"&gt;Common per-process configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#discord-rich-presence-lifecycle"&gt;Discord Rich Presence lifecycle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#opening-the-portal-second-launch"&gt;Opening the Portal (second launch)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#reload-cycle-and-the-wallpaper-engine"&gt;Reload, cycle, and the wallpaper engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#teardown-exit-vs-close"&gt;Teardown: Exit vs. Close&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#crash-path"&gt;Crash path&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#see-also"&gt;See also&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="h-the-launcher-is-the-entry-point"&gt;The Launcher is the entry point&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Sucrose.Launcher.exe&lt;/code&gt; (&lt;code&gt;src/Launcher/Sucrose.Launcher/App.xaml.cs&lt;/code&gt;) is the process that starts when the user opens Sucrose. It is a WPF app with &lt;strong&gt;no main window&lt;/strong&gt; — it lives entirely in the system tray, with &lt;code&gt;ShutdownMode = ShutdownMode.OnExplicitShutdown&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="h-cold-start-sequence"&gt;Cold start sequence&lt;/h2&gt;
&lt;p&gt;The Launcher's &lt;code&gt;OnStartup&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;SRHR.SetLanguage(SMMG.Culture)&lt;/code&gt; — set the UI language from the saved &lt;code&gt;Culture&lt;/code&gt; setting (default = the current UI two-letter ISO language).&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;ShutdownMode = OnExplicitShutdown&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single-instance gate:&lt;/strong&gt; &lt;code&gt;SSSHI.Basic(SMMRM.Launcher, SMMRA.Launcher)&lt;/code&gt; (mutex &lt;code&gt;{Sucrose-Wallpaper-Engine-Launcher}&lt;/code&gt;). See &lt;a href="./Single-Instance-Mutexes"&gt;Single Instance Mutexes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If &lt;code&gt;true&lt;/code&gt;&lt;/strong&gt; (this is the first instance), run &lt;code&gt;Configure()&lt;/code&gt;:&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SSLMI.TrayIconManager.Start()&lt;/code&gt; — create the tray icon + context menu.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSMI.LauncherManager.StartChannel(SSSSLSS.Handler)&lt;/code&gt; — start the &lt;strong&gt;Launcher signal channel&lt;/strong&gt; (a &lt;code&gt;FileSystemWatcher&lt;/code&gt; over &lt;code&gt;Launcher.sgnl&lt;/code&gt;) using &lt;code&gt;LauncherSignalService.Handler&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Discord.Initialize()&lt;/code&gt; — start the Discord Rich Presence timer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If &lt;code&gt;false&lt;/code&gt;&lt;/strong&gt; (an instance is already running), this second copy does &lt;strong&gt;not&lt;/strong&gt; stay resident: it calls &lt;code&gt;SSLCI.Command()&lt;/code&gt; (asks the running stack to open the Portal) and then &lt;code&gt;Close()&lt;/code&gt;s itself.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So double-launching the Launcher means "open the settings window," not "create a second tray icon."&lt;/p&gt;
&lt;p&gt;In words:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Sucrose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Launcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;exe&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;-&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OnStartup&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Basic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"{...-Launcher}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sucrose.Launcher.exe"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nt"&gt;-&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Configure&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;TrayIconManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Start&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;tray&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;icon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;menu&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;auto-restore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;wallpaper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Reportdog&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;check&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;LauncherManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StartChannel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;LauncherSignalService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Discord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Initialize&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;5s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;presence&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Discord&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;running&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="h-tray-start-side-effects-auto-restore"&gt;Tray-start side effects (auto-restore)&lt;/h2&gt;
&lt;p&gt;When &lt;code&gt;TrayIconManager.Start()&lt;/code&gt; runs, it also performs three actions regardless of any user click:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SSLCE.Command(false)&lt;/code&gt; — &lt;strong&gt;&lt;code&gt;Engine.Command(false)&lt;/code&gt;&lt;/strong&gt;: if a wallpaper is configured and no engine is currently running, start the wallpaper engine now. This is how the wallpaper &lt;strong&gt;auto-restores on launch&lt;/strong&gt; — users do not re-apply each boot. &lt;code&gt;Run.Start()&lt;/code&gt; reads the selected wallpaper's &lt;code&gt;SucroseInfo&lt;/code&gt; JSON, maps its &lt;code&gt;Type&lt;/code&gt; to the configured engine, and (when &lt;code&gt;PerformanceCounter&lt;/code&gt; is on) first spawns &lt;code&gt;Backgroundog&lt;/code&gt; via &lt;code&gt;✔Backgroundog✖&amp;lt;Backgroundog&amp;gt;&lt;/code&gt;, then spawns the engine via &lt;code&gt;✔Live✖&amp;lt;engine.exe&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSLCRG.Command()&lt;/code&gt; — &lt;strong&gt;&lt;code&gt;Reportdog.Command()&lt;/code&gt;&lt;/strong&gt;: spawn &lt;code&gt;Sucrose.Reportdog.exe&lt;/code&gt; (via Commandog) to flush pending telemetry / error reports.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;SMMU.Auto&lt;/code&gt; (auto-update on): &lt;code&gt;SSLCU.Command(false)&lt;/code&gt; — a silent update check (spawns &lt;code&gt;Sucrose.Update.exe&lt;/code&gt; via Commandog).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tray visibility honors the &lt;code&gt;AppVisible&lt;/code&gt; setting (default &lt;code&gt;true&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="h-common-per-process-configuration"&gt;Common per-process configuration&lt;/h2&gt;
&lt;p&gt;Every process's constructor or &lt;code&gt;Main&lt;/code&gt; runs the same two configuration helpers before anything else:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SSDHR.Configure()&lt;/code&gt; (&lt;code&gt;Shared.Dependency.Helper.Runtime&lt;/code&gt;) — &lt;strong&gt;RELEASE-only.&lt;/strong&gt; Points the process at the &lt;strong&gt;bundled private .NET runtime&lt;/strong&gt; by setting environment variables &lt;code&gt;DOTNET_ROOT&lt;/code&gt; (and the &lt;code&gt;(x86)&lt;/code&gt;/&lt;code&gt;(arm64)&lt;/code&gt; variants), &lt;code&gt;DOTNET_MULTILEVEL_LOOKUP=0&lt;/code&gt;, &lt;code&gt;DOTNET_ROLL_FORWARD=LatestMajor&lt;/code&gt;, and prepending the runtime folder &lt;code&gt;&amp;lt;parent&amp;gt;/Sucrose.Runtime&lt;/code&gt; plus the app dir to &lt;code&gt;PATH&lt;/code&gt;. (Debug builds use the machine's installed runtime — the path manipulation happens only in &lt;code&gt;RELEASE&lt;/code&gt;.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSDHG.Configure()&lt;/code&gt; (&lt;code&gt;Shared.Dependency.Helper.Graphic&lt;/code&gt;) — calls &lt;code&gt;EnsureHighPerformance(exe)&lt;/code&gt; so the process is registered for the high-performance GPU on multi-GPU laptops.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then each process sets its thread culture from the &lt;code&gt;Culture&lt;/code&gt; setting. WPF apps set &lt;code&gt;ShutdownMode&lt;/code&gt;; the console apps (Commandog, Backgroundog, Reportdog) additionally set &lt;code&gt;Console.InputEncoding/OutputEncoding = UTF8&lt;/code&gt; so the &lt;code&gt;✔&lt;/code&gt; (U+2714) / &lt;code&gt;✖&lt;/code&gt; (U+2716) command delimiters are not mangled.&lt;/p&gt;
&lt;p&gt;Build-level runtime/GC config from &lt;code&gt;Directory.Build.targets&lt;/code&gt; (all non-library outputs): &lt;code&gt;ThreadPoolMinThreads=10&lt;/code&gt;, &lt;code&gt;ThreadPoolMaxThreads=500&lt;/code&gt;, &lt;code&gt;UseWindowsThreadPool=true&lt;/code&gt;; &lt;code&gt;ServerGarbageCollection=true&lt;/code&gt;, &lt;code&gt;ConcurrentGarbageCollection=true&lt;/code&gt;, &lt;code&gt;RetainVMGarbageCollection=false&lt;/code&gt;; &lt;code&gt;TieredCompilation=true&lt;/code&gt;, &lt;code&gt;InvariantGlobalization=false&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="h-discord-rich-presence-lifecycle"&gt;Discord Rich Presence lifecycle&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Discord.Initialize()&lt;/code&gt; starts a &lt;code&gt;DispatcherTimer&lt;/code&gt; (&lt;code&gt;InitializeTimer&lt;/code&gt;) ticking every &lt;strong&gt;5 seconds&lt;/strong&gt;. On each tick it checks whether the user enabled Discord (&lt;code&gt;SMMH.DiscordConnect&lt;/code&gt;) &lt;strong&gt;and&lt;/strong&gt; whether &lt;code&gt;Discord.exe&lt;/code&gt; or &lt;code&gt;DiscordPTB.exe&lt;/code&gt; is running (&lt;code&gt;ApplicationNames = ["Discord.exe", "DiscordPTB.exe"]&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If yes and not yet initialized → &lt;code&gt;Client.Initialize()&lt;/code&gt; then &lt;code&gt;SetPresence()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If Discord is gone / disabled → &lt;code&gt;ClearPresence()&lt;/code&gt; and stop the refresh timer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An optional auto-refresh timer (&lt;code&gt;RefreshTimer&lt;/code&gt;) runs at interval &lt;code&gt;SMMH.DiscordRefreshDelay&lt;/code&gt; seconds and re-randomizes the presence text/images. The Discord application id is &lt;code&gt;1126294965950103612&lt;/code&gt; (library: DiscordRPC). On shutdown the Launcher's &lt;code&gt;OnExit&lt;/code&gt; runs &lt;code&gt;Discord.Dispose()&lt;/code&gt; → &lt;code&gt;LauncherManager.StopChannel()&lt;/code&gt; → &lt;code&gt;TrayIconManager.Dispose()&lt;/code&gt; → &lt;code&gt;Close()&lt;/code&gt;. See &lt;a href="./Discord-Rich-Presence"&gt;Discord Rich Presence&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="h-opening-the-portal-second-launch"&gt;Opening the Portal (second launch)&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clicks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tray&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Open"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Launcher&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;again&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Commandog&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"✔Interface✖&amp;lt;Portal.exe&amp;gt;"&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Commandog&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Portal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Portal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OnStartup&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Basic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{...-Portal}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Sucrose.Portal.exe"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;first&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Security&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MainWindow&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;already&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="h-reload-cycle-and-the-wallpaper-engine"&gt;Reload, cycle, and the wallpaper engine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reload&lt;/strong&gt; (tray) → &lt;code&gt;✔RestartLive✖Unknown&lt;/code&gt; → Commandog stops the live engine + browser subprocesses, &lt;strong&gt;kills &lt;code&gt;Sucrose.Backgroundog.exe&lt;/code&gt;&lt;/strong&gt;, runs &lt;code&gt;Run.Start()&lt;/code&gt;, waits 1500 ms, and retries if the engine is not running.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cycle&lt;/strong&gt; (slideshow advance) → &lt;code&gt;✔Cycyling✖...&lt;/code&gt; → same as Reload &lt;strong&gt;but without killing Backgroundog&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Killing/starting the engine is handled by &lt;code&gt;Kill.Stop()&lt;/code&gt; / &lt;code&gt;Kill.StopSubprocess()&lt;/code&gt; (which also resumes and kills &lt;code&gt;msedgewebview2&lt;/code&gt; / &lt;code&gt;CefSharp.BrowserSubprocess&lt;/code&gt; child processes whose command line contains &lt;code&gt;Sucrose&lt;/code&gt;) and &lt;code&gt;Run.Start()&lt;/code&gt;. See &lt;a href="./Commandog-Dispatcher"&gt;Commandog Dispatcher&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="h-teardown-exit-vs-close"&gt;Teardown: Exit vs. Close&lt;/h2&gt;
&lt;p&gt;The tray menu's last item label and behavior depend on the &lt;code&gt;AppExit&lt;/code&gt; setting (default &lt;strong&gt;false&lt;/strong&gt;), via &lt;code&gt;Close.cs&lt;/code&gt; (&lt;code&gt;src/Shared/Sucrose.Shared.Launcher/Command/Close.cs&lt;/code&gt;):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;AppExit&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Tray label&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt; (default)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;"Exit"&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only the Launcher itself exits. The wallpaper engine and the background services keep running.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;"Close"&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A &lt;strong&gt;full teardown&lt;/strong&gt;: stop the engine (&lt;code&gt;Kill.Stop&lt;/code&gt; / &lt;code&gt;StopSubprocess&lt;/code&gt;), then kill (if running), in order — &lt;strong&gt;Undo, Portal, Update, Property, Watchdog, Commandog, Reportdog, Backgroundog&lt;/strong&gt; — release the tray, then &lt;code&gt;Process.GetCurrentProcess().Kill()&lt;/code&gt; + &lt;code&gt;Environment.Exit(0)&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;Sucrose.Undo.exe&lt;/code&gt; is a separate full-removal path (the uninstaller), not the same as Close. See &lt;a href="./Undo-Internals"&gt;Undo Internals&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="h-crash-path"&gt;Crash path&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;throws&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Watchdog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Commandog&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"✔Watchdog✖&amp;lt;Watchdog.exe&amp;gt;✖&amp;lt;Base64(app✖ex✖show✖log)&amp;gt;"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Watchdog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Report&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;guid&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Kill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Dark&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Light&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dialog&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;later&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Reportdog&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Report&lt;/span&gt;&lt;span class="o"&gt;/*.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExceptionData&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TelemetryData&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Global exception handling is wired identically in Launcher / Portal / Watchdog / Property (handlers for &lt;code&gt;ThreadException&lt;/code&gt;, &lt;code&gt;FirstChanceException&lt;/code&gt;, &lt;code&gt;UnhandledException&lt;/code&gt;, &lt;code&gt;UnobservedTaskException&lt;/code&gt;, &lt;code&gt;DispatcherUnhandledException&lt;/code&gt;). See &lt;a href="./Crash-Reporting"&gt;Crash Reporting&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="h-see-also"&gt;See also&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="./Architecture-Overview"&gt;Architecture Overview&lt;/a&gt; — the process roster and coupling mechanisms.&lt;/li&gt;
&lt;li&gt;&lt;a href="./Single-Instance-Mutexes"&gt;Single Instance Mutexes&lt;/a&gt; — the &lt;code&gt;Instance.Basic&lt;/code&gt; gate used at startup.&lt;/li&gt;
&lt;li&gt;&lt;a href="./Commandog-Dispatcher"&gt;Commandog Dispatcher&lt;/a&gt; — how &lt;code&gt;Run&lt;/code&gt;/&lt;code&gt;Reload&lt;/code&gt;/&lt;code&gt;Cycle&lt;/code&gt; commands are executed.&lt;/li&gt;
&lt;li&gt;&lt;a href="./Discord-Rich-Presence"&gt;Discord Rich Presence&lt;/a&gt; — enabling and configuring presence.&lt;/li&gt;
&lt;li&gt;&lt;a href="./Crash-Reporting"&gt;Crash Reporting&lt;/a&gt; — the Watchdog/Reportdog flow.&lt;/li&gt;
&lt;li&gt;&lt;a href="./Theme-Tray-Startup"&gt;Theme, Tray &amp;amp; Startup&lt;/a&gt; — the user-facing tray, exit-on-close, and startup modes.&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bayram Emekli</dc:creator><pubDate>Sat, 04 Jul 2026 04:15:30 -0000</pubDate><guid>https://sourceforge.netb77dddb3a18f9cf7e351d7341a18f5a5f7da802b</guid></item></channel></rss>