.NET runtime bootstrapper for Windows applications
$ dotnet add package DotnetRuntimeBootstrapper✅ Project status: active. What does it mean?
.NET Runtime Bootstrapper replaces the default application host exe file, generated by MSBuild for Windows executables, with a fully featured bootstrapper that can automatically download and install .NET runtime and other missing components required by your application.
💬 If you want to chat, join my Discord server.
📦 NuGet: dotnet add package DotnetRuntimeBootstrapper
Currently, .NET offers two main ways of distributing applications: framework-dependent deployment and self-contained deployment. Both of them come with a set of obvious and somewhat less obvious drawbacks:
dll file) is portable in the sense that it can run on any platform where the target runtime is supported, the application host (the exe file) is a native executable built for a specific platform (by default, the same platform as the dev machine). This means that if the application was built on Windows x64, a user running on Windows x86 will not be able to launch the application through the exe file, even if they have the correct runtime installed (dotnet myapp.dll will still work, however)..NET Runtime Bootstrapper is a project that provides an alternative third deployment option. Combining the best of both framework-dependent and self-contained deployments, bootstrapped deployment eliminates the above-mentioned issues.
hostfxr.dllhttps://user-images.githubusercontent.com/1935960/123711355-346ed380-d825-11eb-982f-6272a9e55ebd.mp4
To add .NET Runtime Bootstrapper to your project, simply install the corresponding NuGet package.
MSBuild will automatically pick up the props and targets files provided by the package and integrate them inside the build process.
After that, no further configuration is required.
⚠ Bootstrapper only supports applications targeting .NET Core 3.0 or higher.
⚠ Bootstrapper's user experience is optimized for desktop applications. Other application models are supported in theory but not necessarily in practice.
In order to create a sharable distribution of your application, run dotnet publish as you normally would.
This should produce the following files in the output directory:
MyApp.exe <-- bootstrapper's application host
MyApp.exe.config <-- .NET config required by the application host
MyApp.runtimeconfig.json <-- runtime config required by the application host
MyApp.dll <-- your application
MyApp.pdb
MyApp.deps.json
[... other application dependencies ...]Make sure to include all marked files in your application distribution.
⚠ Single-file deployment (
/p:PublishSingleFile=true) is not supported by the bootstrapper.
The client-facing side of .NET Runtime Bootstrapper is implemented as a custom .NET runtime host. Internally, it's a pre-compiled managed executable built against legacy .NET Framework v3.5, which allows it to run out-of-the-box on all operating systems starting with Windows 7.
When the user runs the application through the bootstrapper, it executes these steps:
hostfxr.dllruntimeconfig.json fileWhen the bootstrapper is created, the build task injects important native resources from the target assembly into the application host:
<ApplicationManifest> project property.<ApplicationIcon> project property.<FileVersion>, <InformationalVersion>, <Product>, <Copyright>, and other similar project properties.Additionally, the version info resource is further modified to contain the following attributes:
InternalName set to the application host's file name.OriginalName set to the application host's file name.AppHost set to .NET Runtime Bootstrapper (vX.Y.Z) where X.Y.Z is the version of the bootstrapper.By default, bootstrapper is only created when publishing the project (i.e. when running dotnet publish).
If you want to also have it created on every build, set the <GenerateBootstrapperOnBuild> project property to true:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<!-- ... -->
<!-- Create bootstrapper on every build, in addition to every publish -->
<GenerateBootstrapperOnBuild>true</GenerateBootstrapperOnBuild>
</PropertyGroup>
<!-- ... -->
</Project>⚠ Bootstrapper's application host does not support debugging. In order to retain debugging capabilities of your application during local development, keep
<GenerateBootstrapperOnBuild>set tofalse(default).
If the build process does not seem to produce the bootstrapper correctly, you may be able to get more information by running the command with higher verbosity.
For example, running dotnet publish --verbosity normal on DotnetRuntimeBootstrapper.Demo project should produce output that contains the following section:
CreateBootstrapperAfterPublish:
Extracting apphost...
Extracted apphost to 'f:\Projects\Softdev\DotnetRuntimeBootstrapper\DotnetRuntimeBootstrapper.Demo\bin\Debug\net6.0-windows\DotnetRuntimeBootstrapper.Demo.exe'.
Extracted apphost config to 'f:\Projects\Softdev\DotnetRuntimeBootstrapper\DotnetRuntimeBootstrapper.Demo\bin\Debug\net6.0-windows\DotnetRuntimeBootstrapper.Demo.exe.config'.
Injecting target binding...
Injected target binding to 'DotnetRuntimeBootstrapper.Demo.exe'.
Injecting manifest...
Injected manifest to 'DotnetRuntimeBootstrapper.Demo.exe'.
Injecting icon...
Injected icon to 'DotnetRuntimeBootstrapper.Demo.exe'.
Injecting version info...
Injected version info to 'DotnetRuntimeBootstrapper.Demo.exe'.
Bootstrapper created successfully.In the event of a fatal error, in addition to showing a message to the user, bootstrapper will also produce a timestamped error dump in the application's directory (for example, AppHost_Error_20211205001042.txt).
If the bootstrapper does not have sufficient permissions to create a file in that directory, it will write it to %LocalAppData%\Tyrrrz\DotnetRuntimeBootstrapper instead.
The dump has the following format:
Timestamp: 05.12.2021 0:10:42 +02:00
AppHost: .NET Runtime Bootstrapper v2.0.0 (https://github.com/Tyrrrz/DotnetRuntimeBootstrapper)
Message: System.Exception: Test failure
at DotnetRuntimeBootstrapper.AppHost.Program.Run(String[] args)
at DotnetRuntimeBootstrapper.AppHost.Program.Main(String[] args)