Skip to content

Development

Rod Christiansen edited this page Sep 3, 2026 · 1 revision

Development

Repository layout

csharpdialog/
├── .github/workflows/
│   ├── ci.yml                     Build on PR and push to main
│   └── release.yml                Build, package and publish on a v* tag
├── src/
│   ├── csharpDialog.Core/         Library: config model, parsers, services
│   │   ├── CommandLineParser.cs   Every CLI flag is parsed here
│   │   ├── DialogConfiguration.cs The configuration model and its defaults
│   │   ├── IDialogService.cs      The front-end contract, the factory, and the console front end
│   │   ├── Models/                Command, ListItemConfiguration, ListItemStatus, JSON schema, themes
│   │   └── Services/              Command parser/watcher, Cimian monitor, first-run detection,
│   │                              icon cache, file log, and the unwired manager/theme/style layers
│   ├── csharpDialog.CLI/          dialog.exe — the shipped entry point
│   └── csharpDialog.WPF/          The windows and WpfDialogService; also builds its own host exe
├── docs/                          Long-form notes (predate this wiki; verify against source)
├── examples/                      PowerShell examples
├── build.ps1                      Local build, sign, MSI and .pkg packaging
├── build-info.yaml                Cimian package metadata template
└── CsharpDialog.sln

Note the disk casing: directories are CsharpDialog.*, the namespaces and project files are csharpDialog.*. Both spellings appear in paths inside the repository and in build.ps1; this is harmless on Windows but will bite on a case-sensitive filesystem.

Prerequisites

Tool Version Needed for
.NET SDK 10.0 (preview quality is what CI uses) Everything. All three projects target net10.0-windows
WiX Toolset CLI 6.x, installed as a global dotnet tool MSI output only
Windows SDK (signtool.exe) any recent Signing only
dotnet tool install --global wix --version "6.*"

Build and run

dotnet build CsharpDialog.sln -c Release

The CLI's AssemblyName is dialog, so the binary is src\csharpDialog.CLI\bin\Release\net10.0-windows\dialog.exe.

Run it in place:

dotnet run --project src/csharpDialog.CLI -- --window --title "Dev" --message "Hello"

.vscode/tasks.json defines build and run-cli tasks for the same two commands.

Tests

There is no test project. dotnet test finds nothing and reports success — do not read that as a passing suite. Unit tests are on the repository's roadmap as unimplemented.

Verification today is manual. The examples/ directory is the closest thing to a test matrix, covering basic messages, progress, list items, buttons, sizing, the command file, fullscreen, kiosk, an install tracker and an onboarding flow. Note that several of those scripts pass --progressbar, which is not a real flag — it is silently ignored, so they still work, but do not copy that token into new code.

A quick smoke test of the paths that matter:

dialog --window --title "Smoke" --message "Window front end" --timeout 5
$f = "$env:TEMP\smoke.txt"; Remove-Item $f -ErrorAction SilentlyContinue
Start-Process dialog -ArgumentList "--window","--progress","--commandfile",$f
Start-Sleep 3
Add-Content $f "listitem: add, title: Item, status: wait" -Encoding UTF8
Add-Content $f "listitem: update, title: Item, status: success" -Encoding UTF8
Add-Content $f "progress: 100" -Encoding UTF8
Add-Content $f "quit" -Encoding UTF8

Packaging and release

build.ps1

Switch Effect
-Build dotnet build the solution and queue the built executables for signing
-Sign Sign the queued files with signtool
-Msi Publish self-contained per runtime, harvest the output into a generated WiX v4-schema source, and build an MSI per architecture
-Pkg Publish per runtime and assemble a Cimian .pkg (a zip of build-info.yaml, payload/ and scripts/postinstall.ps1)
-All All four
-SkipMsi, -SkipPkg Turn the corresponding step back off
-Configuration Default Release
-Runtime Default win-x64, win-arm64

With no switches at all it behaves as -Build -Sign -Msi -Pkg. Artifacts land in dist\.

Things worth knowing before changing it:

  • Version is a build timestamp, yyyy.MM.dd.HHmm, generated at run time. The MSI version is that with the century stripped and leading zeros removed (2026.09.03.1030 → 26.9.3.1030) so it fits the installer's four-field limit.
  • The upgrade code is persisted in a .wix-upgrade-code file at the repo root, created on first use. Do not delete it — a new upgrade code makes every future MSI a side-by-side install rather than an upgrade.
  • Component GUIDs are regenerated on every build. Combined with the harvest-everything approach this is fine for a fresh install and an upgrade that replaces the whole payload, but it rules out patching.
  • The MSI adds INSTALLFOLDER to the system PATH and writes HKLM\Software\csharpDialog with InstallPath and Version.
  • Published output is self-contained, so releases do not require a .NET runtime on the target.
  • Publishing signs the published executables before they are packaged, so the MSI and .pkg payloads carry signatures rather than being signed only on the outside.
  • Signing reads the certificate subject from SIGNING_CERT_CN, resolves signtool.exe from PATH, SIGNTOOL_PATH/SIGNTOOL, or the newest Windows Kits bin directory, and retries up to four times across three timestamp authorities. If signtool cannot be found it warns once and skips — a build can therefore "succeed" while signing nothing.

CI workflow

ci.yml runs on pull requests to main and pushes to main, on windows-latest, with a 30-minute timeout and per-ref concurrency cancellation. It sets up .NET 10 (preview quality), caches NuGet by the hash of the project files, restores, and builds the solution in Release. It does not run tests, package, or sign.

Release workflow

release.yml runs only on a v* tag push, on windows-latest, with contents: write. In order it:

  1. Derives the version by stripping the leading v from the tag name.
  2. Installs the WiX 6 CLI.
  3. Runs .\build.ps1 -Build -Msi -Pkg — note -Sign is deliberately absent, so everything the workflow produces is unsigned.
  4. Collects *.msi, *.pkg, *.nupkg, *.exe and *.zip from dist\ into release\.
  5. Zips the win-x64 and win-arm64 publish directories as csharpdialog-x64.zip and csharpdialog-arm64.zip, failing the run if either publish directory is missing.
  6. Generates GitHub release notes via the API, strips the contributor attributions and the "New Contributors" section, prepends a build-provenance block (SDK version, runner OS, a link to the run) and appends signing instructions, then creates the release with every artifact attached.

Merging to main ships nothing. A release only happens when a v* tag is pushed, and what it publishes is unsigned. Any environment that requires signed binaries has to sign the release artifacts itself, or build from source with build.ps1 -Sign and a certificate available.

To cut a release:

git tag v1.2.0
git push origin v1.2.0

Coding conventions

  • C# with nullable reference types and implicit usings enabled across all three projects.
  • Namespaces are csharpDialog.* (lower-case c). Two files use CSharpDialog.* for the theme and styling models; both spellings are imported where needed.
  • Front ends are added by implementing IDialogService. The interface is large because it carries the unfinished JSON, theming, styling and shell-execution surface; returning Task.FromResult(false) from members you do not support is the established pattern.
  • New CLI flags go in the single switch in CommandLineParser.ParseArguments, and must be added to ShowHelp() in the same file. Match the existing shape: lowercase the flag, consume the value token with i++ only when a value was actually taken.
  • New command-file verbs must be added to CommandParser.ValidCommands and given a case in WpfDialogService.ProcessCommandSync — a verb in the list without a handler is the root cause of most of the accepted-but-ignored commands documented in this wiki.
  • Logging goes through FileLog (Debug/Info/Warn/Error). It never throws: a log that cannot be written must not take a dialog down. Keep script-parseable output on stdout and diagnostics in the log.
  • The [DEBUG] Console.WriteLine calls throughout the WPF path are development scaffolding that reached the shipped build. New code should prefer FileLog so stdout stays useful to callers.

Contributing

See CONTRIBUTING.md in the repository. Issues and pull requests go to windowsadmins/csharpdialog.

The repository is public. Keep hostnames, machine names, user names, certificate subject names, tenant identifiers and internal URLs out of code, commits, tests and issues — use neutral placeholders.

Clone this wiki locally