Thomas Reinwart
Einleitung
Früher oder später steht jeder vor der Frage, in welcher Form liefert man seine Software aus und wie informiert man seine Kunden über Updates. Für jeden Projekttyp kann eine andere Deployment Strategie Sinn machen bzw. ist durch den verwendeten Projekttyp ausschließlich möglich.
Da sich eine Windows Applikation in der Realität nicht professionell als single exe oder zip ausliefern lässt, bleibt nur ein Setup Produkt oder die Auslieferung mittels Microsoft ClickOnce über. Eine Information über eine Aktualisierung des Produkts auf einer Webseite alleine ist nicht mehr zeitgemäß, eine aktive Prüfung durch die Applikation muss her.
Feature | ClickOnce | MSI Windows Installer |
Automatic update | Yes | Yes |
Post-installation rollback | Yes | No |
Update from Web | Yes | No |
Does not affect shared components or other applications | Yes | No |
Security permissions granted | Grants only permissions necessary for the application (more safe) | Grants Full Trust by default (less safe) |
Security permissions required | Internet or Intranet Zone (Full Trust for CD-ROM installation) | Administrator |
Application and deployment manifest signing | Yes | No |
Installation-time user interface | Single prompt | Multipart Wizard |
Installation of assemblies on demand | Yes | No |
Installation of shared files | No | Yes |
Installation of drivers | No | Yes (with custom actions) |
Installation to Global Assembly Cache | No | Yes |
Installation for multiple users | No | Yes |
Add application to Start menu | Yes | Yes |
Add application to Startup group | No | Yes |
Add application to Favorites menu | No | Yes |
Register file types | Yes | Yes |
Install time registry access | Limited | Yes |
Binary file patching | No | Yes |
Application installation location | ClickOnce application cache | Program Files folder |
Microsoft schlägt dazu in einem Strategie Dokument zur Auslieferung folgendes vor:
Microsoft ClickOnce
Mit dieser Technologie lassen sich auf einfache Weise Windows Anwendungen verteilen. Die Abhängigkeiten, wie etwa zu einer .net Framework Version, werden automatisch im Setup Wizzard erkannt. Möchte man eine neue Version seiner Anwendung erstellen, zählt ClickOnce die Version automatisch bei jedem Publish hinauf, man ergänzt gegebenenfalls nur seine neue Assembly und zählt die eigene Assembly Version hinauf. Aber über den Update Mechanismus selber muss man sich keine weiteren Gedanken machen. Das erstellte Setup kann auf verschiedenen Ablagen deponiert werden: UNC File Verzeichnis, ftp, http. Für die notwendige Security sorgt CAS (Code Access Security), dabei wird verhindert, dass Systemfunktionen nicht von einem ClickOnce-Programm aus dem Internet aufgerufen werden können. Nach der erstmaligen Installation wird am Client die Information der Installationsquelle gespeichert. Beim Starten der ausgelieferten Anwendung wird gegen die Installationsquelle überprüft, ob eine neuen Version vorliegt. Sollte die Quelle offline sein, lässt sich die Anwendung trotzdem starten. Beachten sollte man, dass beim Verlegen der Installationsquelle am Server die Clients keine Updates mehr erhalten. Das bemerkt man lange nicht, wundert sich aber dann, warum die Anwender keine Updates mehr erhalten.
Was zu den Projekttypen zu ergänzen ist: ClickOnce funktioniert bei Form und WPF Applikationen, hingegen bei Windows Services oder Web Applikationen nicht. Die installierten Pakete werden im User spezifischen Ablageverzeichnis gespeichert, pro User. Jedes ClickOnce-Programm ist vom anderen separiert. Teilen sich mehrere User einen Rechner, ist eine mehrfache Installation für jeden der User notwendig. Außerdem werden alte Versionen default nicht gelöscht, mit jedem Update kommt eine neue am Filesystem hinzu. ClickOnce hat den Vorteil, dass hier keine Admin Rechte zur Installation notwendig sind. Eine ClickOnce Installation kann eine andere installierte ClickOnce nicht beeinflussen oder zerstören, alles ist separat und es gibt keine gemeinsamen Verzeichnisse zwischen ClickOnce Anwendungen.
Mit Windows 8 kam noch eine weitere Hürde bei der ClickOnce Installation dazu. Wurde die Installations http Url nicht mit einem Zertifikat ausgestattet, weist Windows auf eine unsichere Quelle hin, sofern die Applikation außerhalb der eigenen Domain ausgeliefert wurde. Das lässt sich zwar im Installationsdialog von Windows umgehen, jedoch ist dies bei jedem der Updates notwendig. Das schafft wenig Vertrauen in das eigene Produkt. Die Lösung ist hier ein Zertifikat, dass die Option „Code Signing“ enthält, welches man aber z.B. bei GeoTrust käuflich erwerben muss.
Visual Studio Installer
Ab Visual Studio 2013 liefert Microsoft kein Setup Project Template mehr aus. Stattdessen wird ein Installshield light angeboten. Über das Menü Extension and Updates in Visual Studio kann das Visual Studio Installer Project aber nachinstalliert werden. Anschließend gibt es eine Variante mit Wizzard bei der Zusammenstellung des eigenen Setups und eine ohne Wizzard. Als Ergebnis erhält man ein msi File und ein Setup.exe. Für den Autoupdater benötigen wir nur das msi File.
Autoupdater Produkte
Gleich Vorweg: es gibt keinen zwingenden Zusammenhang zwischen Microsoft Visual Studio Installer und einem der Autoupdater Produkte. Sie können auch ein anderes Setup Projekt wählen. Der Updater prüft die Möglichkeit eines Updates und startet dann bloß das msi File. Das Setup selber muss seine eigene Installation handhaben und mit einem Update der Files zurechtkommen.
Inzwischen gibt es eine Palette von Libraries, nuget Packages und individuelle Lösungen, die sich dem Thema annehmen. Über die Seite nugetmusthaves [1] kann man sich einen Überblick über die beliebtesten Produkte schaffen. Das nuget package von Netsparkle liegt auf GitHub.
Installation NetSparkle.new
Die Installation von NetSparkle.New in Visual Studio erfolgt über nuget [2] :
nuget Package: Install-Package NetSparkle.New
Implementierung des Updaters in der Application
using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using NetSparkle; namespace AutoUpdateSolution { public partial class FormAutoUpdate : Form { private Sparkle _sparkle; public FormAutoUpdate() { InitializeComponent(); _sparkle = new Sparkle( "http://obelix/NetSparkle/autoupdatesolution/appcast.xml", SystemIcons.Application, SecurityMode.Strict) // choose SecurityMode { TrustEverySSLConnection = true, PrintDiagnosticToConsole = true, UseNotificationToast = true, }; _sparkle.UpdateDetected += new UpdateDetected(_sparkle_updateDetected); _sparkle.PrintDiagnosticToConsole = true; // Output console info _sparkle.StartLoop(true); //if (_sparkle.IsUpdateLoopRunning) // MessageBox.Show("Loop is running"); //else // MessageBox.Show("Loop is not running"); } void _sparkle_updateDetected (object sender, UpdateDetectedEventArgs e) { DialogResult res = MessageBox.Show( "Update detected, perform unattended", "Update", MessageBoxButtons.YesNoCancel); switch (res) { case DialogResult.Yes: e.NextAction = NextUpdateAction.PerformUpdateUnattended; break; case DialogResult.Cancel: e.NextAction = NextUpdateAction.ProhibitUpdate; break; default: e.NextAction = NextUpdateAction.ShowStandardUserInterface; break; } } private void Form1_FormClosing (object sender, FormClosingEventArgs e) { _sparkle.StopLoop(); } private void buttonCheckUpdate_ClickAsync (object sender, EventArgs e) { Task.WaitAll(Task.Run(async () => await CheckAsync())); } private async Task CheckAsync() { SparkleUpdateInfo updateInfo = await _sparkle.CheckForUpdatesAtUserRequest(); textBoxOuput.Text += updateInfo.Status.ToString(); textBoxOuput.Text += updateInfo.Updates.ToString(); } } }
Der Company Name in der AssemblyInfo.cd ist zwingend zu ergänzen:
[assembly: AssemblyCompany("My Company")]
Nach dem Starten im Debugger und der manuellen Update Prüfung erscheint diese Fehlermeldung:
Um den Fehler zu finden ist es notwendig, mehr Informationen von Netsparkle auszugeben. Dazu ergänzt man
_sparkle.PrintDiagnosticToConsole = true;
Vor (!) dem StartLoop Aufruf. Im Output Fenster von Visual Studio erscheint nun:
netsparkle: Starting update loop… netsparkle: Reading config… netsparkle: Downloading and checking appcast netsparkle: Signature check of appcast failed netsparkle: No version information in app cast found netsparkle: Sleeping for an other 1440 minutes, exit event or force update check event netsparkle: Downloading and checking appcast netsparkle: Signature check of appcast failed netsparkle: No version information in app cast found
Im nächsten Schritt müssen wir uns laut Fehlermeldung um die passenden Signaturen kümmern.
Passende Signatur erstellen
Da NetSparkle Programme aus dem Internet herunterlädt und diese auf dem lokalen Rechner ausführt, sollte man die Update-Pakete absichern. Hierfür wird mit den mitgelieferten Tools zuerst ein Schlüsselpaar generiert.
Dazu erzeugen wir uns NetSparkleDSAHelper.exe, der ebenfalls auf der nuget Projektseite als Sourcecode vorliegt.
NetSparkle.DSAHelper.exe NetSparkle DSA Helper (c) 2011 Dirk Eisenberg under the terms of MIT license NetSparkle.DSAHelper.exe /genkey_pair Generates a public and a private DSA key pair which is stored in the current working directory. The private is stored in the file NetSparkle_DSA.priv The public key will be stored in a file named NetSparkle_DSA.pub. Add the public key file as resource to your application. NetSparkle.DSAHelper.exe /sign_update {YourPackage.msi} {NetSparkle_DSA.priv} Allows to sign an existing update package unattended. YourPackage.msi has to be a valid path to the package binary as self (mostly Windows Installer packages). The NetSparkle_DSA.priv has to be a path to the generated DAS private key, which has to be used for signing. NetSparkle.DSAHelper.exe /verify_update {YourPackage.msi} {NetSparkle_DSA.pub} "{Base64SignatureString}"Storing public key to NetSparkle_DSA.pub
1)
NetSparkle.DSAHelper.exe /genkey_pair NetSparkle DSA Helper (c) 2011 Dirk Eisenberg under the terms of MIT license Generating key pair with 1024 Bits... Storing private key to NetSparkle_DSA.priv Storing public key to NetSparkle_DSA.pub
2)
NetSparkle.DSAHelper.exe /sign_update setupproject.msi NetSparkle_DSA.priv ZivBB/nU40jhCWnz2ifOgAuHYotRYPSNXkObV5lvtK9RNM4bBD3+4Q==
Es wird ein öffentlicher und privater Schlüssel erzeugt.
Der öffentliche Schlüssel wird im Projekt hinzugefügt und als Embedded Resource markiert:
Hinzufügen des privaten Schlüssels in die appcast.xml
Am Server im IIS legt man folgende Filestruktur an:
<?xml version="1.0" encoding="utf-8"?> <rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/"> <channel> <title>AutoUpdateSolution</title> <link>http://obelix/NetSparkle/autoupdatesolution/versioninfo.xml</link> <description></description> <language>de</language> <item> <title>Version 1.0.1</title> <sparkle:releaseNotesLink>http://obelix/NetSparkle/autoupdatesolution/1.0.1/rnotes.md</sparkle:releaseNotesLink> <pubDate>Wed, 19 Jul 2017 17:00:00 +0000</pubDate> <enclosure url="http://obelix/NetSparkle/autoupdatesolution/1.0.1/SetupProject.msi" length="556032" type="application/octet-stream" sparkle:version="1.0.1" sparkle:dsaSignature="ZivBB/nU40jhCWnz2ifOgAuHYotRYPSNXkObV5lvtK9RNM4bBD3+4Q==" /> </item> </channel> </rss>
App.config Proxy Konfiguration
Falls im netsparkl Output eine Fehlermeldung auf ein Netzwerk Problem hinweist, kann es am Proxy liegen. Mit dieser Einstellung in der app.config werden die Settings von default proxy verwendet.
<!--?xml version="1.0" encoding="utf-8" ?--> <configuration> <startup> <supportedruntime version="v4.0" sku=".NETFramework,Version=v4.5.2"> </supportedruntime></startup> <system.net> <defaultproxy usedefaultcredentials="true"> </defaultproxy> </system.net> </configuration>
Signierte Updates erstellen – appcast.xml.dsa
Der Fehler “netsparkle: Signature check of appcast failed” deutet darauf hin, das am Server die appcast.xml.dsa entweder nicht vorhanden oder ungültig ist.
Ob im IIS die Extension „dsa“ auch konfiguriert wurde, testet man, indem man die Url im Browser aufruft:
http://obelix/NetSparkle/autoupdatesolution/appcast.xml.dsa
Hier war der Fehler im IIS, die Extension „dsa“ war nicht definiert.
HTTP Error 404.3 - Not Found
The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map.
hinzugefügter MIME Type .dsa
appcast.xml.dsa muss dazu mit NetSparkle.DSAHelper.exe erstellt werden.
Default Construktor ist mit _sparkle = new Sparkle("http://obelix/NetSparkle/autoupdatesolution/appcast.xml", SystemIcons.Application) der SecurityMode Strict.
Bei ungültiger sparkle:dsaSignature:
Gültige sparkle:dsaSignature:
Unsignierte Updates verwenden
Ist der Aufwand zu hoch, können mit diesem Constructor auch unsignierte Updates verwendet werden.
_sparkle = new Sparkle("http://obelix/NetSparkle/autoupdatesolution/appcast.xml", SystemIcons.Application, SecurityMode.Unsafe)
Release Notes
Die Release Notes werden in einem Markdown File mit der Endung „.md“, „.mkdn“, „.mkd“, „.markdown“ erstellt. Markdown ist eine einfache Auszeichnungssprache, dabei wird der Text in ein gültiges W3C XHTML umgewandelt. Als Editor kann man Visual Studio oder Visual Studio Code verwenden, es gibt diverse weitere Markdown Extensions [3].
Beispiel
# Sample Application Release Notes ## 1.0.1 * Fixed bug where ExecuteAsync sometimes doesn't send data
Die md Extension muss im IIS in den Mime Types hinzugefügt werden:
hinzugefügter MIME Type .md
Update Test
Das MSI Setup wird ohne UAC Dialog ausgeführt.
Fazit zu NetSparkle.new
Die Dokumentation des NetSparkle.new Projekts ist noch verbesserungswürdig.
Durch die verschiedenen Ableger von NetSparkle und deren leicht unterschiedlichen APIs, nicht dokumentieren Fehlermeldungen, blieb mir zeitweise nichts anderes übrig, als den Update Vorgang mit dem Sourcecode während des Update Vorganges zu debuggen. Zu einem appcast.xml Beispiel gelangt man, in dem man jenes vom Server der Demo App verwendet. Das für die Signatur ein weiteres appcast.xml.dsa File benötigt wird, findet man leider nur im Debugger heraus.
Zukunftsaussicht MSIX
Auf Windows Rechner werden Applikationen mit MSI oder Setup.exe installiert, Applikationen aus dem Store über appx. Der Nachfolger von MSI steht bereits in den Startlöchern, MSIX ein neues Containerformat vom Microsoft zur Software Auslieferung, dass alle Formate zusammenfasst. Damit kann man seine Produkte über den Store als auch über Downloads anbieten. MSIX bietet unter anderem eine höhere Sicherheit, auch Updates lassen sich damit einfacher einspielen. Das MSIX Setup Tool liegt bereits im Windows Store. Im April 2019 erscheint Visual Studio 2019, wir werden sehen, was hier für neue Möglichkeiten im Auslieferungsprozess angeboten werden.
Fazit – für welche Deployment Schiene entscheide ich mich nun
Sofern der .net Projekt Typ nicht bereits die Technologie der Auslieferung bestimmt und eine Auswahl möglich ist, haben beide Technologien seine Vor- und Nachteile.
Microsoft ClickOnce ist die schnelle einfache Alternative, bei der allerdings ein Zertifikat notwendig wird, sobald man seine Windows Domäne bei der Auslieferung verlässt. In der eigenen Domäne, bei mehrfach durch Benutzer genutzten Rechner, ist eine Installation für jeden einzelnen User am gleichen Rechner notwendig. Wobei jeder User dies ohne zusätzliche Admin Rechte selber ausführen kann.
Handelt es sich um eine anspruchsvolle Anwendung, bei der auch Treiber installiert oder ähnliche Eingriffe im Windows System notwendig werden, also alles was über den User spezifischen Ordner wie bei ClickOnce hinausgeht, bleibt ohnehin nur mehr ein klassische Windows Setup über. Aber mit NetSparkle.new nun auch mit einer automatischen Update Möglichkeit.
Links & Quellen
[1] nugetmusthaves
http://nugetmusthaves.com/Tag/AutoUpdate
[2] NetSparkle.New
https://www.nuget.org/packages/NetSparkle.New/
[3] Markdown
https://de.wikipedia.org/wiki/Markdown
Autorenbox
Thomas Reinwart verfügt über umfangreiche Berufserfahrung auf dem IT Sektor. In den letzten 25 Jahren war er in den Bereichen Softwareentwicklung, Softwaredesign, Architekt und als Consultant tätig. Technischer Fokus ist derzeit Microsoft .net und SQL Server, wo er alle aktuellen Microsoft Zertifizierungen hat.
Email: office@reinwart.com
Franz war pensionierter HTL Lehrer (TGM), Präsident von ClubComputer, Herausgeber der Clubzeitung PCNEWS und betreute unser Clubtelefon und Internet Support. Er war leidenschaftlicher Rapid Wien Fan. Er ist leider Anfang Jänner 2024 nach langer schwerer Krankheit verstorben.
Neueste Kommentare