| name | extensions-hosting |
| description | Configure, compose, troubleshoot, and migrate CP.Extensions.Hosting packages for Generic Host applications. Use for WPF, WinForms, WinUI, Avalonia, MAUI, ReactiveUI/Splat, single-instance enforcement, plug-ins, Windows service hosting, normal versus .Reactive package selection, and V3.1.26-to-V4.0.0 upgrades. |
Extensions.Hosting
Use this skill when a project references a CP.Extensions.Hosting.* package or needs a
Microsoft Generic Host around a desktop UI, plug-in system, or Windows service.
Read the package-root README.md before changing code. It contains the complete API
tables, platform recipes, exceptions, and migration guide. Treat the installed package
version and the project's target framework as authoritative.
Select packages
Choose one platform host when the application owns a UI:
| Application | Host package | Optional ReactiveUI/Splat bridge |
|---|
| WPF | CP.Extensions.Hosting.Wpf | CP.Extensions.Hosting.ReactiveUI.Wpf |
| WinForms | CP.Extensions.Hosting.WinForms | CP.Extensions.Hosting.ReactiveUI.WinForms |
| WinUI | CP.Extensions.Hosting.WinUI | CP.Extensions.Hosting.ReactiveUI.WinUI |
| Avalonia | CP.Extensions.Hosting.Avalonia | CP.Extensions.Hosting.ReactiveUI.Avalonia |
| MAUI | CP.Extensions.Hosting.Maui | CP.Extensions.Hosting.ReactiveUI.Maui |
Add supporting packages only for features that are used:
CP.Extensions.Hosting.SingleInstance for mutex-backed single-instance enforcement.
CP.Extensions.Hosting.Plugins for plug-in discovery and hosted-service plug-ins.
CP.Extensions.Hosting.PluginService for console or Windows service plug-in hosts.
CP.Extensions.Hosting.Identity.EntityFrameworkCore.SqlServer or .Sqlite for
database and Identity registration.
Do not reference CP.Extensions.Hosting.MainUIThread directly unless implementing a
new platform adapter.
Choose the normal or .Reactive family
Use normal packages with the normal ReactiveUI and ReactiveUI.Primitives family.
Select the .Reactive variant for plug-in, PluginService, or ReactiveUI bridge
features when that project and its plug-ins use the
ReactiveUI.Primitives.*.Reactive or ReactiveUI.*.Reactive dependency family. A UI
bridge is not required for Plugins.Reactive or PluginService.Reactive.
| Normal | .Reactive |
|---|
CP.Extensions.Hosting.Plugins | CP.Extensions.Hosting.Plugins.Reactive |
CP.Extensions.Hosting.PluginService | CP.Extensions.Hosting.PluginService.Reactive |
CP.Extensions.Hosting.ReactiveUI.<Platform> | CP.Extensions.Hosting.ReactiveUI.<Platform>.Reactive |
ReactiveMarbles.Extensions.Hosting.Plugins | ReactiveMarbles.Extensions.Hosting.Reactive.Plugins |
ReactiveMarbles.Extensions.Hosting.PluginService | ReactiveMarbles.Extensions.Hosting.Reactive.PluginService |
ReactiveMarbles.Extensions.Hosting.ReactiveUI | ReactiveMarbles.Extensions.Hosting.Reactive.ReactiveUI |
Platform hosts (CP.Extensions.Hosting.Wpf, .WinForms, .WinUI, .Avalonia,
.Maui), CP.Extensions.Hosting.SingleInstance,
CP.Extensions.Hosting.MainUIThread, Identity helpers, and Log4Net have no
.Reactive siblings. Keep those normal packages even when using a .Reactive bridge
or plug-in package. For example, CP.Extensions.Hosting.ReactiveUI.Wpf.Reactive still
uses CP.Extensions.Hosting.Wpf.
Never mix normal and .Reactive variants of the same feature in one project.
Do not add direct Rx.NET references merely to use these hosting APIs.
Compose the host
Apply configuration before the matching lifetime method, then build once:
using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.AppServices;
using ReactiveMarbles.Extensions.Hosting.ReactiveUI;
using ReactiveMarbles.Extensions.Hosting.Wpf;
var builder = Host.CreateApplicationBuilder(args);
builder.ConfigureSingleInstance("Contoso.Desktop");
builder.ConfigureSplatForMicrosoftDependencyResolver();
builder.ConfigureWpf(wpf => wpf
.UseApplication<App>()
.UseWindow<MainWindow>());
builder.UseWpfLifetime();
using var host = builder.Build();
host.MapSplatLocator(_ => { });
await host.RunAsync().ConfigureAwait(false);
Ordering rules:
- Register application services and optional Splat integration.
- Configure single-instance enforcement before platform-hosted services when used.
- Call
ConfigureWpf, ConfigureWinForms, ConfigureWinUI, ConfigureAvalonia, or
ConfigureMaui.
- Call the matching
Use*Lifetime method where the platform exposes one.
- Build the host.
- Call
MapSplatLocator only on the built host.
- Run and dispose the host.
UseWpfLifetime and UseAvaloniaLifetime throw when called before their matching
Configure* method. WinUI installs its hosted lifetime in ConfigureWinUI; it has no
UseWinUILifetime method.
Initialize services on the UI thread
Register platform initialization services with DI. The host invokes them on the
platform UI thread:
public sealed class ThemeInitializer : IWpfService
{
public void Initialize(System.Windows.Application application)
{
application.Resources["AccentBrush"] =
new System.Windows.Media.SolidColorBrush(
System.Windows.Media.Colors.CornflowerBlue);
}
}
builder.Services.AddSingleton<IWpfService, ThemeInitializer>();
Equivalent contracts are IWinFormsService, IWinUIService, IAvaloniaService, and
IMauiService.
ReactiveUI schedulers
Call the platform ConfigureSplatForMicrosoftDependencyResolver() extension. For WPF,
V4 automatically binds RxSchedulers.MainThreadScheduler to the dispatcher owned by
the hosted application through an internal IWpfService.
Do not add or register SchedulerRegistrar. Do not use removed RxApp scheduler
workarounds.
Plug-ins
Configure scan roots and explicit include patterns:
builder.ConfigurePlugins(plugins =>
{
plugins?.AddScanDirectories(AppContext.BaseDirectory);
plugins?.IncludePlugins("plugins/**/*.Plugin.dll");
plugins?.ExcludePlugins("plugins/**/*.Disabled.dll");
plugins?.RequirePlugins();
});
Implement IPlugin.ConfigureHost to register services. Use PluginOrderAttribute for
deterministic order and HostedServiceBase<T> only when its application-lifetime
callbacks fit the service. OnStarted() runs once; implement bounded retry and
cancellation explicitly when needed.
Single-instance applications
Use ConfigureSingleInstance for hosted applications:
builder.ConfigureSingleInstance(mutex =>
{
mutex.MutexId = "Contoso.Desktop";
mutex.IsGlobal = false;
mutex.WhenNotFirstInstance = (_, logger) =>
logger.LogWarning("Another instance is already running.");
});
Use one of the four explicit ResourceMutex.Create overloads for direct locking. A
blank mutex id is invalid. Dispose the mutex on its owning thread.
Diagnose lifecycle failures
- Missing UI: verify the application/window/page type is registered and the shell
implements the platform shell interface.
- Host never exits: enable the platform lifetime after platform configuration.
- WPF/Avalonia lifetime throws during setup: reverse the
Use*Lifetime and
Configure* calls.
- MAUI/WinUI shutdown throws: the running application must expose a usable dispatcher;
rejected dispatch is an
InvalidOperationException, and cancellation is honored.
- Avalonia fails on Windows startup: enter the hosted UI from an STA thread.
- Splat cannot resolve host services: call
MapSplatLocator after Build.
- No plug-ins load: verify scan roots, glob patterns, runtime subdirectory, and
ValidatePlugin.
Migrate V3.1.26 to V4.0.0
- Upgrade every
CP.Extensions.Hosting.* reference together.
- Rebuild all consumers; V4 changes binary signatures even where source calls remain.
- Choose the normal or
.Reactive family and update namespaces as a unit.
- Replace
System.Reactive.ICancelable assumptions on HostedServiceBase<T> with
ReactiveUI.Primitives.Disposables.IsDisposed and IDisposable.
- Replace optional
ResourceMutex.Create assumptions with an explicit overload.
- Replace partial
ServiceHost.Create/CreateApplication calls with either the
two-argument overload or all six arguments.
- Make custom WPF, Avalonia, WinForms, and MAUI shells implement their new platform
base interface.
- Remove
SchedulerRegistrar; use hosted WPF scheduler wiring.
- Retarget MAUI platform-workload applications, and all
ReactiveUI.Maui
consumers, to net10 or later. The base CP.Extensions.Hosting.Maui host still
supports net9; its platform workload assets begin at net10. Move Log4Net
netstandard2.0 consumers to a supported target.
- Run a warning-as-error build and exercise startup and shutdown on the native UI.
Validate
For a consumer project, restore, build, and test the application for every target
platform/runtime, then manually exercise native startup and shutdown.
For a repository checkout:
cd src
dotnet workload restore
dotnet restore Extensions.Hosting.slnx
dotnet build Extensions.Hosting.slnx -c Release -warnaserror
dotnet test --solution Extensions.Hosting.slnx -c Release
For consumer projects, validate both the target platform and deployment runtime.
MAUI, WinUI, WPF, WinForms, and Windows service behavior cannot be fully established
by a platform-neutral unit test alone.