Bootstrapped framework-dependent deployment for .NET applications
MIT License
.NET Runtime Bootstrapper is an MSBuild plugin that replaces the default application host exe
file β generated for Windows executables during the build process β with a fully featured bootstrapper that can automatically download and install the .NET runtime and other missing components required by your application.
By using this project or its source code, for any purpose and in any shape or form, you grant your implicit agreement to all the following statements:
To learn more about the war and how you can help, click here. Glory to Ukraine! πΊπ¦
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 seeks to solve all the above problems by providing an alternative, third deployment option β bootstrapped deployment.
https://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.
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 <-- assembly config required by the application host
MyApp.runtimeconfig.json <-- runtime config required by the application host
MyApp.dll <-- main assembly of your application
MyApp.pdb
MyApp.deps.json
... other application dependencies ...
Make sure to include all highlighted files in your application distribution.
Warning: 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. It's generated during the build process by injecting project-specific instructions into a special pre-compiled executable provided by the package. Internally, the host executable is a managed .NET Framework v3.5 assembly, which allows it to run out-of-the-box on all platforms starting with Windows 7.
When the user executes the application using the bootstrapper, it goes through the following steps:
flowchart
1[Locate an existing .NET installation] --> 1a(Found?)
1a -- Yes --> 2
1a -- No --> 3
2[Run the app using latest hostfxr.dll] --> 2a(Successful?)
2a -- Yes --> 2b[Wait until exit]
2a -- No --> 3
3[Resolve target runtime from runtimeconfig.json] -->
4[Identify missing prerequisites] -->
5[Prompt the user to install them] -->
6[Download and install] --> 6a(Reboot required?)
6a -- Yes --> 6b[Prompt the user to reboot] --> 6c[Reboot] --> 1
6a -- No --> 1
When the bootstrapper is created, the build task copies all native resources from the target assembly into the application host. This includes:
24
). Configured by the <ApplicationManifest>
project property.3
and 14
). Configured by the <ApplicationIcon>
project property.16
). Contains values configured by <FileVersion>
, <InformationalVersion>
, <Product>
, <Copyright>
, and other similar project properties.Additionally, 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 (VARIANT)
where X.Y.Z
is the version of the bootstrapper and VARIANT
is either CLI
or GUI
.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 regular builds as well, set the <GenerateBootstrapperOnBuild>
project property to true
:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<!-- ... -->
<!-- Create bootstrapper on every build, in addition to every publish -->
<GenerateBootstrapperOnBuild>true</GenerateBootstrapperOnBuild>
</PropertyGroup>
<!-- ... -->
</Project>
Warning: 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).
Depending on your application type (i.e. the value of the <OutputType>
project property), the build process will generate either a CLI-based or a GUI-based bootstrapper.
You can override the default behavior and specify the preferred variant explicitly using the <BootstrapperVariant>
project property:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<!-- ... -->
<!-- Specify bootstrapper variant explicitly (GUI or CLI) -->
<BootstrapperVariant>GUI</BootstrapperVariant>
</PropertyGroup>
<!-- ... -->
</Project>
DotnetRuntimeBootstrapper relies on the autogenerated runtimeconfig.json
file to determine the version of the runtime required by your application.
You can override the default value (which is inferred from the <TargetFramework>
project property) by using the <RuntimeFrameworkVersion>
project property:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<!-- ... -->
<!-- Specify target runtime version explicitly -->
<RuntimeFrameworkVersion>8.0.1</RuntimeFrameworkVersion>
</PropertyGroup>
<!-- ... -->
</Project>
By default, the bootstrapper will prompt the user to confirm the installation of missing prerequisites.
You can disable this prompt by setting the <BootstrapperPromptRequired>
project property to false
:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<!-- ... -->
<!-- Skip the confirmation prompt and install prerequisites straight away -->
<BootstrapperPromptRequired>false</BootstrapperPromptRequired>
</PropertyGroup>
<!-- ... -->
</Project>
If the build process does not seem to generate 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
should produce an output that includes the following section:
CreateBootstrapperAfterPublish:
Bootstrapper target: 'f:\Projects\Softdev\DotnetRuntimeBootstrapper\DotnetRuntimeBootstrapper.Demo.Gui\bin\Debug\net6.0-windows\DotnetRuntimeBootstrapper.Demo.dll'.
Bootstrapper variant: 'GUI'.
Extracting apphost...
Extracted apphost to 'f:\Projects\Softdev\DotnetRuntimeBootstrapper\DotnetRuntimeBootstrapper.Demo.Gui\bin\Debug\net6.0-windows\DotnetRuntimeBootstrapper.Demo.Gui.exe'.
Extracted apphost config to 'f:\Projects\Softdev\DotnetRuntimeBootstrapper\DotnetRuntimeBootstrapper.Demo.Gui\bin\Debug\net6.0-windows\DotnetRuntimeBootstrapper.Demo.Gui.exe.config'.
Injecting target binding...
Injected target binding to 'DotnetRuntimeBootstrapper.Demo.Gui.exe'.
Injecting manifest...
Injected manifest to 'DotnetRuntimeBootstrapper.Demo.Gui.exe'.
Injecting icon...
Injected icon to 'DotnetRuntimeBootstrapper.Demo.Gui.exe'.
Injecting version info...
Injected version info to 'DotnetRuntimeBootstrapper.Demo.Gui.exe'.
Bootstrapper created successfully.
In the event of a fatal error, bootstrapper will produce an error dump (in addition to showing a message to the user).
It can be found in the Windows event log under Windows Logs β Application with event ID 1023
and source .NET Runtime
.
The dump has the following format:
Description: Bootstrapper for a .NET application has failed.
Application: DotnetRuntimeBootstrapper.Demo.Gui.exe
Path: F:\Projects\Softdev\DotnetRuntimeBootstrapper\DotnetRuntimeBootstrapper.Demo.Gui\bin\Debug\net6.0-windows\DotnetRuntimeBootstrapper.Demo.Gui.exe
AppHost: .NET Runtime Bootstrapper v2.3.0
Message: System.Exception: Test failure
at DotnetRuntimeBootstrapper.AppHost.Core.ApplicationShellBase.Run(String[] args)
at DotnetRuntimeBootstrapper.AppHost.Gui.Program.Main(String[] args)