reactivemarbles / Extensions.Hosting

An Extension of the Microsoft.Extensions.Hosting library with the aim of allowing windows applications to use the hosting base.

Repository from Github https://github.comreactivemarbles/Extensions.HostingRepository from Github https://github.comreactivemarbles/Extensions.Hosting

NOTE: The namespacing has been changed to ReactiveMarbles.Extensions.Hosting. Please update your references to the new namespace.

ReactiveMarbles.Extensions.Hosting

Extensions for Microsoft.Extensions.Hosting that bring WPF, WinForms, WinUI, ReactiveUI, plug-ins, single-instance control, and common host utilities to desktop apps.

This repository supports both classic IHostBuilder and the newer IHostApplicationBuilder hosting model introduced in .NET 8+. Existing IHostBuilder APIs remain unchanged; equivalent IHostApplicationBuilder overloads are available where appropriate.

Supported targets include .NET Framework 4.6.2/4.8, .NET Standard 2.0, and .NET 8/9 (Windows where applicable).

Quick start

Choose a hosting model:

  • IHostBuilder (generic host): Host.CreateDefaultBuilder(args)
  • IHostApplicationBuilder (new app builder): Host.CreateApplicationBuilder(args)

Example: WPF app with IHostBuilder

using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.Wpf;

var host = Host.CreateDefaultBuilder(args)
    .ConfigureWpf(wpf =>
    {
        // Optional: register Application type and windows via the WpfBuilder
        wpf.ApplicationType = typeof(App);
        wpf.WindowTypes.Add(typeof(MainWindow));
        wpf.ConfigureContextAction = ctx => ctx.ShutdownMode = ShutdownMode.OnMainWindowClose;
    })
    .UseWpfLifetime(ShutdownMode.OnMainWindowClose)
    .Build();

await host.RunAsync();

Example: WPF app with IHostApplicationBuilder

using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.Wpf;

var builder = Host.CreateApplicationBuilder(args)
    .ConfigureWpf(wpf =>
    {
        wpf.ApplicationType = typeof(App);
        wpf.WindowTypes.Add(typeof(MainWindow));
        wpf.ConfigureContextAction = ctx => ctx.ShutdownMode = ShutdownMode.OnMainWindowClose;
    })
    .UseWpfLifetime(ShutdownMode.OnMainWindowClose);

await builder.Build().RunAsync();

Packages and APIs

The following sections outline the main features, their APIs for both hosting models, and example usage.

WPF (CP.Extensions.Hosting.Wpf)

Namespace: ReactiveMarbles.Extensions.Hosting.Wpf

  • ConfigureWpf
    • IHostBuilder ConfigureWpf(this IHostBuilder, Action?)
    • IHostApplicationBuilder ConfigureWpf(this IHostApplicationBuilder, Action?)
    • Use IWpfBuilder to set:
      • Type? ApplicationType
      • Application? Application (optional existing instance)
      • IList WindowTypes
      • Action? ConfigureContextAction
  • UseWpfLifetime
    • IHostBuilder UseWpfLifetime(this IHostBuilder, ShutdownMode = OnLastWindowClose)
    • IHostApplicationBuilder UseWpfLifetime(this IHostApplicationBuilder, ShutdownMode = OnLastWindowClose)
  • IWpfContext
    • ShutdownMode ShutdownMode { get; set; }
    • Application? WpfApplication { get; set; }
    • Dispatcher Dispatcher { get; }
    • bool IsLifetimeLinked { get; set; } (set internally when using UseWpfLifetime)

Example (builder model): see Quick start above.

WinForms (CP.Extensions.Hosting.WinForms)

Namespace: ReactiveMarbles.Extensions.Hosting.WinForms

  • ConfigureWinForms
    • IHostBuilder ConfigureWinForms(this IHostBuilder, Action?)
    • IHostApplicationBuilder ConfigureWinForms(this IHostApplicationBuilder, Action?)
  • ConfigureWinForms() where TView : Form
    • IHostBuilder ConfigureWinForms(...)
    • IHostApplicationBuilder ConfigureWinForms(...)
    • Registers the main form and, if it implements IWinFormsShell, also registers it as IWinFormsShell
  • ConfigureWinFormsShell() where TShell : Form, IWinFormsShell
    • IHostBuilder ConfigureWinFormsShell()
    • IHostApplicationBuilder ConfigureWinFormsShell()
  • UseWinFormsLifetime
    • IHostBuilder UseWinFormsLifetime(this IHostBuilder)
    • IHostApplicationBuilder UseWinFormsLifetime(this IHostApplicationBuilder)
  • IWinFormsContext
    • bool EnableVisualStyles { get; set; }
    • Dispatcher? Dispatcher { get; set; } (WinForms dispatcher abstraction)

Example (application builder):

using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.WinForms;

var builder = Host.CreateApplicationBuilder(args)
    .ConfigureWinForms(ctx =>
    {
        ctx.EnableVisualStyles = true;
    })
    .ConfigureWinFormsShell<MainForm>()
    .UseWinFormsLifetime();

await builder.Build().RunAsync();

WinUI (CP.Extensions.Hosting.WinUI)

Namespace: ReactiveMarbles.Extensions.Hosting.WinUI

  • ConfigureWinUI<TApp, TAppWindow>() where TApp : Microsoft.UI.Xaml.Application where TAppWindow : Microsoft.UI.Xaml.Window
    • IHostBuilder ConfigureWinUI<TApp, TAppWindow>()
    • IHostApplicationBuilder ConfigureWinUI<TApp, TAppWindow>()
  • IWinUIContext
    • Window? AppWindow { get; set; }
    • Type? AppWindowType { get; set; }
    • DispatcherQueue? Dispatcher { get; set; }
    • Application? WinUIApplication { get; set; }

Example (application builder):

using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.WinUI;

var builder = Host.CreateApplicationBuilder(args)
    .ConfigureWinUI<App, MainWindow>();

await builder.Build().RunAsync();

ReactiveUI integration

Namespaces: ReactiveMarbles.Extensions.Hosting.ReactiveUI (per UI stack)

  • ConfigureSplatForMicrosoftDependencyResolver
    • IHostBuilder ConfigureSplatForMicrosoftDependencyResolver(this IHostBuilder)
    • IHostApplicationBuilder ConfigureSplatForMicrosoftDependencyResolver(this IHostApplicationBuilder)
  • Variants:
    • WPF: Adds WithWpf() builder stage
    • WinForms: Adds WithWinForms() builder stage
    • WinUI: Adds WithWinUI() builder stage
  • MapSplatLocator
    • IHost MapSplatLocator(this IHost host, Action<IServiceProvider?> containerFactory)

Example (WPF + ReactiveUI with application builder):

using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.ReactiveUI;
using ReactiveMarbles.Extensions.Hosting.Wpf;

var builder = Host.CreateApplicationBuilder(args)
    .ConfigureSplatForMicrosoftDependencyResolver()
    .ConfigureWpf(wpf =>
    {
        wpf.ApplicationType = typeof(App);
        wpf.WindowTypes.Add(typeof(MainWindow));
    })
    .UseWpfLifetime();

await builder.Build().RunAsync();

Plug-in system (CP.Extensions.Hosting.Plugins)

Namespace: ReactiveMarbles.Extensions.Hosting.Plugins

  • ConfigurePlugins
    • IHostBuilder ConfigurePlugins(this IHostBuilder, Action<IPluginBuilder?> configure)
    • IHostApplicationBuilder ConfigurePlugins(this IHostApplicationBuilder, Action<IPluginBuilder?> configure)
  • IPluginBuilder key options (typical):
    • UseContentRoot (bool): also scan content root
    • IncludeFrameworks(params string[] globs)
    • IncludePlugins(params string[] globs)
    • AddScanDirectories(params string[] directories)
    • PluginMatcher / FrameworkMatcher (advanced globbing)
    • AssemblyScanFunc: Func<Assembly, IEnumerable<IPlugin?>> for discovery

Example (application builder):

using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.Plugins;

var builder = Host.CreateApplicationBuilder(args)
    .ConfigurePlugins(plugins =>
    {
        plugins.UseContentRoot = true;
        // Add framework assemblies and plugin patterns
        plugins.IncludeFrameworks(@"\\netstandard2.0\\*.FrameworkLib.dll");
        plugins.IncludePlugins(@"\\Plugins\\{runtime}\\ReactiveMarbles.Plugin.*.dll");
    });

await builder.Build().RunAsync();

Single instance (CP.Extensions.Hosting.SingleInstance)

Namespace: ReactiveMarbles.Extensions.Hosting.AppServices

  • ConfigureSingleInstance
    • IHostBuilder ConfigureSingleInstance(this IHostBuilder, Action configure)
    • IHostApplicationBuilder ConfigureSingleInstance(this IHostApplicationBuilder, Action configure)
    • IHostBuilder ConfigureSingleInstance(this IHostBuilder, string mutexId)
    • IHostApplicationBuilder ConfigureSingleInstance(this IHostApplicationBuilder, string mutexId)
  • IMutexBuilder
    • string MutexId { get; set; }
    • Action<IHostEnvironment, ILogger>? WhenNotFirstInstance { get; set; }

Example (application builder):

using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.AppServices;

var builder = Host.CreateApplicationBuilder(args)
    .ConfigureSingleInstance(cfg =>
    {
        cfg.MutexId = "{ea031523-3a63-45e5-85f2-6fa75fbf37ed}";
        cfg.WhenNotFirstInstance = (env, logger) =>
            logger.LogWarning("Application {0} already running.", env.ApplicationName);
    });

await builder.Build().RunAsync();

Service host utilities (CP.Extensions.Hosting.PluginService)

Namespace: ReactiveMarbles.Extensions.Hosting.PluginService

  • UseServiceBaseLifetime
    • IHostBuilder UseServiceBaseLifetime(this IHostBuilder)
    • IHostApplicationBuilder UseServiceBaseLifetime(this IHostApplicationBuilder)
  • UseConsoleLifetime (IHostApplicationBuilder only)
    • IHostApplicationBuilder UseConsoleLifetime(this IHostApplicationBuilder)
  • RunAsServiceAsync
    • Task RunAsServiceAsync(this IHostBuilder, CancellationToken = default)
    • Task RunAsServiceAsync(this HostApplicationBuilder, CancellationToken = default)
  • ServiceHost
    • Task Create(Type type, string[] args, Func<IHostBuilder?, IHostBuilder?>? configureHostBuilder = null, Action? configureHost = null, string nameSpace = "ReactiveMarbles.Plugin", string? targetRuntime = null)
    • Task CreateApplication(Type type, string[] args, Func<IHostApplicationBuilder?, IHostApplicationBuilder?>? configureHostBuilder = null, Action? configureHost = null, string nameSpace = "ReactiveMarbles.Plugin", string? targetRuntime = null)

Example (service/console dual mode using IHostApplicationBuilder):

using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.PluginService;

var builder = Host.CreateApplicationBuilder(args)
    .UseContentRoot(Directory.GetCurrentDirectory())
    .ConfigureLogging()
    .ConfigureConfiguration(args)
    .UseConsoleLifetime(); // or .UseServiceBaseLifetime() for Windows Service

await builder.Build().RunAsync();

Example (helper):

using ReactiveMarbles.Extensions.Hosting.PluginService;

await ServiceHost.CreateApplication(
    typeof(Program),
    args,
    hb => hb // external builder customization
            .ConfigurePlugins(pb => { /* plugin globs */ }),
    host => { /* use host.Services */ },
    nameSpace: "ReactiveMarbles.Plugin");

Identity + Entity Framework Core

Namespaces:

  • ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore (SqlServer)
  • ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite (Sqlite)

APIs (service collection extensions used inside UseWebHostServices):

  • IServiceCollection UseEntityFrameworkCoreSqlServer<TContext, TUser, TRole>(WebHostBuilderContext, string connectionStringName, ServiceLifetime = Scoped)
  • IServiceCollection UseEntityFrameworkCoreSqlServer<TContext, TUser>(WebHostBuilderContext, string connectionStringName, ServiceLifetime = Scoped)
  • IServiceCollection UseEntityFrameworkCoreSqlite<TContext, TUser, TRole>(WebHostBuilderContext, string connectionStringName, ServiceLifetime = Scoped)
  • IServiceCollection UseEntityFrameworkCoreSqlite<TContext, TUser>(WebHostBuilderContext, string connectionStringName, ServiceLifetime = Scoped)

Host/web host wiring:

  • IHostBuilder UseWebHostServices(this IHostBuilder, Action<WebHostBuilderContext, IServiceCollection> configureServices, bool validateScopes = false)
  • IHostBuilder UseWebHostServices(this IHostBuilder, Action<WebHostBuilderContext, IServiceCollection> configureServices, Func<IWebHostBuilder, IWebHostBuilder> configureWebHost, bool validateScopes = false)
  • IHostBuilder UseWebHostServices(this IHostBuilder, Action<WebHostBuilderContext, IServiceCollection> configureServices, Func<IWebHostBuilder, IWebHostBuilder> configureWebHost, Func<IApplicationBuilder, IApplicationBuilder> configureApp, bool validateScopes = false)

Example:

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore;

var host = Host.CreateDefaultBuilder(args)
    .UseWebHostServices((whb, services) =>
    {
        services.UseEntityFrameworkCoreSqlServer<AppDbContext, IdentityUser, IdentityRole>(whb, "DefaultConnection");
    })
    .Build();

await host.RunAsync();

Reactive examples

  • WPF + ReactiveUI + Single instance:
var builder = Host.CreateApplicationBuilder(args)
    .ConfigureSplatForMicrosoftDependencyResolver()
    .ConfigureWpf(wpf =>
    {
        wpf.ApplicationType = typeof(App);
        wpf.WindowTypes.Add(typeof(MainWindow));
    })
    .UseWpfLifetime()
    .ConfigureSingleInstance("{ea031523-3a63-45e5-85f2-6fa75fbf37ed}");

await builder.Build().RunAsync();

Notes

  • WPF/WinForms/WinUI components target Windows only.
  • When using IHostApplicationBuilder, prefer chaining extension methods that return IHostApplicationBuilder so you can call Build() on the final HostApplicationBuilder instance.
  • Plugin scanning uses glob patterns. Ensure your plugin folders are copied to output and the patterns match your runtime folder (e.g., Plugins\net9.0-windows\Your.Plugin.*.dll).

Legacy snippets (IHostBuilder only)

ReactiveMarbles.Extensions.Hosting.Plugins

.ConfigurePlugins(pluginBuilder =>
{
    Console.ForegroundColor = ConsoleColor.Yellow;
    Console.WriteLine("Running using dotNet {0}", Environment.Version);

    var process = Process.GetCurrentProcess();
    var fullPath = process.MainModule?.FileName?.Replace(process.MainModule.ModuleName!, string.Empty);
    Console.WriteLine("Add Scan Directories: {0}", fullPath);
    pluginBuilder?.AddScanDirectories(fullPath!);

    pluginBuilder?.IncludeFrameworks(@"\netstandard2.0\*.FrameworkLib.dll");

    var runtime = Path.GetFileName(AppContext.BaseDirectory);
    Console.WriteLine(@"Include Plugins from: \Plugins\{0}\{1}*.dll", runtime, "ReactiveMarbles.Plugin");
    pluginBuilder?.IncludePlugins(@$"\Plugins\{runtime}\{{YourPluginNamespace}}*.dll");
    Console.ResetColor();
})

ReactiveMarbles.Extensions.Hosting.PluginService

await ServiceHost.Create(
    typeof(Program),
    args,
    hb => hb, // Configure the HostBuilder
    host => {}, // Configure the Host
    nameSpace: "ReactiveMarbles.Plugin").ConfigureAwait(false);

About

An Extension of the Microsoft.Extensions.Hosting library with the aim of allowing windows applications to use the hosting base.

License:MIT License


Languages

Language:C# 96.8%Language:PowerShell 1.7%Language:Shell 1.4%Language:Batchfile 0.1%