Repository navigation
Architecture
| Project | Output | Role |
|---|---|---|
csharpDialog.Core |
library | Configuration model, argument parser, command parser, command-file watcher, the IDialogService contract, the console front end, and the Cimian/first-run/icon services |
csharpDialog.WPF |
csharpDialog.WPF.exe (WinExe) |
The WPF windows and WpfDialogService, the front end the CLI actually drives. Also runnable on its own |
csharpDialog.CLI |
dialog.exe (Exe, AssemblyName = dialog) |
The shipped entry point |
All three target net10.0-windows with UseWPF. The CLI references the WPF project
directly for packaging, but loads the WPF service by reflection at runtime
(Assembly.LoadFrom on csharpDialog.WPF.dll, then Activator.CreateInstance on
csharpDialog.WPF.Services.WpfDialogService), falling back to the console front end if
that fails for any reason.
flowchart TD
A["dialog.exe Main"] --> B{"args == --autolaunch only?"}
B -- yes --> C["First-run detection<br/>exit 0 if not first run"]
C --> D["Built-in kiosk preset"]
B -- no --> E["CommandLineParser.ParseArguments"]
E --> F{"--autolaunch present<br/>with other flags?"}
F -- yes --> G["First-run gate,<br/>then first-run preset"]
F -- no --> H
G --> H{"window, fullscreen, kiosk,<br/>firstrun or commandfile?"}
D --> I
H -- yes --> I["WpfDialogService<br/>(ProgressDialogWindow)"]
H -- no --> J["ConsoleDialogService<br/>(stdout + Console.ReadLine)"]
I --> K{"--cimian?"}
J --> K
K -- yes --> L["CimianMonitor starts:<br/>tail run.log, seed from manifests"]
K -- no --> M["ShowDialog blocks"]
L --> M
M --> N["Stop monitor, mark first-run done,<br/>print Result/Timestamp, exit 0 or 1"]
The STA requirement is handled in Main: on Windows the whole run is marshalled onto a
dedicated STA thread before anything WPF-related is touched.
There are two IDialogService implementations and, confusingly, two different WPF windows.
This is what dialog.exe shows. The window is a fixed five-row layout defined in
XAML — header, message, scrolling list, progress caption, progress bar, one button — and
the service configures it by looking elements up by name (TitleText, MessageText,
ListItemsPanel, ProgressText, ProgressBarControl, OkButton) after the window's
Loaded event.
That name-lookup approach is why several flags are inert: the service only assigns the handful of properties it explicitly reaches for. Width, height, title, message, button-1 text, button-1 disabled state, initial list items, timeout and the close/keyboard locks are applied. Icon, image, video, colours, fonts, topmost, initial progress value and initial progress text are not.
Theme is not configurable at all here: the window reads the system dark-mode setting and picks one of two hardcoded palettes, and asks DWM for a dark title bar to match.
The fallback. It prints the title, message, progress line, list and a numbered button menu
to stdout and blocks on Console.ReadLine(). It implements far more of IDialogService
than the WPF service does — including progressincrement, progressreset, config,
style, theme and the execute* verbs — but all of it is Console.WriteLine
narration, not a rendering.
Because it blocks on stdin, reaching this front end from a non-interactive context is a hang, not an error. See Troubleshooting.
The WPF project also builds its own executable whose App.OnStartup parses the same
arguments and shows MainWindow — a different, simpler window that does honour
--icon, --image, --backgroundcolor, --textcolor, --fontfamily, --fontsize,
--centeronscreen, --topmost, auto-sizing, and multiple buttons.
MainWindow has no list, no progress bar and no command-file support, so it is not useful
for the progress-reporting job csharpDialog exists to do. It is best understood as an
earlier design that the CLI no longer uses. Scripts should target dialog.exe.
--fullscreen and --kiosk do not maximise the dialog. They create a
FullscreenOverlayWindow sized to the whole virtual desktop (all monitors), transparent,
topmost, not in the taskbar, with a 57 %-opacity black grid and a DWM acrylic backdrop
requested via DWMWA_SYSTEMBACKDROP_TYPE. The dialog window is then loaded, hidden, and
its content tree is reparented into a centred Border inside the overlay, capped at
850 × 1200.
Kiosk adds a Closing handler that cancels every close attempt, so the only exits are a
quit command or a timeout.
There is no admin escape hotkey. Some older internal notes mention one; it does not exist in the code.
sequenceDiagram
participant S as Script
participant F as Command file
participant W as FileSystemWatcher
participant P as CommandParser
participant U as UI thread
S->>F: Add-Content "progress: 60"
F->>W: Changed (LastWrite / Size)
W->>W: debounce 100 ms
W->>F: read from stored byte offset
W->>P: ParseCommand per line
P->>U: Dispatcher.Invoke(ProcessCommandSync)
U->>U: set ProgressBarControl.Value
State that matters: the monitor keeps a byte offset and only ever reads forward. Truncating or rewriting the file while a dialog is open desynchronises it — a shorter file simply produces no reads until it grows past the old offset again.
Commands are applied on the UI thread via Dispatcher.Invoke, so ordering within the file
is preserved.
When --cimian is present a CimianMonitor runs alongside the dialog:
- Refuses to start unless first-run detection says this is a first-run scenario.
- Resolves a log to tail —
reports\run.log, else the newestlogs\<date>\<time>\run.log. - Ensures
managedsoftwareupdateis running, starting it if not. - Scans every cached manifest for
managed_installs:entries with a simple line scan (no YAML dependency) and adds onependinglist row per unique name. With no manifests it falls back to a three-item placeholder list. - Tails the log and matches four regular expressions — installation started, download or
installation progress with a percentage, installation complete, and installation
error — translating each into a
listitemupdate.
The regexes are exact-phrase matchers. If the client's log wording differs, the monitor tails happily and updates nothing.
Everything durable is listed in Configuration. In summary: a rolling
diagnostic log under %ProgramData%\ManagedNotifications\logs\, an optional icon cache
under %ProgramData%\csharpDialog\IconCache\, the caller's command file wherever the caller
put it, and first-run completion markers in the registry. No dialog state survives the
process.
csharpDialog.Core also contains DialogManager, ProgressManager, ThemeService,
StylingService, JsonConfigurationService and ShellCommandService. They compile and
have real implementations, but the WPF service stubs out every corresponding
IDialogService member (returning false or an empty result), and no CLI flag or command
verb routes to them. Do not plan around them.
csharpDialog — MIT licensed — windowsadmins/csharpdialog
Reference
Guides
Internals
Help