-
-
Notifications
You must be signed in to change notification settings - Fork 60
Lifecycle
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 (who exists) with when and in what order things happen.
🖼️ 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.
- The Launcher is the entry point
- Cold start sequence
- Tray-start side effects (auto-restore)
- Common per-process configuration
- Discord Rich Presence lifecycle
- Opening the Portal (second launch)
- Reload, cycle, and the wallpaper engine
- Teardown: Exit vs. Close
- Crash path
- See also
Sucrose.Launcher.exe (src/Launcher/Sucrose.Launcher/App.xaml.cs) is the process that starts when the user opens Sucrose. It is a WPF app with no main window — it lives entirely in the system tray, with ShutdownMode = ShutdownMode.OnExplicitShutdown.
The Launcher's OnStartup:
-
SRHR.SetLanguage(SMMG.Culture)— set the UI language from the savedCulturesetting (default = the current UI two-letter ISO language). - Set
ShutdownMode = OnExplicitShutdown. -
Single-instance gate:
SSSHI.Basic(SMMRM.Launcher, SMMRA.Launcher)(mutex{Sucrose-Wallpaper-Engine-Launcher}). See Single Instance Mutexes.-
If
true(this is the first instance), runConfigure():-
SSLMI.TrayIconManager.Start()— create the tray icon + context menu. -
SSMI.LauncherManager.StartChannel(SSSSLSS.Handler)— start the Launcher signal channel (aFileSystemWatcheroverLauncher.sgnl) usingLauncherSignalService.Handler. -
Discord.Initialize()— start the Discord Rich Presence timer.
-
-
If
false(an instance is already running), this second copy does not stay resident: it callsSSLCI.Command()(asks the running stack to open the Portal) and thenClose()s itself.
-
If
So double-launching the Launcher means "open the settings window," not "create a second tray icon."
In words:
User runs Sucrose.Launcher.exe
-> OnStartup: Instance.Basic("{...-Launcher}", "Sucrose.Launcher.exe") == true
-> Configure()
-> TrayIconManager.Start() (tray icon + menu; auto-restore wallpaper; Reportdog; update check)
-> LauncherManager.StartChannel(LauncherSignalService.Handler)
-> Discord.Initialize() (5s timer; presence if Discord running + enabled)
When TrayIconManager.Start() runs, it also performs three actions regardless of any user click:
-
SSLCE.Command(false)—Engine.Command(false): if a wallpaper is configured and no engine is currently running, start the wallpaper engine now. This is how the wallpaper auto-restores on launch — users do not re-apply each boot.Run.Start()reads the selected wallpaper'sSucroseInfoJSON, maps itsTypeto the configured engine, and (whenPerformanceCounteris on) first spawnsBackgroundogvia✔Backgroundog✖<Backgroundog>, then spawns the engine via✔Live✖<engine.exe>. -
SSLCRG.Command()—Reportdog.Command(): spawnSucrose.Reportdog.exe(via Commandog) to flush pending telemetry / error reports. - If
SMMU.Auto(auto-update on):SSLCU.Command(false)— a silent update check (spawnsSucrose.Update.exevia Commandog).
Tray visibility honors the AppVisible setting (default true).
Every process's constructor or Main runs the same two configuration helpers before anything else:
-
SSDHR.Configure()(Shared.Dependency.Helper.Runtime) — RELEASE-only. Points the process at the bundled private .NET runtime by setting environment variablesDOTNET_ROOT(and the(x86)/(arm64)variants),DOTNET_MULTILEVEL_LOOKUP=0,DOTNET_ROLL_FORWARD=LatestMajor, and prepending the runtime folder<parent>/Sucrose.Runtimeplus the app dir toPATH. (Debug builds use the machine's installed runtime — the path manipulation happens only inRELEASE.) -
SSDHG.Configure()(Shared.Dependency.Helper.Graphic) — callsEnsureHighPerformance(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.
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.
Discord.Initialize() starts a DispatcherTimer (InitializeTimer) ticking every 5 seconds. On each tick it checks whether the user enabled Discord (SMMH.DiscordConnect) and whether Discord.exe or DiscordPTB.exe is running (ApplicationNames = ["Discord.exe", "DiscordPTB.exe"]):
- If yes and not yet initialized →
Client.Initialize()thenSetPresence(). - If Discord is gone / disabled →
ClearPresence()and stop the refresh timer.
An optional auto-refresh timer (RefreshTimer) runs at interval SMMH.DiscordRefreshDelay seconds and re-randomizes the presence text/images. The Discord application id is 1126294965950103612 (library: DiscordRPC). On shutdown the Launcher's OnExit runs Discord.Dispose() → LauncherManager.StopChannel() → TrayIconManager.Dispose() → Close(). See Discord Rich Presence.
User clicks tray "Open" (or runs Launcher again)
-> Interface.Command() -> spawn Commandog "✔Interface✖<Portal.exe>"
-> Commandog: Process.Start(Portal.exe)
-> Portal OnStartup: Instance.Basic("{...-Portal}", "Sucrose.Portal.exe")
-> if first: build Generic Host (DI), Security.Apply(), host.Start() -> MainWindow
-> if already running: Close() (no second window)
-
Reload (tray) →
✔RestartLive✖Unknown→ Commandog stops the live engine + browser subprocesses, killsSucrose.Backgroundog.exe, runsRun.Start(), waits 1500 ms, and retries if the engine is not running. -
Cycle (slideshow advance) →
✔Cycyling✖...→ same as Reload but without killing Backgroundog.
Killing/starting the engine is handled by Kill.Stop() / Kill.StopSubprocess() (which also resumes and kills msedgewebview2 / CefSharp.BrowserSubprocess child processes whose command line contains Sucrose) and Run.Start(). See Commandog Dispatcher.
The tray menu's last item label and behavior depend on the AppExit setting (default false), via Close.cs (src/Shared/Sucrose.Shared.Launcher/Command/Close.cs):
AppExit |
Tray label | Behavior |
|---|---|---|
false (default) |
"Exit" | Only the Launcher itself exits. The wallpaper engine and the background services keep running. |
true |
"Close" | A full teardown: stop the engine (Kill.Stop / StopSubprocess), then kill (if running), in order — Undo, Portal, Update, Property, Watchdog, Commandog, Reportdog, Backgroundog — release the tray, then Process.GetCurrentProcess().Kill() + Environment.Exit(0). |
Sucrose.Undo.exe is a separate full-removal path (the uninstaller), not the same as Close. See Undo Internals.
Any process throws
-> Watchdog.Start(app, ex, show, log)
-> spawn Commandog "✔Watchdog✖<Watchdog.exe>✖<Base64(app✖ex✖show✖log)>"
-> Watchdog: write Cache/Report/<guid>.json -> Kill(app) -> (if show) Dark/Light error dialog
... later ...
Reportdog loop -> upload Cache/Report/*.json (only if ExceptionData / TelemetryData enabled)
Global exception handling is wired identically in Launcher / Portal / Watchdog / Property (handlers for ThreadException, FirstChanceException, UnhandledException, UnobservedTaskException, DispatcherUnhandledException). See Crash Reporting.
- Architecture Overview — the process roster and coupling mechanisms.
-
Single Instance Mutexes — the
Instance.Basicgate used at startup. -
Commandog Dispatcher — how
Run/Reload/Cyclecommands are executed. - Discord Rich Presence — enabling and configuring presence.
- Crash Reporting — the Watchdog/Reportdog flow.
- Theme, Tray & Startup — the user-facing tray, exit-on-close, and startup modes.
Getting Started
- Installation
- System Requirements
- Quick Start
- Portal Interface Tour
- Updating Sucrose
- Uninstalling Sucrose
Wallpaper Types
Using Sucrose
- Managing Library
- Using Store
- Customizing Wallpaper
- Multi-Monitor
- Wallpaper Cycling
- Choosing Engines
- Performance Rules
- Theme, Tray & Startup
- Discord Rich Presence
Settings Reference
- Settings Overview
- Settings: General
- Settings: Personal
- Settings: Performance
- Settings: Wallpaper
- Settings: System
- Settings: Other
- Settings: All Keys
Creating Wallpapers
- Create Overview
- Create: Step By Step
- Create: Package Format
- Create: Customization Controls
- Create: JS Bridge
- Create: Audio API
- Create: System API
- Create: Property Listener & Filters
- Create: Web Architecture
- Create: Compatibility
- Create: Example Wallpapers
- Create: Sharing & Publishing
Engine Reference
- Engines Overview
- Engine: MpvPlayer
- Engine: VlcPlayer
- Engine: WebView
- Engine: CefSharp
- Engine: Nebula
- Engine: Vexana
- Engine: Xavier
- Engine: Aurora
- Engine Comparison
Automation & Command Line
Architecture & Internals
- Architecture Overview
- Lifecycle
- Commandog Dispatcher
- Single-Instance Mutexes
- IPC
- Backgroundog Service
- Crash Reporting
- Update Internals
- Property Service
- Undo Internals
Data, Files & Diagnostics
Building & Contributing
- Building From Source
- Repository Layout
- Shared Item Projects
- Code Conventions
- Preprocessor Symbols
- Publish Pipeline
- Bundle Installer Internals
- Extending Sucrose
- Contributing
- Translating with Localizer
- Localization Coverage
- Security Policy
- Privacy & Telemetry
Help & Support