Im Zuge der Zukunftssicherung (Heute nennt man das Refactoring) eines meiner älteren aber immer noch aktiven Projekte, einem Windows Forms Projekt sollte es dieses mal nicht nur dem eigentlichen Code sondern dem gesamten Build und Deployment Prozess an den Kragen gehen.
Mit den, bis zur Visual Studio 2010 enthaltenen, und Stand Heute mit der nächsten Visual Studio Version nicht mehr unterstützen, “Visual Studio Setup Projekte”, war ich eigentlich noch nie wirklich zufrieden. Also muss etwas neues her, mit dem ich dieses ungeliebte Setup Projekt ersetzen kann und mein Projekt auch noch mit der nächsten Visual Studio Version weiter entwickeln und distribuieren kann.
Nach ein wenig Recherche und ein wenig Gezwitscher auf Twitter stand fest, dass WiX wohl die beste Lösung sei um das Deployment der Anwendung durchzuführen.
WiX ist ein von Microsoft initiiertes Open Source Projekt dass unter anderem von Microsoft selbst für die Distribution von Microsoft Office verwendet wird.
Nachdem klar war, das WiX in Zukunft bei mir für die Installation meines Projektes zuständig sein soll, habe ich über Twitter von Sebastian Seidel einen Link auf einen Beitrag erhalten in welchem beschrieben wird wie man in 5 einfachen Schritten aus einem Visual Studio Setup ein WiX Setup Projekt machen kann.
Ich bin dann dieser Anleitung gefolgt und habe die Konvertierung und Änderungen laut dem oben genannten Beitrag durchgeführt.
Wobei ich sagen muss, dass diese Konvertierung zwar ganz schön ist, aber leider nicht so ohne weiteres direkt zum Erfolg führt.
Ich habe dann mit ein wenig Hilfe von Sebastian, Danke noch mal dafür, relativ schnell, erst einmal ein generell funktionierendes Setup mit WiX zum laufen bekommen. Leider noch ohne wirklich zu wissen wie WiX tatsächlich funktioniert und wie umfangreich WiX in Wirklichkeit ist.
Da ich aber nicht so leicht zufrieden zu stellen bin und mir das WiX Setup, welches aus dem decompilierten alten Setup erstellt wurde, nicht gefallen hat (da waren zu viele statische Angaben enthalten und es hatte keine echte nachvollziehbare Struktur) habe ich mich mit WiX näher beschäftigt.
Nachdem ich ein paar Tage mit WiX gearbeitet habe, würde ich sagen, dass es sich nicht lohnt ein Setup Projekt zu decompilieren um dieses dann manuell nachzuarbeiten.
Meiner Meinung nach ist es einfacher ein Basistemplate mit den wichtigsten Funktionalitäten, gepaart mit ein wenig Grundwissen über WiX zu nehmen und sich das benötigte Setup Projekt manuell zu erstellen.
Und genau ein solches Basistemplate möchte ich hier zusammen mit dem notwendigen Basiswissen zu WiX vorstellen und vermitteln.
Wer WiX noch nicht auf seinem Rechner installiert hat, sollte das nun aber spätestens nachholen.
Hier der Link zum Download von WiX
Nachdem man WiX installiert hat und Visual Studio startet, findet man dort neue Projektvorlagen:
In diesem Beitrag dreht sich alles nur um das eigentliche Setup Projekt die anderen Projekt Vorlagen sind dann eher für die Fortgeschrittenen.
Um die Funktionsweise von WiX direkt selbst ausprobieren zu können, habe ich ein Testprojekt erstellt dass sowohl eine Windows Forms Anwendung, wie eine Class Library und das zugehörige Wix Setup Projekt in einer Solution enthält.
Also einfach die Solution (VS2010) herunterladen, entpacken, BS starten und loslegen.
Der Einfachheit halber habe ich alle Erklärungen direkt als Kommentar in das WiX Skript aufgenommen.
So hier das WiX Script zum anschauen. Im Anschluss folgt dann auch noch der Link zum herunterladen der gesamten Test Solution.
<?xml version="1.0" encoding="UTF-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <!-- <?define Manufactor = "Hans-Peter Schelian"?> Anstelle den Manufactor an mehreren Stellen anzugeben weise ich hier lieber einer Variablen den Wert zu und verwende im weiteren Verlauf einfach die Variable --> <!-- <?define UpgradeGuid = "{3B563A68-99BC-4528-9356-295ADB67909A}"?> Ganz Wichtig, die Guid für den UpgradeCode darf nach der ersten Installation nicht mehr geändert werden aus diesem Grund weise ich diese Guid einer Variablen zu die ich dann im weiteren Verlauf im Bereich <Product UpgradeCode="$(var.UpgradeGuid)" /> und im Bereich <Upgrade Id="$(var.UpgradeGuid)" anstelle der Eingabe der eigentlichen Guid verwenden kann. --> <!-- Hier nun alle Defines die durch den Präprozessor aufgelöst werden --> <?define Manufactor = "Hans-Peter Schelian"?> <?define UpgradeGuid = "{3B563A68-99BC-4528-9356-295ADB67909A}"?> <!-- Erklärungen zum Bereich <Product> Siehe auch http://wix.sourceforge.net/manual-wix3/wix_xsd_product.htm Id="*" Mit dem + wird erreicht, dass bei jedem erzeugen des Setup eine neue Guid erstellt wird. Man könnte natürlich auch alternativ eine feste Guid vergeben, dass würde dann so ausssehen: Id="{2BE1FDE8-FAEE-48B5-AA08-FD2FE26D7FB2}" Name="WiXDemoProjekt" Name des Projektes wie es in "Programme und Funktionen" angezeigt wird Version="!(bind.FileVersion.MyApplicationFile)" - Das ist die Programmversion des Produktes, entweder man muss die Version manuell angeben: Version="1.0.1.1" oder man kann wie ich es hier demonstriere, Binder Variablen des WiX Linker verwenden um Dynamisch die Vesionsnummer aus einer beliebiegen Datei des Projektes zu ermitteln. Im Beispiel verwende ich die Möglichkeit die Version welche mit [assembly: AssemblyFileVersion("1.0.0.0")] in der AssemblyInfo gesetzt wurde aus der Datei auszulesen. Manufacturer="$(var.Manufactor)" Zuweisung des in der Präprozessor Variablen gespeicherten Herstellers, alternativ kann man natürlich auch eine manuelle Zuweiung Manufacturer="Hans-Peter Schelian" vornehmen UpgradeCode="$(var.UpgradeGuid)"> Zuweisung der in der Präprozessor Variablen gespeicherten Guid, alternativ kann man die Zuweisung UpgradeCode="{2BE1FDE8-FAEE-48B5-AA08-FD2FE26D7FB2}"> auch manuell durchführen. --> <Product Id="*" Name="WiXDemoProjekt" Language="1033" Version="!(bind.FileVersion.MyApplicationFile)" Manufacturer="$(var.Manufactor)" UpgradeCode="$(var.UpgradeGuid)"> <!-- Erklärungen zum Bereich <Package> http://wix.sourceforge.net/manual-wix3/wix_xsd_package.htm InstallerVersion="200" Ab Windows Installer 2.0 Compressed="yes" - So werden die Dateien im MSI File komprimiert Description="Installiert das WiXDemoProjekt" - Eine Beschreibung für das Installer Package Manufacturer="Hans-Peter Schelian" - Und wieder der Hersteller siehe auch <Procuct Manufacturer="" /> Comments="Ich wünsche allen viel Spaß mit diesem Beispiel" - Das was es bedeutet ein Kommentar --> <Package InstallerVersion="200" Compressed="yes" Description="Installiert das WiXDemoProjekt" Manufacturer="$(var.Manufactor)" Comments="Ich wünsche allen viel Spaß mit diesem Beispiel" /> <!-- Erklärungen zum Bereich <Media> Siehe auch http://wix.sourceforge.net/manual-wix3/wix_xsd_media.htm --> <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" /> <!-- ARP Support Oder wie konfiguriert man Add/Remove Programs mit dem Windows Installer Siehe auch http://msdn.microsoft.com/en-us/library/aa368032.aspx --> <Property Id="ARPHELPTELEPHONE" Value="XXXXX/XXXXXXX" /> <Property Id="ARPHELPLINK" Value="https://blog.schelian.de" /> <Property Id="ARPCONTACT" Value="Schelian IT Beratung" /> <Property Id="ARPCOMMENTS" Value="Alles WiX oder was" /> <Property Id="ARPURLINFOABOUT" Value="http://www.schelian.de" /> <!-- Erklärungen zum Bereich <Upgrade> Upgrade Id="$(var.UpgradeGuid)"> hier Zuweisung durch Präprozessor Variable, siehe auch Hinweise zu define UpgradeGuid <UpgradeVersion Minimum="!(bind.FileVersion.MyApplicationFile)" - Hier wird festgestellt ob bereits ein neueres Produkt (Neuere Version) installiert ist. <UpgradeVersion Minimum="0.1.0.0" - Prüfung ob bereits eine frühere Version installiert ist <UpgradeVersion Minimum="1.0.0.0" Maximum="99.0.0.0" - Ist das Prpdukt in welcher Versio auch immer bereits auf dem Rechner installiert, dann setzt die internet Variable PREVIOUSVERSIONSINSTALLED, dadurch wird verhindert, dass selbst dann wenn man eine Version die nicht als Upgrade gilt (nur im vierten Bereich) der Versionsnummer geändert ist, nicht dazu führt, dass es zu doppelten Einträgen im Add/Remove Programms kommt. --> <Upgrade Id="$(var.UpgradeGuid)"> <UpgradeVersion Minimum="!(bind.FileVersion.MyApplicationFile)" IncludeMinimum="no" OnlyDetect="yes" Language="1033" Property="NEWPRODUCTFOUND" /> <UpgradeVersion Minimum="0.1.0.0" IncludeMinimum="yes" Maximum="!(bind.FileVersion.MyApplicationFile)" IncludeMaximum="no" Language="1033" Property="UPGRADEFOUND" /> <UpgradeVersion Minimum="1.0.0.0" Maximum="99.0.0.0" Property="PREVIOUSVERSIONSINSTALLED" IncludeMinimum="yes" IncludeMaximum="no" /> </Upgrade> <!-- So nun wird die Verzeichnis Struktur für die Installation aufgebaut <Directory Id="TARGETDIR" Name="SourceDir"> Diese Zeile setzt das Root Verzeichnis der Installation "\" <Directory Id="ProgramFilesFolder"> verwendet eine durch den Windows Installer vordefinierte Id zur Verwendung der Standard Programmpfads. "C:\Program Files" <Directory Id="CompanyFolder" Name="Schelian IT Beratung"> "C:\Program Files\Schelian IT Beratung\" <Directory Id="INSTALLLOCATION" Name="WiXDemoProjekt"> "C:\Program Files\Schelian IT Beratung\WiXDemoProjekt\" --> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder"> <Directory Id="CompanyFolder" Name="Schelian IT Beratung"> <Directory Id="INSTALLLOCATION" Name="WiXDemoProjekt"> <!-- Und dahinein packen wir eine Component, in diesem Fall Unser Ausführbares Programm Jede Component muss eine eindeutige Id bestzen, diese wird weiter unter verwendet um die Component einem Feature zuzuordnen. Eine Component sollte eine Guid besitzten, damit es später Möglich ist anhand dieser Guid die Datei eindeutig zu identifizieren, damit man mit dem MS Installer, eine Reparatur einer vorhandenen Installation durchführen kann. --> <Component Id="ProductComponent" Guid="{4A5619DF-5841-48EC-8D7C-D368718C6D1A}"> <!-- Innerhalb der Komponente bestimmen wir dann die zu installierende Datei Jedes File muss einen eindeutige Id besitzen, der Name ist der Dateiname am Zielort und mit Source wird die Quelle der Datei angegeben. Name="$(var.WixDemoProjekt.TargetFileName)" nutzt die Möglichkeit den Zielnamen aus dem Visual Studio Projekt zu übernehmen, dass hat den Vorteil, dass man sich keine Gedanken darüber machen muss das Setup anfassen zu müssen, wennm man den Ausgabe Namen im VS Projekt ändert. Source="$(var.WixDemoProjekt.TargetPath)" nutzt ebenfalls die Möglicheit der Visual Studio Project References und Variablen. Siehe auch http://wix.sourceforge.net/manual-wix3/votive_project_references.htm Man miuss übrigens um diese References nutzen zu können im WiX Setup Projekt eine Reference auf das Visual Studio Projekt setzen. KeyPath="yes" Für nähere Informatinonen siehe http://wix.sourceforge.net/manual-wix3/wix_xsd_file.htm --> <File Id="MyApplicationFile" Name="$(var.WixDemoProjekt.TargetFileName)" Source="$(var.WixDemoProjekt.TargetPath)" KeyPath="yes"> <!-- Dann legen wir doch gleich noch einen Desktop Shortcut für das Projekt an. --> <Shortcut Advertise="yes" Id="DesktopShortCut" Name="WiXDemoProjekt" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" Description="WiXDemoProjekt Programm ausführen" Icon="Icon.exe"> <!-- Und dieses Icon soll für den Shortcut verwendet werden --> <Icon Id="Icon.exe" SourceFile="$(var.WixDemoProjekt.TargetPath)" /> </Shortcut> </File> </Component> <!-- Nun muss für jedes weitere File dass installiert werden soll, eine Component erstellt werden Es wäre auch möglich mehrere Files in einer Component zusammenzufassen, allerdings würde ich das nur empfehlen, wenn man tgatsächlich einzeln zu installierende Feature implementiert, die je Component aus mehereren Dateien bestehen. --> <Component Id="WiXDemoLib" Guid="{777028E8-DA56-400E-9C71-844AE328E8BF}"> <!-- Hier nun die zu installierende Datei der Component hinzufügen --> <File Id="WiXDemoLib" Name="WiXDemoLib.dll" Source="$(var.WixDemoProjekt.TargetDir)WiXDemoLib.dll" KeyPath="yes" /> </Component> <!-- Beispiel wie man eine App.Config in das Setup aufnehmen und während der Setup Erstellung umbenennen kann Hier wird aus der app.config die WixDemoProjekt.Exe.Config --> <Component Id="app.config" Guid="{EE4CA09D-2C10-48E4-946A-D8B9242F557E}"> <File Id="app.config" Name="WixDemoProjekt.Exe.Config" Source="$(var.WixDemoProjekt.ProjectDir)app.config" KeyPath="yes" /> </Component> <!-- Und hier ein Beispiel wie man Datei in Unterverzeichnis am Zielort kopieren kann--> <Directory Id="IMAGEFOLDER" Name="Images"> <Component Id="WiX.image" Guid="{ACD0322F-8170-4C67-84BC-161FCEE6274A}"> <File Id="WiX.image" Name="wixlogo.png" Source="$(var.WixDemoProjekt.TargetDir)images\wixlogo.png" KeyPath="yes" /> </Component> </Directory> </Directory> </Directory> </Directory> <!-- Verknüpfungen im Startmenü setzen Siehe auch http://wix.sourceforge.net/manual-wix3/create_start_menu_shortcut.htm --> <Directory Id="ProgramMenuFolder"> <Directory Id="MyShortCutsDir" Name="WiXDemoProjekt"> <Component Id="ShortCutComponent" Guid="{B51BC276-F509-4F25-9738-EE176445D073}"> <Shortcut Id="ProgShortCut" Name="WiXDemoProjekt" Description="WiXDemoProjekt Programm Verkmüpfung" Target="[INSTALLLOCATION]WixDemoProjekt.Exe"></Shortcut> <Shortcut Id="UninstallShortCut" Name="Uninstall WiXDemoProjekt" Target="[System64Folder]msiexec.exe" Arguments="/x [ProductCode]"/> <RemoveFolder Id="RemoveMyShortCuts" On="uninstall"/> <RegistryValue Root="HKCU" Key="Software\Schelian IT Beratung\WiXDemoProjekt" Name="installed" Type="integer" Value="1" KeyPath="yes"/> </Component> </Directory> </Directory> <!-- Wird benötigt um den Desktop Shortcut anlegen zu können --> <Directory Id="DesktopFolder" Name="Desktop" /> </Directory> <!-- Feature Bereich hier als Basistemplate nur ein Feature (Alles installieren) Siehe auch http://wix.sourceforge.net/manual-wix3/wix_xsd_feature.htm --> <Feature Id="ProductFeature" Title="WiXDemoProjekt" Level="1"> <ComponentRef Id="ProductComponent" /> <ComponentRef Id="WiXDemoLib" /> <ComponentRef Id="app.config" /> <ComponentRef Id="WiX.image" /> <ComponentRef Id="ShortCutComponent" /> </Feature> <!-- Das was nun folgt ist schon ein wenig Finetuning CustomActions siehe auch http://wix.sourceforge.net/manual-wix3/wix_xsd_customaction.htm --> <!-- Keinen Downgrade zulassen --> <CustomAction Id="PreventDowngrading" Error="Es ist bereits eine neuere Version instlliert." /> <!-- Nun noch die auszuführende Reihenfolg angeben siehe auch http://wix.sourceforge.net/manual-wix3/wix_xsd_installexecutesequence.htm --> <InstallExecuteSequence> <Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom> <RemoveExistingProducts After="InstallFinalize" /> </InstallExecuteSequence> <!-- Wenn ein neueres Produkt gerunden wurde, soll das auch in der UI ausgegeben werden siehe auch http://wix.sourceforge.net/manual-wix3/wix_xsd_installuisequence.htm --> <InstallUISequence> <Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom> </InstallUISequence> <!-- So kann man eine eigene Lizenzmeldung ausgeben --> <WixVariable Id='WixUILicenseRtf' Overridable='yes' Value='$(var.WixDemoProjekt.ProjectDir)Lizenzbestimmungen.rtf'/> <!-- UI Definition für Minimales Setup Um diese UI Definition vornehmen zu können, muss im WiX Projekt die WixUIExtension.dll als Reference hinzugefügt werden --> <UIRef Id="WixUI_Minimal" /> <UIRef Id="WixUI_ErrorProgressText" /> </Product> </Wix>