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
&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;private&lt;/span&gt; &lt;span class="hljs-keyword"&gt;async&lt;/span&gt; &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;CheckForUpdate_Click&lt;/span&gt;(&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;&lt;span class="hljs-params"&gt;  &lt;span class="hljs-keyword"&gt;object&lt;/span&gt; sender, RoutedEventArgs e&lt;/span&gt;) &lt;/span&gt;{&lt;br/&gt; &lt;span class="hljs-keyword"&gt;try&lt;/span&gt;  { &lt;br/&gt;  &lt;span class="hljs-keyword"&gt;using&lt;/span&gt; (&lt;span class="hljs-keyword"&gt;var&lt;/span&gt; mgr = &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; UpdateManager(UPDATE_FOLDER)) { &lt;br/&gt;   UpdateInfo info = &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; mgr.CheckForUpdate(); &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (info.ReleasesToApply.Any()) { &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;string&lt;/span&gt; availableUpdates = &lt;span class="hljs-keyword"&gt;string&lt;/span&gt;.Join(&lt;span class="hljs-string"&gt;"\n"&lt;/span&gt;, &lt;br/&gt;     info.ReleasesToApply.OrderBy(x =&amp;gt; x.Version)&lt;br/&gt;     .Select(x =&amp;gt; x.Version.ToString())); &lt;br/&gt;    LogText(&lt;span class="hljs-string"&gt;"Update(s) available:\n"&lt;/span&gt; + &lt;br/&gt;     availableUpdates); &lt;br/&gt;    SetReleaseNotes(info); &lt;br/&gt;   } &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;else&lt;/span&gt; { &lt;br/&gt;    LogText(&lt;span class="hljs-string"&gt;"No update available"&lt;/span&gt;); &lt;br/&gt;   } &lt;br/&gt;  } &lt;br/&gt; } &lt;br/&gt; &lt;span class="hljs-keyword"&gt;catch&lt;/span&gt; (Exception ex) { &lt;br/&gt;  LogText(&lt;span class="hljs-string"&gt;"Error during check for updates: "&lt;/span&gt; + &lt;br/&gt;   ex.Message); &lt;br/&gt; } &lt;br/&gt;} 
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
&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;private&lt;/span&gt; &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;SetReleaseNotes&lt;/span&gt;(&lt;span class="hljs-params"&gt;UpdateInfo info&lt;/span&gt;) &lt;/span&gt;{ &lt;br/&gt; &lt;span class="hljs-keyword"&gt;var&lt;/span&gt; releaseNotes = info.FetchReleaseNotes(); &lt;br/&gt; StringBuilder sb = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; StringBuilder(); &lt;br/&gt; &lt;span class="hljs-keyword"&gt;foreach&lt;/span&gt; (&lt;span class="hljs-keyword"&gt;var&lt;/span&gt; entry &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; releaseNotes.Keys&lt;br/&gt;  .OrderByDescending(x =&amp;gt; x.Version)) { &lt;br/&gt;  sb.AppendLine(&lt;span class="hljs-string"&gt;"Version "&lt;/span&gt; + entry.Version); &lt;br/&gt;  sb.AppendLine(&lt;span class="hljs-string"&gt;"~~~~~~~~~~~~~~~~~~~~~~~~~~"&lt;/span&gt;); &lt;br/&gt;  sb.AppendLine(releaseNotes[entry]); &lt;br/&gt; } &lt;br/&gt; LogText(sb.ToString()); &lt;br/&gt;} 
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(&lt;br/&gt;  object sender, RoutedEventArgs e) {&lt;br/&gt; &lt;span class="hljs-keyword"&gt;try&lt;/span&gt; &lt;br/&gt; { &lt;br/&gt;  using (var mgr = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; UpdateManager(UPDATE_FOLDER)){ &lt;br/&gt;   UpdateInfo info = &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; mgr.CheckForUpdate(); &lt;br/&gt;   &lt;span class="hljs-regexp"&gt;//&lt;/span&gt; download &lt;span class="hljs-keyword"&gt;and&lt;/span&gt; install updates with a single &lt;br/&gt;   &lt;span class="hljs-regexp"&gt;//&lt;/span&gt; line &lt;span class="hljs-keyword"&gt;of&lt;/span&gt; code &lt;br/&gt;   &lt;span class="hljs-regexp"&gt;//&lt;/span&gt;&lt;span class="hljs-keyword"&gt;await&lt;/span&gt; mgr.UpdateApp(); &lt;br/&gt;   &lt;span class="hljs-regexp"&gt;//&lt;/span&gt; &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; you want to separate download &lt;span class="hljs-keyword"&gt;and&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-regexp"&gt;//&lt;/span&gt; installation, call these methods separately &lt;br/&gt;   LogText(&lt;span class="hljs-string"&gt;"Start downloading update(s)"&lt;/span&gt;); &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; mgr.DownloadReleases(info.ReleasesToApply); &lt;br/&gt;   LogText(&lt;span class="hljs-string"&gt;"Applying update(s)"&lt;/span&gt;); &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; mgr.ApplyReleases(info); &lt;br/&gt;   LogText(&lt;span class="hljs-string"&gt;"Update finished. Please restart the &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-string"&gt;     application"&lt;/span&gt;); &lt;br/&gt;  } &lt;br/&gt; } &lt;br/&gt; &lt;span class="hljs-keyword"&gt;catch&lt;/span&gt; (Exception ex) { &lt;br/&gt;  LogText(&lt;span class="hljs-string"&gt;"Error during update: "&lt;/span&gt; + ex.Message); &lt;br/&gt; } &lt;br/&gt;} 

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
<span class="hljs-selector-tag">Squirrel</span> <span class="hljs-selector-tag">--releasify</span> <span class="hljs-selector-tag">dotnetproSquirrel</span><span class="hljs-selector-class">.1</span><span class="hljs-selector-class">.0</span><span class="hljs-selector-class">.0</span><span class="hljs-selector-class">.nupkg</span> 
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
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;ItemGroup&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt; &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;NuGetCommandLine&lt;/span&gt; &lt;span class="hljs-attr"&gt;Include&lt;/span&gt;=&lt;span class="hljs-string"&gt;"..\packages\&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-tag"&gt;&lt;span class="hljs-string"&gt;   NuGet.CommandLine.*\tools\nuget.exe"&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;InProject&lt;/span&gt;&amp;gt;&lt;/span&gt;False&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;InProject&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt; &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;NuGetCommandLine&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt; &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;Squirrel&lt;/span&gt; &lt;span class="hljs-attr"&gt;Include&lt;/span&gt;=&lt;span class="hljs-string"&gt;"..\packages\Squirrel.Windows.*&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-tag"&gt;&lt;span class="hljs-string"&gt;   \tools\squirrel.exe"&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;InProject&lt;/span&gt;&amp;gt;&lt;/span&gt;False&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;InProject&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt; &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;Squirrel&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt; &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;ItemGroup&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt; &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;Target&lt;/span&gt; &lt;span class="hljs-attr"&gt;Name&lt;/span&gt;=&lt;span class="hljs-string"&gt;"AfterBuild"&lt;/span&gt; &lt;span class="hljs-attr"&gt;Condition&lt;/span&gt;=&lt;span class="hljs-string"&gt;" '&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-tag"&gt;&lt;span class="hljs-string"&gt;  $(Configuration)' == 'Release'"&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;GetAssemblyIdentity&lt;/span&gt; &lt;span class="hljs-attr"&gt;AssemblyFiles&lt;/span&gt;=&lt;span class="hljs-string"&gt;"$(TargetPath)"&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;Output&lt;/span&gt; &lt;span class="hljs-attr"&gt;TaskParameter&lt;/span&gt;=&lt;span class="hljs-string"&gt;"Assemblies"&lt;/span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-tag"&gt;     &lt;span class="hljs-attr"&gt;ItemName&lt;/span&gt;=&lt;span class="hljs-string"&gt;"myAssemblyInfo"&lt;/span&gt; /&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;GetAssemblyIdentity&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;Exec&lt;/span&gt; &lt;span class="hljs-attr"&gt;Command&lt;/span&gt;=&lt;span class="hljs-string"&gt;""&lt;/span&gt;@(&lt;span class="hljs-attr"&gt;NuGetCommandLine-&lt;/span&gt;&amp;gt;&lt;/span&gt;'%(FullPath)')" &lt;br/&gt;    pack dotnetproSquirrel.nuspec -Version &lt;br/&gt;     %(myAssemblyInfo.Version) -Properties &lt;br/&gt;     Configuration=Release -OutputDirectory &lt;br/&gt;     $(OutDir)..\..\..\Install -BasePath $(OutDir)" /&amp;gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;Exec&lt;/span&gt; &lt;span class="hljs-attr"&gt;Command&lt;/span&gt;=&lt;span class="hljs-string"&gt;""&lt;/span&gt;@(&lt;span class="hljs-attr"&gt;Squirrel-&lt;/span&gt;&amp;gt;&lt;/span&gt;'%(FullPath)')" &lt;br/&gt;   --releasify $(OutDir)..\..\..\Install&lt;br/&gt;   \dotnetproSquirrel.$([System.Version]::Parse(&lt;br/&gt;   %(myAssemblyInfo.Version)).ToString(3)).nupkg &lt;br/&gt;   --releaseDir=$(OutDir)..\..\..\Releases" /&amp;gt;&lt;br/&gt; &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;Target&lt;/span&gt;&amp;gt;&lt;/span&gt; 
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
&lt;span class="php"&gt;&lt;span class="hljs-meta"&gt;&amp;lt;?&lt;/span&gt;xml version=&lt;span class="hljs-string"&gt;"1.0"&lt;/span&gt; encoding=&lt;span class="hljs-string"&gt;"utf-8"&lt;/span&gt;&lt;span class="hljs-meta"&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt; &lt;br/&gt; &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;package&lt;/span&gt; &lt;span class="hljs-attr"&gt;xmlns&lt;/span&gt;=&lt;span class="hljs-string"&gt;"http://schemas.microsoft.com&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-tag"&gt;&lt;span class="hljs-string"&gt;   /packaging/2010/07/nuspec.xsd"&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;metadata&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;id&lt;/span&gt;&amp;gt;&lt;/span&gt;dotnetproSquirrel&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;id&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-comment"&gt;&amp;lt;!-- version will be replaced by MSBuild --&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt;0.0.0.0&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;title&lt;/span&gt;&amp;gt;&lt;/span&gt;title&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;title&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;authors&lt;/span&gt;&amp;gt;&lt;/span&gt;authors&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;authors&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;description&lt;/span&gt;&amp;gt;&lt;/span&gt;description&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;description&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;requireLicenseAcceptance&lt;/span&gt;&amp;gt;&lt;/span&gt;false&lt;br/&gt;    &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;requireLicenseAcceptance&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;copyright&lt;/span&gt;&amp;gt;&lt;/span&gt;Copyright 2017&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;copyright&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;dependencies&lt;/span&gt; /&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;metadata&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;files&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;file&lt;/span&gt; &lt;span class="hljs-attr"&gt;src&lt;/span&gt;=&lt;span class="hljs-string"&gt;"*.*"&lt;/span&gt; &lt;span class="hljs-attr"&gt;target&lt;/span&gt;=&lt;span class="hljs-string"&gt;"lib\net45\"&lt;/span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-tag"&gt;     &lt;span class="hljs-attr"&gt;exclude&lt;/span&gt;=&lt;span class="hljs-string"&gt;"*.pdb;*.nupkg;*.vshost.*;*.log"&lt;/span&gt;/&amp;gt;&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;file&lt;/span&gt; &lt;span class="hljs-attr"&gt;src&lt;/span&gt;=&lt;span class="hljs-string"&gt;"**\*.*"&lt;/span&gt; &lt;span class="hljs-attr"&gt;target&lt;/span&gt;=&lt;span class="hljs-string"&gt;"lib\net45\"&lt;/span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-tag"&gt;     &lt;span class="hljs-attr"&gt;exclude&lt;/span&gt;=&lt;span class="hljs-string"&gt;"*.pdb;*.nupkg;*.vshost.*;*.log"&lt;/span&gt;/&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;files&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt; &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;package&lt;/span&gt;&amp;gt;&lt;/span&gt; 
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, http://www.dotnetpro.de/SL1710Squirrel1
  2. Falsches Verknüpfungstarget, http://www.dotnetpro.de/SL1710Squirrel2
  3. Korrektur falsches Verknüpfungstarget, http://www.dotnetpro.de/SL1710Squirrel3
  4. Versionsnummern-Format, http://www.dotnetpro.de/SL1710Squirrel4
  5. Amazon S3, http://www.dotnetpro.de/SL1710Squirrel5

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Installation & Deployment

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Evolutionäres Prototyping von Business-Apps - Low Code/No Code und KI mit Power Apps
Microsoft baut Power Apps zunehmend mit Features aus, um die Low-Code-/No-Code-Welt mit der KI und der professionellen Programmierung zu verbinden.
19 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige