Skip to main content

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;
}
PackageInstallProgressState values:
  • Queued - Installation queued
  • Downloading - Downloading installer
  • Installing - Running installer
  • PostInstall - Post-install cleanup
  • Finished - 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;
}
PackageUninstallProgressState values:
  • Queued - Uninstall queued
  • Uninstalling - Running uninstaller
  • PostUninstall - Cleanup
  • Finished - 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; };
}
ConfigurationSetChangeEventType values:
  • Unknown - Unknown change
  • SetStateChanged - Configuration set state changed
  • UnitStateChanged - 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";
        }
    }
}

Build docs developers (and LLMs) love