Winstaller.Sdk
0.0.3
dotnet add package Winstaller.Sdk --version 0.0.3
NuGet\Install-Package Winstaller.Sdk -Version 0.0.3
<PackageReference Include="Winstaller.Sdk" Version="0.0.3" />
<PackageVersion Include="Winstaller.Sdk" Version="0.0.3" />
<PackageReference Include="Winstaller.Sdk" />
paket add Winstaller.Sdk --version 0.0.3
#r "nuget: Winstaller.Sdk, 0.0.3"
#:package Winstaller.Sdk@0.0.3
#addin nuget:?package=Winstaller.Sdk&version=0.0.3
#tool nuget:?package=Winstaller.Sdk&version=0.0.3
Winstaller.Sdk
The official developer SDK containing interfaces, base classes, attributes, and services for extending the Winstaller installation engine.
This package provides the core contracts required to build:
- Custom Wizard Steps: Cross-target installation steps (
IStep,StepModelBase). - WPF GUI Presentation: Graphical views for Windows installer targets (
StepViewBase,IStepView,[WindowsView]). - Console CUI Presentation: Interactive terminal views for CLI installer targets (
IConsoleStepView,IConsoleContext,[ConsoleView]). - Lifecycle Hook Scripts: Headless pre-install and post-install hooks (
IScript,[PluginName]). - Specialized Step Interfaces: Progress-driven install (
IInstallStep), uninstall (IUninstallStep), license resolution (ILicenseStep), and conditional step visibility (IStepVisibility). - Background Tasks: Long-running background operations with progress reporting (
IBackgroundRunnableStep,InstallationProgress). - Engine Services: Logging, modal dialogs, path variable resolution, localization, and embedded resource retrieval.
Target Framework & Requirements
- Target Framework:
.NET Framework 4.5(net45) — chosen for maximum out-of-the-box compatibility on client Windows machines without requiring external runtime dependencies. - UI Stack: WPF (
UseWPFenabled) for graphical views. - Language Support: C# 10+ / latest, Nullable Reference Types enabled.
Installation
Add the package to your plugin, custom step, or script project:
dotnet add package Winstaller.Sdk
Or reference it directly in your .csproj:
<ItemGroup>
<PackageReference Include="Winstaller.Sdk" Version="0.0.1" />
</ItemGroup>
Architecture & Core Concepts
1. Step Orchestration & Decoupled Views
In Winstaller, step business logic is completely decoupled from UI presentation:
IStepdefines the lifecycle, state, initialization, and navigation logic.[WindowsView(typeof(TView))]binds a step to a WPF GUI view (StepViewBase/IStepView).[ConsoleView(typeof(TConsoleView))]binds a step to an interactive Console view (IConsoleStepView).
This decoupled design enables a single step definition to run seamlessly across all target build configurations:
target: "windows"— loads the WPF GUI view.target: "console"— loads the Console terminal view.target: "both"— dual-target installer executable capable of running in either GUI or Console mode.- Silent mode (
--silent) — runs headless without rendering views.
2. Identifying Steps and Plugins
[Step("StepName")]: Declares the unique name/identifier for a wizard step. Configured ininstaller.jsonunderstepsoruninstallSteps.[PluginName("PluginName")]: Declares the identifier for a script plugin (IScript) or provides backward-compatible step naming.
Implementing Custom Steps
A full custom step consists of the step model and its corresponding presentation views.
1. Step Logic (IStep)
using System;
using System.Threading.Tasks;
using Winstaller.Sdk;
namespace MyCompany.Winstaller.Plugins
{
[Step("MyCustomStep")]
[WindowsView(typeof(MyCustomStepView))]
[ConsoleView(typeof(MyCustomStepConsoleView))]
public class MyCustomStep : IStep
{
public InstallerContext Context { get; set; } = null!;
public int CurrentStep { get; set; }
public int TotalSteps { get; set; }
public event EventHandler? Canceled;
public event EventHandler? Next;
public event EventHandler? Back;
public void Initialize(StepConfig config)
{
// Read custom properties defined in installer.json
if (config.Properties.TryGetValue("DefaultPort", out var port))
{
// Process custom properties
}
}
public void Start(IView? view)
{
if (Context.IsSilent)
{
// In silent/headless mode, advance immediately
RaiseNext();
return;
}
// Instruct the presentation host to render this step
view?.DisplayStep(this);
}
public void End()
{
// Cleanup resources if necessary
}
public Task RollbackAsync()
{
// Roll back any changes if installation is aborted
return Task.FromResult(0);
}
public void RaiseNext() => Next?.Invoke(this, EventArgs.Empty);
public void RaiseBack() => Back?.Invoke(this, EventArgs.Empty);
public void RaiseCanceled() => Canceled?.Invoke(this, EventArgs.Empty);
}
}
2. WPF GUI View (StepViewBase)
Custom GUI views inherit from StepViewBase (a WPF UserControl implementing IStepView and INotifyPropertyChanged). It provides built-in properties to configure navigation buttons in the wizard shell:
| Property | Type | Description |
|---|---|---|
Title |
string |
Wizard window title. |
Header |
string |
Top header / banner title. |
IsNextVisible / IsNextEnabled |
bool |
Controls the Next button. |
IsBackVisible / IsBackEnabled |
bool |
Controls the Back button. |
IsCancelVisible / IsCancelEnabled |
bool |
Controls the Cancel button. |
IsFinishVisible / IsFinishEnabled |
bool |
Controls the Finish button on completion steps. |
IsPerformingCriticalWork |
bool |
Disables wizard buttons during critical operations to prevent aborting. |
View Code-Behind (MyCustomStepView.xaml.cs):
using System.Windows;
using Winstaller.Sdk;
namespace MyCompany.Winstaller.Plugins
{
public partial class MyCustomStepView : StepViewBase
{
private readonly MyCustomStep _step;
public MyCustomStepView(MyCustomStep step)
{
_step = step;
Step = step;
InitializeComponent();
Loaded += MyCustomStepView_Loaded;
}
private void MyCustomStepView_Loaded(object sender, RoutedEventArgs e)
{
Title = "Configuration";
Header = "Configure Service Options";
IsBackVisible = true;
IsNextVisible = true;
IsNextEnabled = true;
IsCancelVisible = true;
}
public override void OnNext()
{
// Save state into shared context data
_step.Context.Data["PortNumber"] = PortTextBox.Text;
_step.RaiseNext();
}
public override void OnBack() => _step.RaiseBack();
public override void OnCancel() => _step.RaiseCanceled();
}
}
3. Console CUI View (IConsoleStepView)
For console and dual-target installers, implement IConsoleStepView. The IConsoleContext abstraction provides formatted, colored output and user prompt utilities:
using System.Threading.Tasks;
using Winstaller.Sdk;
namespace MyCompany.Winstaller.Plugins
{
public class MyCustomStepConsoleView : IConsoleStepView
{
public Task ExecuteAsync(IStep step, IConsoleContext console)
{
if (step is MyCustomStep customStep)
{
console.WriteLine("==================================================");
console.WriteSuccess($" Configuration: {customStep.Context.AppName}");
console.WriteLine("==================================================");
console.WriteLine();
// Interactive prompt with default value
string port = console.Prompt("Enter service port", defaultValue: "8080");
customStep.Context.Data["PortNumber"] = port;
// Confirm proceeding
bool proceed = console.PromptYesNo("Proceed with this configuration?", defaultYes: true);
if (proceed)
{
customStep.RaiseNext();
}
else
{
customStep.RaiseCanceled();
}
}
return Task.FromResult(0);
}
}
}
Specialized Step Interfaces
Winstaller provides optional specialized interfaces for custom step behavior:
IStepVisibility
Determines dynamically whether a step should appear in the wizard flow based on the current context:
public interface IStepVisibility
{
bool ShouldShow(InstallerContext context);
}
Example: Skip database configuration steps if an existing installation already has connection strings configured.
IInstallStep
Handles standard installation execution, progress reporting, and transaction rollback:
public interface IInstallStep
{
Task ExecuteInstallAsync(IStep step, IProgress<InstallationProgress>? progress);
Task RollbackAsync(IStep step);
}
IUninstallStep
Handles standard uninstallation execution and progress reporting:
public interface IUninstallStep
{
Task ExecuteUninstallAsync(IStep step, IProgress<InstallationProgress>? progress);
}
ILicenseStep
Reads and resolves localized license documents (plain text or Rich Text Format .rtf):
public interface ILicenseStep
{
Stream? GetLicenseStream(InstallerContext context);
string GetLicenseText(InstallerContext context);
bool IsRtfFormat(InstallerContext context);
}
IBackgroundRunnableStep
Designed for background workers and operations where fine-grained progress reporting is required:
public interface IBackgroundRunnableStep
{
Task ExecuteAsync(IProgress<InstallationProgress>? progress);
}
Lifecycle Hook Scripts (IScript)
Scripts run headless at specific installation phases (preInstallScripts or postInstallScripts). They are ideal for environment checks, stopping services, file cleanup, or running custom provisioning tasks:
using Winstaller.Sdk;
namespace MyCompany.Winstaller.Plugins
{
[PluginName("CleanupOldFilesScript")]
public class CleanupOldFilesScript : IScript
{
public void Execute(InstallerContext context)
{
var logger = context.GetService<ILogger>();
logger?.Info("Starting pre-installation cleanup...");
string targetDir = context.InstallationPath;
// Perform file manipulation or cleanup here
}
}
}
Engine Services & InstallerContext
InstallerContext
Passed to all steps, scripts, and views. It contains the shared installation state and engine utilities:
| Property | Type | Description |
|---|---|---|
AppName |
string |
Name of the application being installed. |
AppVersion |
string |
Version of the application. |
Publisher |
string |
Publisher name. |
ProductCode |
string |
Unique product code identifier. |
InstallationPath |
string |
Target installation folder (maps to %installation%). |
WorkingInstallationPath |
string |
Temporary staging folder during installation. |
BackupInstallationPath |
string |
Backup directory used during updates before committing. |
Data |
Dictionary<string, string> |
Shared key-value dictionary for passing data between steps and scripts. |
IsSilent |
bool |
true if running in headless/silent command-line mode. |
IsUninstall |
bool |
true if running an uninstallation workflow. |
IsUpdate |
bool |
true if performing an in-place application upgrade. |
IsBackground |
bool |
true if executing within a background worker. |
CancellationToken |
CancellationToken |
Token to observe for aborting long-running tasks. |
Localization |
ILocalizationService |
Localization service instance for current culture. |
GetService<T>() |
method |
Resolves services registered in the engine DI container. |
Available Services
Resolve engine services via context.GetService<T>():
1. ILogger
Logs messages to the installer log file and debug listeners:
var logger = context.GetService<ILogger>();
logger?.Debug("Debug details...");
logger?.Info("Installation step started.");
logger?.Warning("Optional file not found; skipping.");
logger?.Error("Encountered unexpected error.");
logger?.LogException(ex, "Failed to initialize component");
2. IDialogService (Winstaller.Sdk.Services)
Displays modal dialogs and alerts:
using Winstaller.Sdk.Services;
var dialogs = context.GetService<IDialogService>();
var result = dialogs?.Show("Do you want to create a desktop shortcut?", "Shortcut", DialogButton.YesNo, DialogIcon.Question);
if (result == DialogResult.Yes)
{
// User agreed
}
// Show retry prompt on failure
bool retry = dialogs?.ShowRetry("Connection timed out.", "Network Error") ?? false;
3. IPathVariableResolver (Winstaller.Sdk.Services)
Resolves standard installer and system directory variables (e.g. %programfiles%, %installation%, %appname%):
using Winstaller.Sdk.Services;
var resolver = context.GetService<IPathVariableResolver>();
string resolvedPath = resolver?.Resolve(@"%programfiles%\%appname%\config.json") ?? "";
4. IPathService (Winstaller.Sdk.Services)
Checks permissions and path attributes:
using Winstaller.Sdk.Services;
var pathService = context.GetService<IPathService>();
bool needsAdmin = pathService?.RequiresAdminToSave(context.InstallationPath) ?? true;
5. IResourceProvider
Accesses embedded resources compiled directly into the installer package:
var resources = context.GetService<IResourceProvider>();
if (resources?.HasResource("banner.png") == true)
{
byte[] imageBytes = resources.GetResourceBytes("banner.png");
}
Localization & Translations
Translations are defined in the installer configuration (installer.json) under the translations object:
"translations": {
"en": {
"PortStep_Title": "Port Configuration",
"PortStep_Prompt": "Enter listening port for {0}:"
},
"pl": {
"PortStep_Title": "Konfiguracja portu",
"PortStep_Prompt": "Wprowadź port nasłuchiwania dla {0}:"
}
}
Accessing Translations in Code
Use the static TranslationProvider class or context.Localization:
using Winstaller.Sdk;
// Simple string lookup
string title = TranslationProvider.GetString("PortStep_Title");
// Formatted string with parameters
string prompt = TranslationProvider.GetString("PortStep_Prompt", context.AppName);
Standard Exceptions (Winstaller.Sdk.Exceptions)
The SDK provides specific exception types to report standard installer failures cleanly:
AppLockedException: Thrown when application files are locked by running processes or folders cannot be modified. ContainsRunningProcessesandLockedFilescollections.ElevationRejectedException: Thrown when UAC elevation is rejected by the user or administrator rights cannot be acquired.ServiceRunningException: Thrown when an active Windows Service could not be stopped.
using Winstaller.Sdk.Exceptions;
if (IsServiceRunning("MyService"))
{
throw new ServiceRunningException("MyService", "The Windows Service 'MyService' is active and must be stopped.");
}
Backward Compatibility Note
For compatibility with older plugins written for earlier versions of Winstaller, the Winstaller.Interfaces and Winstaller.Interfaces.Services namespaces are preserved via type aliases in Compatibility.cs. For all new development, use the Winstaller.Sdk namespaces.
License
Refer to the LICENSE.txt file packed with this package.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET Framework | net45 is compatible. net451 was computed. net452 was computed. net46 was computed. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
-
.NETFramework 4.5
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.