Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 8 Min.

Ein Installer für .NET

Das Setup-Framework Squirrel vereint die Vorteile von ClickOnce mit einem umfangreichen API.
Die Installation? Stimmt, da war was. Auf der einen Seite wollen Anwender Windows-Anwendungen schnell und einfach installieren. Auf der anderen Seite sollte für die Entwickler die Erstellung eines Installers und von Programm-Updates unkompliziert sein. Gegensätzlich? Unvereinbar?Das Erreichen dieser beiden Ziele haben sich die Macher von Squirrel [1] gesetzt, die ihr Tool mit dem Spruch „Squirrel: It’s like ClickOnce but Works“ beschreiben. Squirrel ist eine Bibliothek und eine Sammlung von Tools, um sowohl Installation als auch Updates von Windows-Anwendungen zu verwalten. Unter .NET besteht die Möglichkeit, innerhalb der Anwendung auf die zu installierenden Updates Einfluss zu nehmen. Doch welche Möglichkeiten bietet Squirrel und wie lässt es sich in die eigene .NET-Anwendung integrieren?Hier einige Features ohne Vollständigkeitsgarantie:
  • Überprüfung auf vorhandene Updates und Bereitstellung eines Change-Logs,
  • Eingreifen in die Installations- und Update-Routinen,
  • Delta-Pakete zur Speicherplatzreduzierung,
  • Möglichkeit zur Integration in den Build-Prozess zur automatisierten Erstellung des Setups,
  • keine UAC-Dialoge (User Account Control),
  • keine Neustarts,
  • Aktualisierung, während die Anwendung läuft.
Um die letzten drei genannten Features anbieten zu können, wurden die folgende Maßnahmen ergriffen: Squirrel installiert die Anwendung im lokalen Benutzeraccount unter C:\Users\<USER>\AppData\Local\<APPLICATION>. Somit erscheint bei der Installation kein UAC-Dialog und der Rechner muss nicht neugestartet werden.Die Aktualisierung der Anwendung im laufenden Betrieb wurde so implementiert, dass es für jede Anwendungsver­sion einen eigenen Unterordner gibt. Bei der Installation einer neuen Version wird somit ein neuer Ordner angelegt, und die Verknüpfungen auf dem Desktop, in der Taskleiste und im Startmenü werden auf die neue Version umgebogen. Somit muss nach der Installation des Updates nur die Anwendung neu gestartet werden. Es werden zudem nur die aktuelle sowie die vorherige Version beibehalten, alle vorherigen Ver­sionsordner werden beim Update gelöscht.

Überblick

Sie binden Squirrel am einfachsten über das NuGet-Paket squirrel.windows in das eigene Projekt ein. Beim Schreiben des Artikels war Version 1.6.0 aktuell. In dieser Version ist jedoch ein Bug beim Anlegen der Desktop-Verknüpfung vorhanden, sodass entweder die etwas ältere Version 1.4.4 [2] oder die neuere Version 1.7.7 verwendet werden muss. Der Fix zu dem Problem ist unter [3] dokumentiert. Mit der Installation des NuGet-Pakets werden zudem mehrere benötigte Bibliotheken installiert, wie zum Beispiel DeltaCompressionDotNet oder Mono.Cecil.Für eine vollständige Integration von Squirrel in eine .NET-Anwendung sind folgende Aspekte zu betrachten:
  • Integration: Sie binden Squirrel in die Anwendung ein.
  • Paketierung: Sie führen die Dateien zu einem Setup-Paket zusammen, das verteilt werden kann.
  • Verteilung: Sie stellen die Setup-Datei(en) an einem für den Benutzer erreichbaren Ort bereit.
  • Installation: Sie installieren die Anwendung auf dem Rechner des Benutzers.
  • Aktualisierung: Eine neue Version der Anwendung wird bereitgestellt.
Als Beispiel für die Integration von Squirrel in eine .NET-Anwendung soll eine WPF-Anwendung entstehen, die einerseits die aktuelle Versionsnummer anzeigt und andererseits das Prüfen auf zu installierende Updates und deren eigentliche Installation ermöglicht. Bild 1 zeigt ihre Oberfläche.

Integration

Die zentrale Klasse, über die der Update-Prozess gesteuert wird, ist UpdateManager. Diese Klasse nimmt im Konstruktor den Pfad, in dem die Updates liegen, entgegen. Danach kann beispielsweise über die CheckForUpdate-Methode geprüft werden, ob zu installierende Updates vorhanden sind.
Listing 1: Prüfung auf zu installierende Updates
private async void CheckForUpdate_Click(
  object sender, RoutedEventArgs e) {
 try  { 
  using (var mgr = 
   new UpdateManager(UPDATE_FOLDER)) { 
   UpdateInfo info = await mgr.CheckForUpdate(); 
   if (info.ReleasesToApply.Any()) { 
    string availableUpdates = string.Join("\n", 
     info.ReleasesToApply.OrderBy(x => x.Version)
     .Select(x => x.Version.ToString())); 
    LogText("Update(s) available:\n" + 
     availableUpdates); 
    SetReleaseNotes(info); 
   } 
   else { 
    LogText("No update available"); 
   } 
  } 
 } 
 catch (Exception ex) { 
  LogText("Error during check for updates: " + 
   ex.Message); 
 } 
} 
Diese Methode gibt ein Objekt der Klasse UpdateInfo zurück, das unter anderem die anzuwendenden Updates in der Property ReleasesToApply bereitstellt. Hierüber lassen sich zudem die Release-Notes abfragen. Eine beispielhafte Implementierung einer Routine zum Überprüfen auf vorhandene Updates kann Listing 1 entnommen werden. Das Auslesen der Release-Notes ist in Listing 2 dargestellt.
Listing 2: Ermittlung der Release-Notes
private void SetReleaseNotes(UpdateInfo info) { 
 var releaseNotes = info.FetchReleaseNotes(); 
 StringBuilder sb = new StringBuilder(); 
 foreach (var entry in releaseNotes.Keys
  .OrderByDescending(x => x.Version)) { 
  sb.AppendLine("Version " + entry.Version); 
  sb.AppendLine("~~~~~~~~~~~~~~~~~~~~~~~~~~"); 
  sb.AppendLine(releaseNotes[entry]); 
 } 
 LogText(sb.ToString()); 
} 
Für das Herunterladen und Anwenden von Updates ist ebenfalls der UpdateManager zuständig. Dieser bietet die Möglichkeit, mit nur einem Aufruf der UpdateApp-Methode die Anwendung zu aktualisieren. Um das Herunterladen und Anwenden von Updates zu trennen, können alternativ aber auch die Methoden DownloadReleases und ApplyReleases hintereinander aufgerufen werden (siehe Listing 3).
Listing 3: Update herunterladen und anwenden
private async void DownloadUpdate_Click(
  object sender, RoutedEventArgs e) {
 try 
 { 
  using (var mgr = new UpdateManager(UPDATE_FOLDER)){ 
   UpdateInfo info = await mgr.CheckForUpdate(); 
   // download and install updates with a single 
   // line of code 
   //await mgr.UpdateApp(); 
   // if you want to separate download and 
   // installation, call these methods separately 
   LogText("Start downloading update(s)"); 
   await mgr.DownloadReleases(info.ReleasesToApply); 
   LogText("Applying update(s)"); 
   await mgr.ApplyReleases(info); 
   LogText("Update finished. Please restart the 
     application"); 
  } 
 } 
 catch (Exception ex) { 
  LogText("Error during update: " + ex.Message); 
 } 
} 

Paketierung

Nachdem die Anwendung für eine Aktualisierung mit Squirrel vorbereitet wurde, muss diese nun in ein Setup-Paket überführt werden. Squirrel nutzt NuGet-Pakete, sodass hierfür der NuGet Package Explorer verwendet werden kann. Eine Integration in den Visual-Studio-Buildprozess wird später vorgestellt. Für die Beispielanwendung entsteht ein neues NuGet-Paket und wird mit den gewünschten Metadaten befüllt. Die Binärdateien der Anwendung müssen, unabhängig davon, für welche .NET-Version die Anwendung geschrieben wurde, im Ordner lib\net45 abgelegt werden. Dieser Ordner lässt sich über das Kontextmenü anlegen.
Das fertige NuGet-Paket der Beispielanwendung zeigt Bild 2. Es sind alle Dateien hinzuzufügen, die sowohl zur Anwendung als auch zu Squirrel gehören.Bei der Versionsnummer ist Vorsicht geboten, da diese nur drei Teile haben darf. Das ist technisch bedingt [4].Nachdem das NuGet-Paket erstellt ist, folgt nun die Bereitstellung dieses Pakets. Das erreichen Sie durch den Befehl
Squirrel --releasify dotnetproSquirrel.1.0.0.nupkg 
in der Package Manager Console. Das Kommando Squirrel bezieht sich auf Squirrel.exe, das bei der Installation des Squirrel-NuGet-Pakets in die Anwendung mitgeliefert wird. Der Dateiname ist dem Paket entsprechend anzupassen. Sollte ein Fehler erscheinen, dass das Kommando Squirrel nicht gefunden wird, reicht ein Neustart von Visual Studio.Der Release-Schritt führt zu folgendem Output:
  • Releases-Order: Squirrel legt einen Ordner Releases standardmäßig unterhalb des Solution-Ordners an.
  • Setup.exe: Es entsteht eine Setup-Datei mit der aktuellsten Version der Anwendung.
  • RELEASES-Datei: Die Textdatei enthält eine Auflistung aller Releases, die während des Aktualisierungsvorgangs verwendet werden.
  • dotnetproSquirrel.1.0.0-full.nupkg: Dies ist das erstellte NuGet-Paket mit dem vollständigen Inhalt.
  • dotnetproSquirrel.*.*.*-delta.nupkg: Bei einer neuen Ver­sion wird eine Delta-Datei mit der Differenz zum vorherigen Paket erstellt, um die Update-Größe so gering wie möglich zu halten.

Verteilung

Das durch Squirrel.exe erstellte und gefüllte Releases-Verzeichnis kann nun auf einem Netzlaufwerk oder im Internet bereitgestellt werden. Den Inhalt des Verzeichnisses für die Beispielanwendung mit zwei Updates zeigt Bild 3. Eine Bereitstellung auf eigenem Webspace ist ebenso möglich wie die Bereitstellung in Amazon S3 [5] oder Microsoft Azure.

Installation

Die Erstinstallation erfolgt durch die über den Squirrel --relea­sify-Schritt erstellte Setup.exe. Während des Installationsprozesses laufen folgende Schritte ab:
  • Erstellung des Verzeichnisses %LocalAppData%\dotnet
    proSquirrel
    .
  • Erstellung des Unterverzeichnisses dotnetproSquirrel-1.0.0 für die Version 1.0.0 und Ablage aller Dateien des NuGet-Pakets in diesem Ordner.
  • Ausführen der Datei dotnetproSquirrel-1.0.0\dotnetpro
    Squirrel.exe
    .
Da der Installationsordner unter dem persönlichen Benutzerkonto des angemeldeten Benutzers erstellt wird, erscheint bei der Installation kein UAC-Dialog. Im Installationsverzeichnis wird für jede Version ein eigener Ordner erstellt. Wie oben schon beschrieben, werden jedoch lediglich die aktuelle und die direkte Vorgängerversion beibehalten. Die Verknüpfungen passt Squirrel an. Durch diese Vorgehensweise ist eine Aktualisierung bei laufender Anwendung möglich. Im Instal­la­tionsverzeichnis werden zudem die im RELEASES-Ordner bereitgestellten und heruntergeladenen Voll- und Delta-Packages ebenso wie die RELEASES-Datei abgelegt. Diese benötigt das Setup-Programm, um bestimmen zu können, welche Updates zu laden sind.Im Rahmen der Version 1.5.0 wurden sogenannte StubExecutables eingeführt. Das bisherige Verfahren, Updates bei laufender Anwendung einzuspielen, hat einige Nachteile. Datei-Assoziationen gehen bei einem Update verloren, Icons werden unter Umständen nicht mehr richtig angezeigt oder es kommt zu Konflikten mit der Firewall. Um diese Probleme zu vermeiden, wird für jede ausführbare Datei im Programmverzeichnis eine gleichnamige Stub-Datei im übergeordneten Verzeichnis abgelegt – mit dem gleichen Icon und den gleichen Metadaten. Es werden prinzipiell die Stub-Dateien ausgeführt und zum Beispiel an die Taskleiste angeheftet. Diese Stub-Dateien führen dann die richtigen Dateien aus.

Aktualisierung

Um eine neue Version der Anwendung bereitzustellen, muss in einem ersten Schritt die AssemblyVersion hochgesetzt werden, zum Beispiel auf 1.0.1. Danach sind die Schritte aus dem Abschnitt Paketierung durchzuführen. Dies hat folgende Auswirkungen:
  • Aktualisierung der Setup.exe, die nun die jetzt aktuelle Version enthält.
  • Hinzufügen des neuen NuGet-Pakets mit dem kompletten Inhalt.
  • Erzeugung eines Delta-Pakets mit den Unterschieden zur Vorversion.
  • Erweiterung der RELEASES-Datei um die neue Version.
Die geänderten und neu hinzugekommenen Dateien sind nun im Bereitstellungsordner zu hinterlegen. Ist dies geschehen, wird die Aktualisierung in der Anwendung ermittelt und kann installiert werden.

Visual-Studio-Build-Integration

Das manuelle Erstellen der NuGet-Pakete und der Aufruf von Squirrel über die Kommandozeile sind lästige Schritte, die Sie nicht notwendigerweise manuell ausführen müssen. Es besteht auch die Möglichkeit, diese beiden Schritte in den Build-Prozess von Visual Studio zu integrieren. Es können wahlweise beide Schritte oder auch nur das Erstellen des NuGet-Pakets integriert werden.
Listing 4: Visual-Studio-Build-Integration
<ItemGroup> 
 <NuGetCommandLine Include="..\packages\
   NuGet.CommandLine.*\tools\nuget.exe"> 
  <InProject>False</InProject> 
 </NuGetCommandLine> 
 <Squirrel Include="..\packages\Squirrel.Windows.*
   \tools\squirrel.exe"> 
  <InProject>False</InProject> 
 </Squirrel> 
 </ItemGroup> 
 <Target Name="AfterBuild" Condition=" '
  $(Configuration)' == 'Release'"> 
  <GetAssemblyIdentity AssemblyFiles="$(TargetPath)"> 
   <Output TaskParameter="Assemblies" 
     ItemName="myAssemblyInfo" /> 
  </GetAssemblyIdentity> 
  <Exec Command=""@(NuGetCommandLine->'%(FullPath)')" 
    pack dotnetproSquirrel.nuspec -Version 
     %(myAssemblyInfo.Version) -Properties 
     Configuration=Release -OutputDirectory 
     $(OutDir)..\..\..\Install -BasePath $(OutDir)" /> 
  <Exec Command=""@(Squirrel->'%(FullPath)')" 
   --releasify $(OutDir)..\..\..\Install
   \dotnetproSquirrel.$([System.Version]::Parse(
   %(myAssemblyInfo.Version)).ToString(3)).nupkg 
   --releaseDir=$(OutDir)..\..\..\Releases" />
 </Target> 
Legen Sie dazu zunächst ein Build-Target wie in Listing 4 dargestellt an. Bearbeiten Sie hierzu die .csproj-Datei am besten in einem Texteditor. Das Beispiel zeigt einen Build-Schritt, der im Release-Build ausgeführt wird und in dem zwei Kommandos ausgeführt werden. Das erste Kommando erzeugt anhand einer .nuspec-Datei ein NuGet-Paket. Eine solche .nuspec-Datei enthält die für das Erstellen des NuGet-Pakets notwendigen Metainformationen, wie zum Beispiel Autor, Titel und Beschreibung, ebenso aber auch die hinzuzufügenden Dateien (Beispiel siehe Listing 5) . Damit Visual Studio den NuGet-Befehl erkennt, muss das NuGet.CommandLine-Package installiert werden. Erscheint beim Build eine Fehlermeldung zu Fehlercode 9009, reicht ein Neustart von Visual Studio oder das Öffnen der Package Manager Console.
Listing 5: Definition des NuGet-Paketinhaltes für die Build-Integration
<?xml version="1.0" encoding="utf-8"?> 
 <package xmlns="http://schemas.microsoft.com
   /packaging/2010/07/nuspec.xsd"> 
  <metadata> 
   <id>dotnetproSquirrel</id> 
   <!-- version will be replaced by MSBuild --> 
   <version>0.0.0.0</version> 
   <title>title</title> 
   <authors>authors</authors> 
   <description>description</description> 
   <requireLicenseAcceptance>false
    </requireLicenseAcceptance> 
   <copyright>Copyright 2017</copyright> 
   <dependencies /> 
  </metadata> 
  <files> 
   <file src="*.*" target="lib\net45\" 
     exclude="*.pdb;*.nupkg;*.vshost.*;*.log"/> 
   <file src="**\*.*" target="lib\net45\" 
     exclude="*.pdb;*.nupkg;*.vshost.*;*.log"/> 
  </files> 
 </package> 
Das zweite Kommando führt den Releasify-Schritt von Squirrel aus und aktualisiert den Releases-Ordner mit einer neuen Setup.exe und den Komplett- und Delta-Paketen.

Fazit

Squirrel hat viel Ähnlichkeit zu ClickOnce, bietet jedoch insbesondere für den Entwickler einige interessante Features, um in den Installationsprozess einzugreifen. Das macht es sehr attraktiv, die eigene Anwendung damit zu verteilen.
Projektdateien

Fussnoten

  1. GitHub-Repository,
  2. Falsches Verknüpfungstarget,
  3. Korrektur falsches Verknüpfungstarget,
  4. Versionsnummern-Format,
  5. Amazon S3,
Installation & Deployment

Neueste Beiträge

Von Text zu Struktur: JSON-Ausgaben aus LLMs zuverlässig nutzen - KI für KMU, Teil 4
Mit JSON-Schema lassen sich LLM-Ausgaben direkt deserialisieren, typsicher verarbeiten und in bestehende Workflows integrieren.
7 Minuten
27. Nov 2025
Viele Developer, ein Gedanke: Lass uns zusammen die Zukunft bauen
Know-how von früh bis spät, direkter Kontakt zu den Experten, Austausch in der Community und leckeres Essen: das war die große .NET-Konferenz in Köln.
10 Minuten
4. Dez 2025
Layouts, Grids und responsive Gestaltung - Moderne UI-Gestaltung mit der Uno Platform, Teil 1
Mit Layout-Containern in der Uno Platform lassen sich strukturierte, performante und responsive Oberflächen erstellen.
10 Minuten

Das könnte Dich auch interessieren

MSI plus APPX gleich MSIX - Das Ende einer Ära
Ein Nachfolger für den austauschreifen Windows Installer steht bereits in den Startlöchern.
26 Minuten
14. Jan 2019
Unter der Haube von MSIX - MSIX-Pakete verwalten
Verwalten von Anwendungspaketen in einem individuell anpassbaren Zielsystem.
22 Minuten
15. Jun 2020
Quo vadis, MSIX? - Neuerungen bei MSIX
Das Paketformat für Windows-Apps MSIX ist seit mehr als einem Jahr auf dem Markt – Zeit für ein Resümee: Wie hat sich MSIX weiterentwickelt, und wo geht die Reise hin?
23 Minuten
15. Jun 2020
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige