• Stars
    star
    238
  • Rank 169,306 (Top 4 %)
  • Language
    C#
  • License
    MIT License
  • Created over 3 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Bootstrapped framework-dependent deployment for .NET applications

.NET Runtime Bootstrapper

Made in Ukraine Build Version Downloads Discord Donate Fuck Russia

🟑 Project status: maintenance mode[?]

Icon

.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 .NET runtime and other missing components required by your application.

Terms of use[?]

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:

  • You condemn Russia and its military aggression against Ukraine
  • You recognize that Russia is an occupant that unlawfully invaded a sovereign state
  • You support Ukraine's territorial integrity, including its claims over temporarily occupied territories of Crimea and Donbas
  • You reject false narratives perpetuated by Russian state propaganda

To learn more about the war and how you can help, click here. Glory to Ukraine! πŸ‡ΊπŸ‡¦

Install

  • πŸ“¦ NuGet: dotnet add package DotnetRuntimeBootstrapper

Why?

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.

  • Framework-dependent deployment:
    • Requires the user to have the correct .NET runtime installed on their machine. Not only will many users inevitably miss or ignore this requirement, the task of installing the correct .NET runtime can be very challenging for non-technical individuals. Depending on their machine and the specifics of your application, they will need to carefully examine the download page to find the installer for the right version, framework (i.e. base, desktop, or aspnet), CPU architecture, and operating system.
    • Comes with an application host that is not platform-agnostic. Even though the application itself (the 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).
  • Self-contained deployment:
    • While eliminating the need for installing the correct runtime, this method comes at a significant file size overhead. A very basic WinForms application, for example, starts at around 100 MB in size. This can be very cumbersome when doing auto-updates, but also seems quite wasteful if you consider that the user may end up with multiple .NET applications each bringing their own runtime.
    • Targets a specific platform, which means that you have to provide separate binaries for each operating system and processor architecture that you intend to support. Additionally, it can also create confusion among non-technical users, who may have a hard time figuring out which of the release binaries they need to download.
    • Snapshots a specific version of the runtime when it's produced. This means that your application won't be able to benefit from newer releases of the runtime β€” which may potentially contain performance or security improvements β€” unless you deploy a new version of the application.
    • Is, in fact, not completely self-contained. Depending on the user's machine, they might still need to install the Visual C++ runtime or certain Windows updates, neither of which are packaged with the application. Although this is only required for older operating systems, it may still affect a significant portion of your user base.

.NET Runtime Bootstrapper seeks to solve all the above problems by providing an alternative, third deployment option β€” bootstrapped deployment.

  • Bootstrapped deployment:
    • Takes care of installing the target .NET runtime automatically. All the user has to do is accept the prompt and the bootstrapper will download and install the correct version of the runtime on its own.
    • Can also automatically install the Visual C++ runtime and missing Windows updates, when running on older operating systems. This means that users who are still using Windows 7 will have just as seamless experience as those running on Windows 10.
    • Does not impose any file size overhead as it does not package additional files. Missing prerequisites are downloaded on-demand.
    • Allows your application to benefit from newer releases of the runtime that the user might install in the future. When deploying your application, you are only tying it to a minimum .NET version within the same major.
    • Is truly portable because the provided application host is a platform-agnostic .NET Framework 3.5 executable that works out-of-the-box on all environments starting with Windows 7. This means that you only need to share a single distribution of your application, without worrying about different CPU architectures or other details.

Features

  • Executes the target assembly in-process using a custom runtime host
  • Provides GUI-based or CLI-based host, depending on the application
  • Detects and installs missing dependencies:
    • Required version of .NET runtime
    • Required Visual C++ binaries
    • Required Windows updates
  • Works out-of-the-box on Windows 7 and higher
  • Supports all platforms in a single executable
  • Integrates seamlessly inside the build process
  • Retains native resources, such as version info, manifest, and icons
  • Imposes no overhead in file size or performance

Video

Bootstrapper.mp4

Usage

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.

Publishing

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.

Application host

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

Application resources

When the bootstrapper is created, the build task copies all native resources from the target assembly into the application host. This includes:

  • Application manifest (resource type: 24). Configured by the <ApplicationManifest> project property.
  • Application icon (resource types: 3 and 14). Configured by the <ApplicationIcon> project property.
  • Version info (resource type: 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.

Customizing behavior

Generate bootstrapper on build

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>net6.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 to false (default).

Override bootstrapper variant

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>net6.0-windows</TargetFramework>
    <!-- ... -->

    <!-- Specify bootstrapper variant explicitly (GUI or CLI) -->
    <BootstrapperVariant>GUI</BootstrapperVariant>
  </PropertyGroup>

  <!-- ... -->

</Project>

Override runtime version

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>net6.0-windows</TargetFramework>
    <!-- ... -->

    <!-- Specify target runtime version explicitly -->
    <RuntimeFrameworkVersion>6.0.9</RuntimeFrameworkVersion>
  </PropertyGroup>

  <!-- ... -->

</Project>

Disable confirmation prompt

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>net6.0-windows</TargetFramework>
    <!-- ... -->

    <!-- Skip the confirmation prompt and install prerequisites straight away -->
    <BootstrapperPromptRequired>false</BootstrapperPromptRequired>
  </PropertyGroup>

  <!-- ... -->

</Project>

Troubleshooting

Build task logs

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.

Application host logs

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)

More Repositories

1

DiscordChatExporter

Exports Discord chat logs to a file
C#
5,677
star
2

YoutubeDownloader

Downloads videos and playlists from YouTube
C#
4,837
star
3

CliWrap

Library for running command-line processes
C#
3,705
star
4

YoutubeExplode

Abstraction layer over YouTube's internal API
C#
2,473
star
5

LightBulb

Reduces eye strain by adjusting gamma based on the current time
C#
1,762
star
6

CliFx

Class-first framework for building command-line interfaces
C#
1,324
star
7

Onova

Unintrusive auto-update framework
C#
400
star
8

GitHubActionsTestLogger

.NET test logger that reports to GitHub Actions
C#
221
star
9

MiniRazor

Portable Razor compiler & code generator
C#
214
star
10

Gress

Progress reporting toolbox
C#
137
star
11

LtGt

Lightweight HTML processor
C#
117
star
12

OsuHelper

Beatmap suggester for osu!
C#
95
star
13

YoutubeExplode.Converter

Muxes and converts videos from YoutubeExplode
C#
87
star
14

SpellingUkraine

Learn the correct way to spell Ukrainian names in English
TypeScript
77
star
15

interview-questions

Collection of popular interview questions and their answers
66
star
16

Ressy

Resource editor for PE files
C#
43
star
17

Contextual

Implicit parameters via contexts
C#
40
star
18

JetBrainsDotnetDay2020

Presentation and code for my talk at JetBrains .NET Day Online 2020
F#
35
star
19

JsonExtensions

Extensions for System.Text.Json
C#
29
star
20

Extensions

My .NET extensions
C#
29
star
21

YoutubeMusicDownloader

Downloads Youtube videos and playlists as mp3 files
C#
26
star
22

Hallstatt

Low-ceremony testing framework optimized for modern C#
C#
25
star
23

Deorcify

Prevent your software from being used by terrorists
C#
22
star
24

WpfExtensions

My WPF extensions, converters and behaviors
C#
19
star
25

route-descriptor

Single source of truth for routing
TypeScript
17
star
26

Cogwheel

Library for managing application settings
C#
16
star
27

QuickJson

Simple JSON parser in a source-only package
C#
14
star
28

LockFile

Simplest lock file implementation
C#
13
star
29

Failsafe

Retry utility
C#
13
star
30

Tyrrrz.me

My personal website
TypeScript
13
star
31

.github

Assets shared between my repositories
12
star
32

FuncTestingInAspNetCoreExample

Example of doing functional testing with an ASP.NET Core application
C#
12
star
33

PolyShim

Polyfills for projects targeting older versions of .NET
C#
11
star
34

MyFlickList

Social cataloging app for movies and TV-shows
TypeScript
9
star
35

RaidTrend

Documenting air raid alerts across Ukraine
TypeScript
8
star
36

DotNetFest2019

My presentation and live demo project used during my talk at .NET Fest 2019
C#
8
star
37

WPSteamMarketExcerpt

Embeds Steam Market listings into WordPress pages
PHP
6
star
38

hip-cloud-test

will delete later
TypeScript
6
star
39

netfwdays-hipster-cloud

.NET fwdays'21 workshop
TypeScript
4
star
40

DiscordFonts

4
star
41

PrintForegroundWindow

Prints info about current foreground window
C#
4
star
42

Hashsum

Culture-invariant fluent checksum builder
C#
3
star
43

Tyrrrz

Profile readme
2
star
44

action-http-request

GitHub Action that sends an HTTP request
JavaScript
2
star
45

gatsby-plugin-clicky

Clicky web analytics integration for Gatsby
JavaScript
2
star
46

twitter-auth-cli

Quickly generate access token and secret for Twitter API
TypeScript
2
star
47

Scheddulit

Post scheduler and batcher for Reddit
TypeScript
1
star
48

OnovaTestRepo

Test repository used for Onova integration tests
1
star
49

AspNetCore.Mvc.Lightbox

Tag helper used to initialize Lightbox
C#
1
star
50

action-get-tag

GitHub Action that extracts current git tag
JavaScript
1
star
51

AspNetCore.Mvc.Clicky

Tag helper used to render Clicky activity tracker
C#
1
star
52

AspNetCore.Mvc.Disqus

Tag helper used to render Disqus threads
C#
1
star
53

BMAC-API-Cache

Caching layer for BuyMeACoffee API
TypeScript
1
star