Overview
The Windows Package Manager COM API provides comprehensive progress tracking and event handling for both package management and configuration operations.Package Management Progress
InstallProgress
Tracks installation progress through multiple states.struct InstallProgress
{
PackageInstallProgressState State;
UInt64 BytesDownloaded;
UInt64 BytesRequired;
Double DownloadProgress;
Double InstallationProgress;
}
Queued- Installation queuedDownloading- Downloading installerInstalling- Running installerPostInstall- Post-install cleanupFinished- Operation complete
var operation = manager.InstallPackageAsync(package, options);
operation.Progress = (op, progress) =>
{
switch (progress.State)
{
case PackageInstallProgressState.Queued:
Console.WriteLine("Installation queued...");
break;
case PackageInstallProgressState.Downloading:
if (progress.BytesRequired > 0)
{
var mbDownloaded = progress.BytesDownloaded / 1024.0 / 1024.0;
var mbRequired = progress.BytesRequired / 1024.0 / 1024.0;
Console.WriteLine(
$"Downloading: {mbDownloaded:F2} MB / {mbRequired:F2} MB " +
$"({progress.DownloadProgress * 100:F1}%)"
);
}
else
{
Console.WriteLine($"Downloading: {progress.DownloadProgress * 100:F1}%");
}
break;
case PackageInstallProgressState.Installing:
if (progress.InstallationProgress > 0)
{
Console.WriteLine($"Installing: {progress.InstallationProgress * 100:F1}%");
}
else
{
Console.WriteLine("Installing...");
}
break;
case PackageInstallProgressState.PostInstall:
Console.WriteLine("Post-install cleanup...");
break;
case PackageInstallProgressState.Finished:
Console.WriteLine("Finished");
break;
}
};
var result = await operation;
UninstallProgress
Tracks uninstallation progress.struct UninstallProgress
{
PackageUninstallProgressState State;
Double UninstallationProgress;
}
Queued- Uninstall queuedUninstalling- Running uninstallerPostUninstall- CleanupFinished- Complete
var operation = manager.UninstallPackageAsync(package, options);
operation.Progress = (op, progress) =>
{
switch (progress.State)
{
case PackageUninstallProgressState.Queued:
Console.WriteLine("Queued for uninstallation");
break;
case PackageUninstallProgressState.Uninstalling:
if (progress.UninstallationProgress > 0)
{
Console.WriteLine(
$"Uninstalling: {progress.UninstallationProgress * 100:F1}%"
);
}
else
{
Console.WriteLine("Uninstalling...");
}
break;
case PackageUninstallProgressState.PostUninstall:
Console.WriteLine("Cleaning up...");
break;
case PackageUninstallProgressState.Finished:
Console.WriteLine("Uninstallation complete");
break;
}
};
var result = await operation;
DownloadProgress
Tracks package download progress.struct PackageDownloadProgress
{
PackageDownloadProgressState State;
UInt64 BytesDownloaded;
UInt64 BytesRequired;
Double DownloadProgress;
}
var operation = manager.DownloadPackageAsync(package, options);
operation.Progress = (op, progress) =>
{
if (progress.State == PackageDownloadProgressState.Downloading)
{
var mbDownloaded = progress.BytesDownloaded / 1024.0 / 1024.0;
var mbRequired = progress.BytesRequired / 1024.0 / 1024.0;
var percent = progress.DownloadProgress * 100;
Console.Write(
$"\rDownloading: {mbDownloaded:F2} MB / {mbRequired:F2} MB ({percent:F1}%)"
);
}
};
var result = await operation;
Console.WriteLine();
Configuration Progress
ConfigurationSetChangeData
Provides progress updates during configuration application.runtimeclass ConfigurationSetChangeData
{
ConfigurationSetChangeEventType Change { get; };
ConfigurationSetState SetState { get; };
ConfigurationUnitState UnitState { get; };
IConfigurationUnitResultInformation ResultInformation { get; };
ConfigurationUnit Unit { get; };
}
Unknown- Unknown changeSetStateChanged- Configuration set state changedUnitStateChanged- Configuration unit state changed
var operation = processor.ApplySetAsync(
configSet,
ApplyConfigurationSetFlags.None
);
operation.Progress = (op, data) =>
{
switch (data.Change)
{
case ConfigurationSetChangeEventType.SetStateChanged:
Console.WriteLine($"Configuration Set: {data.SetState}");
break;
case ConfigurationSetChangeEventType.UnitStateChanged:
var unit = data.Unit;
switch (data.UnitState)
{
case ConfigurationUnitState.Pending:
Console.WriteLine($"Pending: {unit.Type}");
break;
case ConfigurationUnitState.InProgress:
Console.WriteLine($"In Progress: {unit.Type} ({unit.Identifier})");
break;
case ConfigurationUnitState.Completed:
var result = data.ResultInformation;
if (result.ResultCode == 0)
{
Console.WriteLine($"✓ Completed: {unit.Type}");
}
else
{
Console.WriteLine($"✗ Failed: {unit.Type}");
Console.WriteLine($" Error: {result.Description}");
}
break;
case ConfigurationUnitState.Skipped:
Console.WriteLine($"Skipped: {unit.Type}");
Console.WriteLine($" Reason: {data.ResultInformation.Description}");
break;
}
break;
}
};
var result = await operation;
Test Progress
Track testing progress for configuration units.var operation = processor.TestSetAsync(configSet);
operation.Progress = (op, unitResult) =>
{
var unit = unitResult.Unit;
Console.Write($"Testing {unit.Type}... ");
switch (unitResult.TestResult)
{
case ConfigurationTestResult.Positive:
Console.WriteLine("✓ In desired state");
break;
case ConfigurationTestResult.Negative:
Console.WriteLine("✗ Not in desired state");
break;
case ConfigurationTestResult.Failed:
Console.WriteLine($"✗ Test failed: {unitResult.ResultInformation.Description}");
break;
case ConfigurationTestResult.NotRun:
Console.WriteLine("- Not run");
break;
}
};
var result = await operation;
Event Subscription
ConfigurationSet Events
Subscribe to configuration set change events.var configSet = openResult.Set;
// Subscribe to events
configSet.ConfigurationSetChange += OnConfigurationSetChange;
void OnConfigurationSetChange(
ConfigurationSet sender,
ConfigurationSetChangeData data)
{
Console.WriteLine($"Event: {data.Change}");
Console.WriteLine($"Set State: {data.SetState}");
if (data.Change == ConfigurationSetChangeEventType.UnitStateChanged)
{
Console.WriteLine($"Unit: {data.Unit.Type}");
Console.WriteLine($"Unit State: {data.UnitState}");
}
}
// Apply configuration (events will fire)
var result = await processor.ApplySetAsync(
configSet,
ApplyConfigurationSetFlags.None
);
// Unsubscribe
configSet.ConfigurationSetChange -= OnConfigurationSetChange;
Configuration History Events
Monitor all configuration changes system-wide.var processor = new ConfigurationProcessor(factory);
processor.ConfigurationChange += OnConfigurationChange;
void OnConfigurationChange(
ConfigurationSet configSet,
ConfigurationChangeData data)
{
switch (data.Change)
{
case ConfigurationChangeEventType.SetAdded:
Console.WriteLine($"New configuration set: {configSet.Name}");
Console.WriteLine($" ID: {data.InstanceIdentifier}");
break;
case ConfigurationChangeEventType.SetStateChanged:
Console.WriteLine($"Configuration state changed: {configSet.Name}");
Console.WriteLine($" State: {data.State}");
break;
case ConfigurationChangeEventType.SetRemoved:
Console.WriteLine($"Configuration removed");
Console.WriteLine($" ID: {data.InstanceIdentifier}");
break;
}
}
// Events will fire for any configuration changes
Diagnostics Events
Monitor diagnostic messages from the configuration system.processor.MinimumLevel = DiagnosticLevel.Informational;
processor.Diagnostics += OnDiagnostics;
void OnDiagnostics(object sender, IDiagnosticInformation diagnostics)
{
var prefix = diagnostics.Level switch
{
DiagnosticLevel.Verbose => "[VERBOSE]",
DiagnosticLevel.Informational => "[INFO]",
DiagnosticLevel.Warning => "[WARN]",
DiagnosticLevel.Error => "[ERROR]",
DiagnosticLevel.Critical => "[CRITICAL]",
_ => "[UNKNOWN]"
};
Console.WriteLine($"{prefix} {diagnostics.Message}");
}
UI Progress Patterns
Console Progress Bar
class ProgressBar : IDisposable
{
private readonly int _width = 50;
private readonly int _row;
private double _current;
public ProgressBar()
{
_row = Console.CursorTop;
}
public void Update(double progress, string status = "")
{
_current = Math.Clamp(progress, 0, 1);
Console.SetCursorPosition(0, _row);
int filled = (int)(_width * _current);
string bar = new string('█', filled) + new string('░', _width - filled);
Console.Write($"[{bar}] {_current * 100:F1}% {status}");
}
public void Dispose()
{
Console.WriteLine();
}
}
// Usage
using var progress = new ProgressBar();
var operation = manager.InstallPackageAsync(package, options);
operation.Progress = (op, p) =>
{
if (p.State == PackageInstallProgressState.Downloading)
{
progress.Update(p.DownloadProgress, "Downloading");
}
else if (p.State == PackageInstallProgressState.Installing)
{
progress.Update(p.InstallationProgress, "Installing");
}
};
var result = await operation;
WPF/WinUI Progress
// In your ViewModel
public class InstallViewModel : INotifyPropertyChanged
{
private double _progress;
private string _status;
public double Progress
{
get => _progress;
set => SetProperty(ref _progress, value);
}
public string Status
{
get => _status;
set => SetProperty(ref _status, value);
}
public async Task InstallAsync(CatalogPackage package)
{
var options = new InstallOptions
{
PackageInstallMode = PackageInstallMode.Silent
};
var operation = _manager.InstallPackageAsync(package, options);
operation.Progress = (op, progress) =>
{
// Update on UI thread
DispatcherQueue.TryEnqueue(() =>
{
switch (progress.State)
{
case PackageInstallProgressState.Downloading:
Progress = progress.DownloadProgress;
Status = $"Downloading... {Progress * 100:F0}%";
break;
case PackageInstallProgressState.Installing:
Progress = progress.InstallationProgress > 0
? progress.InstallationProgress
: 0.5;
Status = "Installing...";
break;
}
});
};
var result = await operation;
if (result.Status == InstallResultStatus.Ok)
{
Progress = 1.0;
Status = "Installation complete";
}
}
}
Related Topics
- PackageManager - Package management APIs
- ConfigurationProcessor - Configuration APIs
- Error Handling - Handle errors during operations