WiX Tricks und Tipps


264

Wir verwenden WiX bereits seit einiger Zeit und trotz der üblichen Probleme mit der Benutzerfreundlichkeit läuft es recht gut. Was ich suche, ist ein nützlicher Rat in Bezug auf:

  • Einrichten eines WiX-Projekts (Layout, Referenzen, Dateimuster)
  • WiX in Lösungen integrieren und Prozesse erstellen / freigeben
  • Konfigurieren von Installationsprogrammen für Neuinstallationen und Upgrades
  • Alle guten WiX-Hacks, die Sie teilen möchten

Werfen
Sie

10
Geschlossen als nicht konstruktiv? Ich habe viel gelernt, indem ich diese Frage gestellt habe! Ein wenig Konsistenz von StackOverflow wäre auch schön ... zB stackoverflow.com/questions/550632/…
si618

15
Es hat '203' Ups, das ist genug, um seine Nützlichkeit zu beweisen.
TarunG

SO-Fragen müssen eine endgültige, korrekte Antwort haben. Offene Fragen lassen die Fragen, die Menschen zu tatsächlichen Problemen stellen, von der Titelseite fallen. faq @Si.: Diese Richtlinie war schon immer bei AFAIK vorhanden, wird aber jetzt besser durchgesetzt. Diese Frage ist fast drei Jahre alt.
Jim Dagg

Fair genug, Jim, es ist eine offene Frage, und ich denke, es liegt an der SO-Community, zu entscheiden, aber ich muss sagen, dass es seltsam erscheint, sie als nicht konstruktiv zu schließen, da ich und anscheinend viele andere Menschen Ich habe diese Frage nützlich gefunden (z. B. goo.gl/Zqp2X ) und sie passt sehr gut zum practical, answerable questions based on actual problems that you faceTeil der FAQ.
Si618

Antworten:


157
  1. Bewahren Sie Variablen in einer separaten wxiInclude-Datei auf. Ermöglicht die Wiederverwendung, Variablen sind schneller zu finden und ermöglichen (falls erforderlich) eine einfachere Bearbeitung durch ein externes Tool.

  2. Definieren Sie Plattformvariablen für x86- und x64-Builds

    <!-- Product name as you want it to appear in Add/Remove Programs-->
    <?if $(var.Platform) = x64 ?>
      <?define ProductName = "Product Name (64 bit)" ?>
      <?define Win64 = "yes" ?>
      <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
    <?else ?>
      <?define ProductName = "Product Name" ?>
      <?define Win64 = "no" ?>
      <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
    <?endif ?>
  3. Speichern Sie den Installationsort in der Registrierung, damit Upgrades den richtigen Speicherort finden können. Zum Beispiel, wenn ein Benutzer ein benutzerdefiniertes Installationsverzeichnis festlegt.

     <Property Id="INSTALLLOCATION">
        <RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)"
                  Key="Software\Company\Product" Name="InstallLocation" />
     </Property>

    Hinweis : Der WiX-Guru Rob Mensching hat einen hervorragenden Blogeintrag veröffentlicht, der detaillierter wird und einen Randfall behebt, wenn Eigenschaften über die Befehlszeile festgelegt werden.

    Beispiele mit 1. 2. und 3.

    <?include $(sys.CURRENTDIR)\Config.wxi?>
    <Product ... >
      <Package InstallerVersion="200" InstallPrivileges="elevated"
               InstallScope="perMachine" Platform="$(var.Platform)"
               Compressed="yes" Description="$(var.ProductName)" />

    und

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
  4. Der einfachste Ansatz besteht immer darin, größere Upgrades durchzuführen , da sowohl Neuinstallationen als auch Upgrades in einem einzigen MSI möglich sind. UpgradeCode ist auf eine eindeutige Richtlinie festgelegt und wird sich nie ändern, es sei denn, wir möchten kein vorhandenes Produkt aktualisieren.

    Hinweis : In WiX 3.5 gibt es ein neues MajorUpgrade- Element, das das Leben noch einfacher macht !

  5. Erstellen eines Symbols in Software

    <Icon Id="Company.ico" SourceFile="..\Tools\Company\Images\Company.ico" />
    <Property Id="ARPPRODUCTICON" Value="Company.ico" />
    <Property Id="ARPHELPLINK" Value="http://www.example.com/" />
  6. Bei Release-Builds versionieren wir unsere Installationsprogramme und kopieren die MSI-Datei in ein Bereitstellungsverzeichnis. Ein Beispiel hierfür für die Verwendung eines wixproj-Ziels, das vom AfterBuild-Ziel aufgerufen wird:

    <Target Name="CopyToDeploy" Condition="'$(Configuration)' == 'Release'">
      <!-- Note we append AssemblyFileVersion, changing MSI file name only works with Major Upgrades -->
      <Copy SourceFiles="$(OutputPath)$(OutputName).msi" 
            DestinationFiles="..\Deploy\Setup\$(OutputName) $(AssemblyFileVersion)_$(Platform).msi" />
    </Target>
  7. Verwenden Sie Hitze, um Dateien mit Platzhalter (*) Guid zu ernten. Nützlich, wenn Sie WXS-Dateien für mehrere Projekte wiederverwenden möchten (siehe meine Antwort zu mehreren Versionen desselben Produkts). Diese Batchdatei sammelt beispielsweise automatisch die RoboHelp-Ausgabe.

    @echo off  
    robocopy ..\WebHelp "%TEMP%\WebHelpTemp\WebHelp" /E /NP /PURGE /XD .svn  
    "%WIX%bin\heat" dir "%TEMP%\WebHelp" -nologo -sfrag -suid -ag -srd -dir WebHelp -out WebHelp.wxs -cg WebHelpComponent -dr INSTALLLOCATION -var var.WebDeploySourceDir 

    Es ist ein bisschen los, robocopywenn Subversion-Arbeitskopien-Metadaten vor dem Ernten entfernt werden. Die -drStammverzeichnisreferenz wird auf unseren Installationsort und nicht auf das Standard-TARGETDIR festgelegt. -varwird verwendet, um eine Variable zum Angeben des Quellverzeichnisses zu erstellen (Web Deployment-Ausgabe).

  8. Einfache Möglichkeit, die Produktversion in den Titel des Begrüßungsdialogs aufzunehmen, indem Sie Strings.wxl zur Lokalisierung verwenden. (Credit: saschabeaumont . Hinzugefügt, da dieser tolle Tipp in einem Kommentar versteckt ist)

    <WixLocalization Culture="en-US" xmlns="http://schemas.microsoft.com/wix/2006/localization">
        <String Id="WelcomeDlgTitle">{\WixUI_Font_Bigger}Welcome to the [ProductName] [ProductVersion] Setup Wizard</String>
    </WixLocalization>
  9. Sparen Sie sich Schmerzen und befolgen Sie die Anweisungen von Wim Coehen zu einer Komponente pro Datei. Auf diese Weise können Sie auch *die Komponenten-GUID weglassen (oder mit einem Platzhalter versehen) .

  10. Rob Mensching hat eine gute Möglichkeit , Probleme in MSI-Protokolldateien durch Suchen schnell aufzuspüren value 3. Beachten Sie die Kommentare zur Internationalisierung.

  11. Wenn Sie bedingte Funktionen hinzufügen, ist es intuitiver, die Standardfunktionsebene auf 0 (deaktiviert) und dann die Bedingungsstufe auf den gewünschten Wert festzulegen. Wenn Sie die Standardfeature-Ebene> = 1 festlegen, muss die Bedingungsstufe 0 sein, um sie zu deaktivieren. Dies bedeutet, dass die Bedingungslogik das Gegenteil von dem sein muss, was Sie erwarten, was verwirrend sein kann :)

    <Feature Id="NewInstallFeature" Level="0" Description="New installation feature" Absent="allow">
      <Condition Level="1">NOT UPGRADEFOUND</Condition>
    </Feature>
    <Feature Id="UpgradeFeature" Level="0" Description="Upgrade feature" Absent="allow">
      <Condition Level="1">UPGRADEFOUND</Condition>
    </Feature>

Über das Hinzufügen des Symbols in den Programmen hinzufügen / entfernen ist es genau das, wonach ich gesucht habe. Wo steckst du diese drei Zeilen? +1 für schiere Großartigkeit.
Everett

Ich neige dazu, sie direkt nach (und offensichtlich unter) dem <Package> -Element zu platzieren. Schauen Sie sich das Schema für die Gültigkeit an wix.sourceforge.net/manual-wix3/schema_index.htm
si618

+1, ich wünschte, ich könnte +100 machen, dies ist die nützlichste Wix-Information, über die ich gestolpert bin.
Tim Long

Danke Tim! Rob Mensching, Bob Arson, Wim Coehen und andere verdienen das Lob für den Austausch ihres Wissens.
Si618

38

Überprüfen, ob IIS installiert ist:

<Property Id="IIS_MAJOR_VERSION">
    <RegistrySearch Id="CheckIISVersion" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp" Name="MajorVersion" Type="raw" />
</Property>

<Condition Message="IIS must be installed">
    Installed OR IIS_MAJOR_VERSION
</Condition>

Überprüfen, ob IIS 6 Metabase Compatibility unter Vista + installiert ist:

<Property Id="IIS_METABASE_COMPAT">
    <RegistrySearch Id="CheckIISMetabase" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp\Components" Name="ADSICompatibility" Type="raw" />
</Property>

<Condition Message="IIS 6 Metabase Compatibility feature must be installed">
    Installed OR ((VersionNT &lt; 600) OR IIS_METABASE_COMPAT)
</Condition>

34

Bewahren Sie alle IDs in separaten Namespaces auf

  • Funktionen beginnen mit F. Beispielen: F.Documentation, F.Binaries, F.SampleCode.
  • Komponenten beginnen mit Beispiel C. : C.ChmFile, C.ReleaseNotes, C.LicenseFile, C.IniFile, C.Registry
  • Benutzerdefinierte Aktionen sind Beispiel CA. : CA.LaunchHelp, CA.UpdateReadyDlg, CA.SetPropertyX
  • Dateien sind Fi.
  • Verzeichnisse sind Di.
  • und so weiter.

Ich finde, dies hilft immens dabei, die verschiedenen IDs in den verschiedenen Kategorien im Auge zu behalten.


Ich verwende keine Namespaces, aber ich füge die IDs hinzu. Zum Beispiel: ExamplesFeature, ChmFileComponent. Ich glaube, ich liebe es
;-)

25

Fantastische Frage. Ich würde gerne einige Best Practices sehen.

Ich habe viele Dateien, die ich verteile, also habe ich mein Projekt in mehrere wxs-Quelldateien eingerichtet.

Ich habe eine Quelldatei der obersten Ebene, die ich Product.wxs nenne und die im Grunde die Struktur für die Installation enthält, aber nicht die eigentlichen Komponenten. Diese Datei besteht aus mehreren Abschnitten:

<Product ...>
  <Package ...>
    <Media>... 
   <Condition>s ...
   <Upgrade ..>
   <Directory> 
        ...
   </Directory>
   <Feature>
      <ComponentGroupRef ... > A bunch of these that
   </Feature>
   <UI ...>
   <Property...>
   <Custom Actions...>
   <Install Sequences....
  </Package>
</Product>

Die restlichen .wix-Dateien bestehen aus Fragmenten, die ComponentGroups enthalten, auf die im Feature-Tag in Product.wxs verwiesen wird. Mein Projekt enthält eine schöne logische Gruppierung der Dateien, die ich verteile

<Fragment>
   <ComponentGroup>
     <ComponentRef>
     ....
    </ComponentGroup>
    <DirectoryRef>
      <Component... for each file
      .... 
    </DirectoryRef>
</Fragment>

Dies ist nicht perfekt, mein OO-Spinnengefühl kribbelt ein wenig, weil die Fragmente auf Namen in der Datei Product.wxs verweisen müssen (z. B. DirectoryRef), aber ich finde es einfacher, diese einzelne große Quelldatei zu pflegen.

Ich würde gerne Kommentare dazu hören oder wenn jemand auch gute Tipps hat!


Unser Setup ist diesem Ansatz ebenfalls sehr ähnlich. Es ist gut, weil wir unser Äquivalent zu Products.wxs als Basis-Setup für eine Vielzahl von Produkten verwenden können.
Si618

@ Peter Tate: Dein Spinnengefühl ist korrekt. Siehe meine Antwort zum Verzeichnis-Aliasing.
Wim Coenen

Ich gehe genauso vor: Product.wxs mit Layout ist statisch, und eine Build-Task (heat.exe) generiert meine Content.wxs-Datei
timvw

20

Fügen Sie dem Exit-Dialogfeld ein Kontrollkästchen hinzu, um die App oder die Hilfedatei zu starten.

...

<!-- CA to launch the exe after install -->
<CustomAction Id          ="CA.StartAppOnExit"
              FileKey     ="YourAppExeId"
              ExeCommand  =""
              Execute     ="immediate"
              Impersonate ="yes"
              Return      ="asyncNoWait" />

<!-- CA to launch the help file -->
<CustomAction Id         ="CA.LaunchHelp"
              Directory  ="INSTALLDIR"
              ExeCommand ='[WindowsFolder]hh.exe IirfGuide.chm'
              Execute    ="immediate"
              Return     ="asyncNoWait" />

<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT"
          Value="Launch MyApp when setup exits." />

<UI>
  <Publish Dialog  ="ExitDialog"
           Control ="Finish"
           Order   ="1"
           Event   ="DoAction"
           Value   ="CA.StartAppOnExit">WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT</Publish>
</UI>

Wenn Sie dies auf diese Weise tun, ist das "Standard" -Auftritt nicht ganz richtig. Das Kontrollkästchen ist immer grau hinterlegt, während der Dialog weiß ist:

Alternativtext http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_1.gif

Eine Möglichkeit, dies zu umgehen, besteht darin , einen eigenen benutzerdefinierten ExitDialog mit einem Kontrollkästchen an einem anderen Ort anzugeben . Dies funktioniert, scheint aber eine Menge Arbeit zu sein, nur um die Farbe eines Steuerelements zu ändern. Eine andere Möglichkeit, das Gleiche zu lösen, besteht darin, die generierte MSI nachzubearbeiten, um die X- und Y-Felder in der Steuertabelle für das jeweilige CheckBox-Steuerelement zu ändern. Der Javascript-Code sieht folgendermaßen aus:

var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
var sql = "UPDATE `Control` SET `Control`.`Height` = '18', `Control`.`Width` = '170'," +
          " `Control`.`Y`='243', `Control`.`X`='10' " +
          "WHERE `Control`.`Dialog_`='ExitDialog' AND " + 
          "  `Control`.`Control`='OptionalCheckBox'";
var view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();

Wenn Sie diesen Code als Befehlszeilenskript (unter Verwendung von cscript.exe) ausführen, nachdem die MSI generiert wurde (aus light.exe), wird ein ExitDialog erstellt, der professioneller aussieht:

Alternativtext http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_2.gif


Ha! Nicht mein Blog. Ich habe es auch gelesen. Und ich habe einen Link zum Blogeintrag im obigen Text. Aber sie haben es anders gemacht als ich. Ich mag meinen Weg besser. !!
Cheeso

1
Danke für das js, sehr hilfreich! Eine Sache , die ich in dem wxs zu ändern hatte , ist ersetzen WIXUI_EXITDIALOGOPTIONALCHECKBOXmit WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installedinnen<Publish>
Alexander Kojevnikov

Gibt es eine Möglichkeit, das Kontrollkästchen standardmäßig zu aktivieren?
Alek Davis

Um das Kontrollkästchen standardmäßig zu aktivieren, habe ich Folgendes verwendet: <Property Id = "WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value = "1" />
Alek Davis

Scheint eine raffinierte Lösung zu sein, aber wie verwende ich sie? Gibt es eine Möglichkeit, das js in das <AfterBuild> -Element in meinem wixproj einzufügen? Oder, da Sie sich darauf beziehen, es über die Befehlszeile auszuführen, ist es besser als Post-Build-Ereignis. In diesem Fall, was ist ein guter js-Befehlszeileninterpreter für Windows?
Vanmelle

18

Erstellen von Live-, Test-, Schulungs-, ... Versionen mit denselben Quelldateien.

Kurz gesagt: Erstellen Sie einen eindeutigen UpgradeCode für jedes Installationsprogramm und definieren Sie automatisch das erste Zeichen jeder Guid für jedes Installationsprogramm, wobei die verbleibenden 31 eindeutig bleiben.

Voraussetzungen

Annahmen

  • WiX-Variablen werden verwendet, um UpgradeCode, ProductName, InstallName zu definieren.
  • Sie haben bereits ein funktionierendes Installationsprogramm. Ich würde es nicht versuchen, bis Sie es tun.
  • Alle Ihre Komponenten werden in einer Datei (Components.wxs) gespeichert. Dieser Vorgang funktioniert, wenn Sie mehrere Dateien haben. Es ist nur noch mehr Arbeit zu erledigen.

Verzeichnisaufbau

  • Setup.Library
    • Alle wxs-Dateien (Komponenten, Funktionen, UI-Dialoge, ...)
    • Common.Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder, ...)
  • Setup.Live (wixproj)
    • Verknüpfen Sie alle Setup.Library-Dateien mit "Vorhandene Datei hinzufügen" -> "Als Link hinzufügen" (die kleine Schaltfläche mit dem Abwärtspfeil direkt neben der Schaltfläche "Hinzufügen" in Visual Studio).
    • Config.wxi (hat einen eindeutigen UpgradeCode, ProductName, InstallName, ...)
  • Setup.Test , ...
    • wie live, aber Config.wxi ist für die Testumgebung konfiguriert.

Prozess

  • Erstellen Sie das Verzeichnis Setup.Library und verschieben Sie alle Ihre wxs- und wxi-Dateien (außer Config.wxi) aus dem vorhandenen Projekt.
  • Erstellen Sie Setup.Live, Setup.Test usw. wie gewohnt wixproj.
  • Fügen Sie das BeforeBuild-Ziel in wixproj in Setup.Live usw. hinzu, um die MSBuild Community Task FileUpdate auszuführen zu ändern (ich habe A für Live, B für Test und C für Training verwendet).
  • Fügen Sie das AfterBuild-Ziel hinzu, um die Guids von Components.wxs auf 0 zurückzusetzen.
  • Stellen Sie mit Orca sicher, dass jede Komponente in jedem MSI die geänderte Richtlinie hat.
  • Stellen Sie sicher, dass die ursprünglichen Hilfslinien wiederhergestellt sind.
  • Stellen Sie sicher, dass jedes MSI das richtige Produkt und den richtigen Standort installiert (und aktualisiert).

Beispiel Config.wxi

<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Upgrade code should not change unless you want to install 
     a new product and have the old product remain installed, 
     that is, both products existing as separate instances. -->
<?define UpgradeCode = "YOUR-GUID-HERE" ?>

<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
  <!-- Product name as you want it to appear in Add/Remove Programs-->
  <?define ProductName = "Foo 64 Bit [Live]" ?>
<?else ?>
  <?define ProductName =  "Foo [Live]" ?>
<?endif ?>

<!-- Directory name used as default installation location -->
<?define InstallName = "Foo [Live]" ?>

<!-- Registry key name used to store installation location -->
<?define InstallNameKey = "FooLive" ?>

<?define VDirName = "FooLive" ?>
<?define AppPoolName = "FooLiveAppPool" ?>
<?define DbName = "BlahBlahLive" ?>
</Include>

Beispiel Config.Common.wxi

<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Auto-generate ProductCode for each build, release and upgrade -->
<?define ProductCode = "*" ?>

<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define ProductVersion = "1.0.0.0" ?>

<!-- Minimum version supported if product already installed and this is an upgrade -->
<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define MinimumUpgradeVersion = "0.0.0.0" ?>

<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
   <?define Win64 = "yes" ?>
   <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
   <?define Win64 = "no" ?>
   <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>

<?define ProductManufacturer = "Foo Technologies"?>

<!-- Decimal Language ID (LCID) for the Product. Used for localization. -->
<?define ProductLanguage = "1033" ?>

<?define WebSiteName = "DefaultWebSite" ?>
<?define WebSitePort = "80" ?>

<?define DbServer = "(local)" ?>
</Include>

Beispiel Components.wxs

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <!-- The pre-processor variable which allows the magic to happen :) -->
  <?include $(sys.CURRENTDIR)\Config.wxi?>
  <?include ..\Setup.Library\Config.Common.wxi?>
  <Fragment Id="ComponentsFragment">
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
          <Component Id="ProductComponent" Guid="0XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" KeyPath="yes">
          ...

Hinweis: Ich würde jetzt vorschlagen, das Guid-Attribut aus der Komponente herauszulassen (entspricht *), eine Datei pro Komponente zu verwenden und die Datei als Schlüsselpfad festzulegen. Dadurch entfallen die unten gezeigten Anrufe ModifyComponentsGuidsund RevertComponentsGuidsZiele. Dies ist jedoch möglicherweise nicht für alle Ihre Komponenten möglich.

Beispiel Setup.Live.wixproj

<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="BeforeBuild">
  <CallTarget Targets="ModifyComponentsGuids" />
</Target>
<Target Name="AfterBuild">
  <CallTarget Targets="RevertComponentsGuids" />
</Target>
<!-- Modify the first character of every Guid to create unique value for Live, Test and Training builds -->
<Target Name="ModifyComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;A" />
</Target>
<!-- Revert the first character of every Guid back to initial value -->
<Target Name="RevertComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;0" />
</Target>

Abschließende Gedanken

  • Dieser Prozess sollte auch zum Erstellen verschiedener Installationsprogramme für verschiedene Zusammenführungsmodule (Live, Test, ... als Funktionen) für dasselbe Installationsprogramm funktionieren. Ich habe mich für verschiedene Installationsprogramme entschieden, da dies eine sicherere Option war. Es besteht ein höheres Risiko, dass jemand Live anstelle von Training aktualisiert, wenn er sich auf derselben Box befindet und Sie nur Funktionen für die verschiedenen Zusammenführungsmodule verwenden.
  • Wenn Sie Ihre MSI verwenden, um Upgrades sowie Neuinstallationen durchzuführen, dh nur die Hauptaktualisierung, und Sie Ihren Installationsort in der Registrierung speichern, müssen Sie für jede Installation eine Variable für den Schlüsselnamen erstellen.
  • Außerdem erstellen wir in jeder Config.wxi Variablen, um eindeutige virtuelle Verzeichnisnamen, Anwendungspools, Datenbanknamen usw. für jedes Installationsprogramm zu aktivieren.

UPDATE 1: Automatisch generierte Komponenten-Guids machen das Aufrufen der FileUpdate-Task überflüssig, wenn Sie für jede Datei eine Komponente mit Guid = "*" erstellen und die Datei als Schlüsselpfad festlegen.

UPDATE 2: Eines der Probleme, auf das wir gestoßen sind, ist, dass die temporären Dateien manuell gelöscht werden müssen, wenn Sie die Komponenten-Guids nicht automatisch generieren und der Build fehlschlägt.

UPDATE 3: Es wurde eine Möglichkeit gefunden, die Abhängigkeit von svn: externals und die temporäre Dateierstellung zu beseitigen. Dies macht den Erstellungsprozess widerstandsfähiger (und ist die beste Option, wenn Sie Ihre Guids nicht mit einem Platzhalter versehen können) und weniger spröde, wenn ein Erstellungsfehler bei Licht oder Kerze auftritt.

UPDATE 4: Die Unterstützung für mehrere Instanzen mithilfe von Instanztransformationen ist in WiX 3.0+ enthalten und auf jeden Fall auch einen Blick wert.


+1 für die MSBuild Community Tasks Referenz, liebe dieses Paket
BozoJoe

17

Verwenden der Msi-Diagnoseprotokollierung, um detaillierte Fehlerinformationen abzurufen

msiexec /i Package.msi /l*v c:\Package.log

Wo

Package.msi
ist der Name Ihres Pakets und
c: \ Package.log
Hier möchten Sie die Ausgabe des Protokolls

Msi-Fehlercodes

Wix-Intro-Video
Oh und zufälliges Wix-Intro-Video mit "Mr. WiX" Rob Mensching ist "konzeptionell ein großes Bild" hilfreich.


2
+1 Es wäre viel besser, wenn wir die Protokollierung in Wix anstelle der Befehlszeile aktivieren könnten.
Si618

3
WiX macht. Legen Sie die MsiLogging-Eigenschaft fest. Wird nur von Windows Installer 4.0+ unterstützt.
Rob Mensching

Vielen Dank "Mr. Wix". Ich muss das überprüfen.
Terrance

17

Verwenden Sie Javascript CustomActions, weil sie so einfach sind

Die Leute haben gesagt, dass Javascript das Falsche für MSI CustomActions ist . Gründe angegeben: schwer zu debuggen, schwer zuverlässig zu machen. Ich stimme nicht zu Es ist nicht schwer zu debuggen, schon gar nicht schwerer als C ++. Es ist einfach anders. Ich fand das Schreiben von CustomActions in Javascript sehr einfach, viel einfacher als die Verwendung von C ++. Viel schneller. Und genauso zuverlässig.

Es gibt nur einen Nachteil: Javascript CustomActions können über Orca extrahiert werden, während eine C / C ++ - Zertifizierungsstelle ein Reverse Engineering erfordern würde. Wenn Sie die Magie Ihres Installationsprogramms als geschütztes geistiges Eigentum betrachten, sollten Sie Skripte vermeiden.

Wenn Sie ein Skript verwenden, müssen Sie nur mit einer Struktur beginnen. Hier sind einige, um Ihnen den Einstieg zu erleichtern.


Javascript "Boilerplate" Code für CustomAction:

//
// CustomActions.js 
// 
// Template for WIX Custom Actions written in Javascript.
// 
// 
// Mon, 23 Nov 2009  10:54
// 
// ===================================================================


// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = {
        OkOnly           : 0,
        OkCancel         : 1,
        AbortRetryIgnore : 2,
        YesNoCancel      : 3
};

var Icons = {
        Critical         : 16,
        Question         : 32,
        Exclamation      : 48,
        Information      : 64
};

var MsgKind = {
        Error            : 0x01000000,
        Warning          : 0x02000000,
        User             : 0x03000000,
        Log              : 0x04000000
};

// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = {
        None             : 0,
        Ok               : 1, // success
        Cancel           : 2,
        Abort            : 3,
        Retry            : 4, // aka suspend?
        Ignore           : 5  // skip remaining actions; this is not an error.
};


function MyCustomActionInJavascript_CA() {
    try {
        LogMessage("Hello from MyCustomActionInJavascript");
        // ...do work here...
        LogMessage("Goodbye from MyCustomActionInJavascript");
    }
    catch (exc1) {
        Session.Property("CA_EXCEPTION") = exc1.message ;
        LogException(exc1);
        return MsiActionStatus.Abort;
    }
    return MsiActionStatus.Ok;
}

// Pop a message box.  also spool a message into the MSI log, if it is enabled. 
function LogException(exc) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
    Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}


// spool an informational message into the MSI log, if it is enabled. 
function LogMessage(msg) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction:: " + msg;
    Session.Message(MsgKind.Log, record);
}


// http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
var WindowStyle = {
    Hidden : 0,
    Minimized : 1,
    Maximized : 2
};

// http://msdn.microsoft.com/en-us/library/314cz14s(v=VS.85).aspx
var OpenMode = {
    ForReading : 1,
    ForWriting : 2,
    ForAppending : 8
};

// http://msdn.microsoft.com/en-us/library/a72y2t1c(v=VS.85).aspx
var SpecialFolders = {
    WindowsFolder : 0, 
    SystemFolder :  1, 
    TemporaryFolder : 2
};

// Run a command via cmd.exe from within the MSI
function RunCmd(command)
{
    var wshell = new ActiveXObject("WScript.Shell");
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder);
    var tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName());

    LogMessage("shell.Run("+command+")");

    // use cmd.exe to redirect the output
    var rc = wshell.Run("%comspec% /c " + command + "> " + tmpFileName, WindowStyle.Hidden, true);
    LogMessage("shell.Run rc = "  + rc);

    // here, optionally parse the output of the command 
    if (parseOutput) {
        var textStream = fso.OpenTextFile(tmpFileName, OpenMode.ForReading);
        while (!textStream.AtEndOfStream) {
            var oneLine = textStream.ReadLine();
            var line = ParseOneLine(oneLine);
                ...
        }
        textStream.Close();
    }

    if (deleteOutput) {
        fso.DeleteFile(tmpFileName);
    }

    return {
        rc : rc,
        outputfile : (deleteOutput) ? null : tmpFileName
    };
}

Registrieren Sie dann die benutzerdefinierte Aktion wie folgt:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.MyCustomAction"
              BinaryKey="IisScript_CA"
              JScriptCall="MyCustomActionInJavascript_CA"
              Execute="immediate"
              Return="check" />
</Fragmemt>

Sie können natürlich beliebig viele Javascript-Funktionen für mehrere benutzerdefinierte Aktionen einfügen. Ein Beispiel: Ich habe Javascript verwendet, um eine WMI-Abfrage in IIS durchzuführen und eine Liste der vorhandenen Websites abzurufen, auf denen ein ISAPI-Filter installiert werden kann. Diese Liste wurde dann verwendet, um ein Listenfeld zu füllen, das später in der UI-Sequenz angezeigt wird. Alles sehr einfach.

Unter IIS7 gibt es keinen WMI-Anbieter für IIS, daher habe ich den shell.Run()Ansatz verwendet, um appcmd.exe aufzurufen, um die Arbeit auszuführen. Einfach.

Verwandte Frage: Über Javascript CustomActions


2
+1 Ich finde den DTF-Ansatz einfach einzurichten, aber Javascript könnte auch nützlich sein.
Si618

12

Peter Tate hat bereits gezeigt, wie Sie wiederverwendbare ComponentGroup-Definitionen in separaten Wix-Fragmenten definieren können. Einige zusätzliche Tricks dazu:

Verzeichnis-Aliasing

Die Komponentengruppenfragmente müssen nichts über Verzeichnisse wissen, die von den Hauptprodukt-wxs definiert werden. In Ihrem Komponentengruppenfragment können Sie über einen Ordner wie diesen sprechen:

<DirectoryRef Id="component1InstallFolder">
...
</DirectoryRef>

Dann kann das Hauptprodukt eines seiner Verzeichnisse (z. B. "productInstallFolder") wie folgt aliasisieren:

<Directory Id="productInstallFolder" Name="ProductName">
   <!-- not subfolders (because no Name attribute) but aliases for parent! -->
   <Directory Id="component1InstallFolder"/> 
   <Directory Id="component2InstallFolder"/> 
</Directory>

Abhängigkeitsdiagramm

ComponentGroup-Elemente können untergeordnete ComponentGroupRef-Elemente enthalten. Dies ist ideal, wenn Sie über einen großen Pool wiederverwendbarer Komponenten mit einem komplexen Abhängigkeitsdiagramm verfügen. Sie richten einfach eine ComponentGroup in einem eigenen Fragment für jede Komponente ein und deklarieren die Abhängigkeiten wie folgt:

<ComponentGroup Id="B">
   <ComponentRef Id="_B" />
   <ComponentGroupRef Id="A">
</ComponentGroup>

Wenn Sie jetzt in Ihrem Setup auf die Komponentengruppe "B" verweisen, da es sich um eine direkte Abhängigkeit Ihrer Anwendung handelt, wird automatisch die Komponentengruppe "A" abgerufen, auch wenn der Anwendungsautor nie bemerkt hat, dass es sich um eine Abhängigkeit von "B" handelt. Es "funktioniert einfach", solange Sie keine zirkulären Abhängigkeiten haben.

Wiederverwendbare Wixlib

Die obige Idee eines Abhängigkeitsdiagramms funktioniert am besten, wenn Sie die Big-Pool-o-Reusable-Komponenten mit lit.exe zu einer wiederverwendbaren Wixlib kompilieren. Wenn Sie ein Anwendungssetup erstellen, können Sie auf diese wixlib ähnlich wie auf eine wixobj-Datei verweisen. Der Candle.exe-Linker entfernt automatisch alle Fragmente, die nicht von den Hauptprodukt-WXS-Dateien "eingezogen" werden.


12

Ich bin überrascht, dass niemand die Verwendung von T4 zum Generieren der WXS-Datei während des Builds erwähnt hat. Ich habe dies durch Henry Lee @ New Age Solutions erfahren .

Im Wesentlichen erstellen Sie eine benutzerdefinierte MSBuild-Aufgabe, um eine T4-Vorlage auszuführen, und diese Vorlage gibt das WXS unmittelbar vor dem Kompilieren des Wix-Projekts aus. Auf diese Weise können Sie (abhängig davon, wie Sie es implementieren) automatisch alle Assemblys einbeziehen, die beim Kompilieren einer anderen Lösung ausgegeben werden (was bedeutet, dass Sie die wxs nicht mehr bearbeiten müssen, wenn Sie eine neue Assembly hinzufügen).


2
+1 das ist wirklich schön, ich mache mir nicht so viele Sorgen um Assemblys, aber unsere Webprojekte können Probleme mit Aspx-Seiten und anderen Artefakten (Bilder, CSS) haben, die dem Projekt hinzugefügt werden, aber nicht WiX.
Si618

4
Für zukünftige Besucher hat Wix 3.5 eine Utility heat.exe , die diese Ernte automatisch durchführt
Mrchief

@Mrchief - Ich glaube nicht, dass Heat referenzierte Assemblys aufnimmt, die lokal kopiert werden - dies ist jedoch anscheinend für 4.0 geplant. Referenz: sourceforge.net/tracker/…
Peter T. LaComb Jr.

Wärme nimmt keine referenzierten Baugruppen auf.
Tofutim

Was sind einige gute Beispiele für die Verwendung von T4 zum Generieren der WXS-Datei?
Tofutim

12

Verwenden Sie Heat.exe, um das Gesicht zu zerschlagen und schmerzhaft großen Installationen "Epic Pwnage" zuzufügen

Erweiterung der Antworten von Si und Robert-P über Hitze.

Übersetzung: (Verwenden von Wärme, um zu vermeiden, dass einzelne Dateien von Hand in das Projekt eingegeben werden, und zum Automatisieren von Builds, um den Prozess insgesamt zu vereinfachen.)

WiX 2.0 Heat Syntax detailliert

Für neuere Versionen (nicht allzu verschieden von älteren Versionen, aber es gibt möglicherweise ärgerliche Syntaxänderungen ...) gehen Sie in das Verzeichnis Heat in der cmd.exe und geben Sie einfach heat ein, aber ich habe hier ein Beispiel, um Hilfe zu erhalten bei Bedarf mit neueren Versionen.

Hinzufügen von Folgendem zu Ihrem Build-Ereignis in Visual Studio 2010.
(Klicken Sie mit der rechten Maustaste auf Projekt-> Eigenschaften -> Build-Ereignisse-> Pre-Build-Ereignisse.)

$(WIX)bin\heat.exe" dir "$(EnviromentVariable)" -cg GroupVariable -gg -scom -sreg -sfrag - srd -dr INSTALLLOCATION -var env.LogicPath -out "$(FragmentDir)\FileName.wxs

-gg 

Generiert Guids, wenn Wärme ausgeführt wird (wie bei der Ausführung des obigen Befehls).

-scom 

Schnappen Sie sich nicht "COM-Dateien"

-sreg 

Schnappen Sie sich nicht "Registrierungsdateien"

-sfrag 

Schnapp dir nicht "Fragmente"

-srd 

Schnapp dir nicht das "root Dir"

dir

dir gibt an, dass Heat in einem Ordner suchen soll

"$ (EnviromentVariable)"

Der Name der Variablen, die Sie zu den Präprozessorvariablen im Abschnitt Projekteigenschaften (Rechtsklick auf Eigenschaften, Gehe zu Eigenschaften) hinzufügen würden-> Erstellen, in dem Präprozessorvariablen definieren steht (vorausgesetzt, Visual Studio 2010)

Beispiel:
EnviromentVariable = C: \ Project \ bin \ Debug;
Keine doppelten Anführungszeichen, sondern mit einem Semikolon enden

-cg GroupVariable 

Die ComponentGroup, auf die aus dem erstellten Fragment in die Hauptdatei wxs verwiesen wird

FragmentDir

Das Fragmentverzeichnis, in dem das Ausgabe-WXS-Fragment gespeichert wird

FileName.wxs

Der Name der Datei

Vollständiges Tutorial hier, also verdammt hilfreich

Teil 1 Teil 2


Es gibt ein weiteres nützliches Tool für etwas andere Zwecke: Paraffin ( wintellect.com/CS/blogs/jrobbins/archive/2010/03/10/4107.aspx )
ralf.w.

9

Einschließlich COM-Objekte:

heatgeneriert die meisten (wenn nicht alle) Registrierungseinträge und andere für sie erforderliche Konfigurationen. Jubeln!

Einschließlich verwalteter COM-Objekte (auch bekannt als .NET- oder C # COM-Objekte)

Wenn Sie heatein verwaltetes COM-Objekt verwenden, erhalten Sie ein fast vollständiges Wix-Dokument.

Wenn Sie die im GAC verfügbare Bibliothek nicht benötigen (dh global verfügbar: Meistens benötigen Sie dies bei Ihren .NET-Assemblys sowieso nicht - Sie haben an dieser Stelle wahrscheinlich etwas falsch gemacht, wenn dies nicht beabsichtigt ist eine gemeinsam genutzte Bibliothek) Sie sollten sicherstellen, dass der CodeBaseRegistrierungsschlüssel, auf den eingestellt werden soll, aktualisiert wird [#ComponentName]. Wenn Sie vorhaben, es im GAC zu installieren (z. B. haben Sie eine neue großartige allgemeine Bibliothek erstellt, die jeder verwenden möchte), müssen Sie diesen Eintrag entfernen und dem FileElement zwei neue Attribute hinzufügen : Assemblyund KeyPath. Die Assembly sollte auf ".net" und KeyPathauf "yes" gesetzt sein.

Einige Umgebungen (insbesondere alle mit verwaltetem Speicher, z. B. Skriptsprachen) benötigen jedoch auch Zugriff auf die Typelib. Stellen Sie sicher, dass Sie heatauf Ihrem Typelib laufen und es einschließen. heatgeneriert alle erforderlichen Registrierungsschlüssel. Wie cool ist das?


8

Installation zu C:\ProductName

Einige Anwendungen müssen installiert werden C:\ProductNameoder ähnliches, aber 99,9% (wenn nicht 100%) der Beispiele im Netz werden installiertC:\Program Files\CompanyName\ProductName .

Der folgende Code kann verwendet werden, um die TARGETDIREigenschaft auf das Stammverzeichnis des C:Laufwerks festzulegen (aus der WiX-Benutzerliste entnommen ):

<CustomAction Id="AssignTargetDir" Property="TARGETDIR" Value="C:\" Execute="firstSequence" />
<InstallUISequence>
    <Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallUISequence>
<InstallExecuteSequence>
    <Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallExecuteSequence>

HINWEIS: Zeigt standardmäßig TARGETDIR nicht auf C:\! Es zeigt eher ROOTDRIVEauf die Wurzel des Laufwerks mit dem meisten freien Speicherplatz ( siehe hier ) - und dies ist nicht unbedingt dieC: Laufwerk. Möglicherweise befindet sich eine andere Festplatte, Partition oder ein anderes USB-Laufwerk!

Dann <Product ...>benötigen Sie irgendwo unter Ihrem Tag wie gewohnt die folgenden Verzeichnis-Tags:

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
        <!-- your content goes here... -->
    </Directory>
</Directory>

Wäre es nicht einfacher, nur zu installieren WindowsVolume?
Wim Coenen

1
Ja, aber Sie müssten eine Problemumgehung verwenden, da die WindowsVolumeEigenschaft möglicherweise nicht als Directory(Compiler gibt einen Fehler / eine Warnung aus) verwendet wird, wie hier und hier ausgeführt . Persönlich finde ich diese Problemumgehung verwirrend.
gehho

7

Umgebungsvariablen

Beim Kompilieren Ihrer Wxs-Dokumente zu Wixobj-Code können Sie Umgebungsvariablen verwenden, um verschiedene Informationen zu ermitteln. Angenommen, Sie möchten ändern, welche Dateien in ein Projekt aufgenommen werden. Nehmen wir an, Sie haben eine Umgebungsvariable namens RELEASE_MODE, die Sie direkt vor dem Erstellen Ihrer MSI festgelegt haben (entweder mit einem Skript oder manuell, es spielt keine Rolle). In Ihrer Wix-Quelle können Sie Folgendes tun:

<define FILESOURCE = c:\source\output\bin\$(env.RELEASE_MODE) >

und verwenden Sie es später in Ihrem Code, um Ihr wxs-Dokument im laufenden Betrieb zu ändern, z.

<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />

1
Auch Kompilierungsvariablen wie $ (Konfiguration) und $ (Plattform) sind verfügbar. Auch ein paar mehr unter msdn.microsoft.com/en-us/library/aa302186.aspx
si618

1
@Si - Irgendwann vor heute ist dieser Link nicht mehr aktiv. Ich konnte den neuesten nicht finden.
Peter M



7

Dialoge bearbeiten

Eine gute Möglichkeit zum Bearbeiten von Dialogen ist die Verwendung von SharpDevelop in einer Version 4.0.1.7090 (oder höher). Mit Hilfe dieses Tools kann ein eigenständiger Dialog (wxs-Dateien aus WiX-Quellen wie z. B. InstallDirDlg.wxs) in der Entwurfsansicht geöffnet, in der Vorschau angezeigt und bearbeitet werden.


Genial, wusste nicht, dass SharpDevelop dies unterstützt.
anton.burger

6

Setzen des IIS enable 32BitAppOnWin64-Flags http://trycatchfail.com/blog/post/WiX-Snippet-change-enable32BitAppOnWin64.aspx

<InstallExecuteSequence>
   <RemoveExistingProducts After="InstallFinalize" />
   <Custom Action="ConfigureAppPool" After="InstallFinalize" >
     <![CDATA[NOT Installed AND VersionNT64 >= 600]]>         
   </Custom>
</InstallExecuteSequence>

<CustomAction Id="ConfigureAppPool" Return="check" Directory="TARGETDIR" ExeCommand="[SystemFolder]inetsrv\appcmd set apppool /apppool.name:[APPPOOLNAME] /enable32BitAppOnWin64:false" />

5

Ändern Sie "Bereit zur Installation?" Dialogfeld (auch bekannt als VerifyReadyDlg), um eine Zusammenfassung der getroffenen Entscheidungen bereitzustellen.

Es sieht so aus:
Alternativtext http://i46.tinypic.com/s4th7t.jpg

Tun Sie dies mit einer Javascript CustomAction:


Javascript-Code:

// http://msdn.microsoft.com/en-us/library/aa372516(VS.85).aspx
var MsiViewModify = 
    {
        Refresh          : 0,
        Insert           : 1,
        Update           : 2,
        Assign           : 3,
        Replace          : 4,
        Merge            : 5,
        Delete           : 6,
        InsertTemporary  : 7,   // cannot permanently modify the MSI during install
        Validate         : 8,
        ValidateNew      : 9,
        ValidateField    : 10,
        ValidateDelete   : 11
    };


// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = 
    {
        OkOnly           : 0,
        OkCancel         : 1,
        AbortRetryIgnore : 2,
        YesNoCancel      : 3
    };

var Icons= 
    {
        Critical         : 16,
        Question         : 32,
        Exclamation      : 48,
        Information      : 64
    }

var MsgKind =
    {
        Error            : 0x01000000,
        Warning          : 0x02000000,
        User             : 0x03000000,
        Log              : 0x04000000
    };

// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = 
    {
        None             : 0,
        Ok               : 1, // success
        Cancel           : 2,
        Abort            : 3,
        Retry            : 4, // aka suspend?
        Ignore           : 5  // skip remaining actions; this is not an error.
    };

function UpdateReadyDialog_CA(sitename)
{
    try 
    {
        // can retrieve properties from the install session like this:
        var selectedWebSiteId = Session.Property("MSI_PROPERTY_HERE");

        // can retrieve requested feature install state like this:
        var fInstallRequested   = Session.FeatureRequestState("F.FeatureName");

        var text1 = "This is line 1 of text in the VerifyReadyDlg";

        var text2 = "This is the second line of custom text";

        var controlView     = Session.Database.OpenView("SELECT * FROM Control");
        controlView.Execute();

        var rec             = Session.Installer.CreateRecord(12);
        rec.StringData(1)   = "VerifyReadyDlg";    // Dialog_
        rec.StringData(2)   = "CustomVerifyText1"; // Control - can be any name
        rec.StringData(3)   = "Text";              // Type
        rec.IntegerData(4)  = 25;                  // X
        rec.IntegerData(5)  = 60;                  // Y
        rec.IntegerData(6)  = 320;                 // Width
        rec.IntegerData(7)  = 85;                  // Height
        rec.IntegerData(8)  = 2;                   // Attributes
        rec.StringData(9)   = "";                  // Property
        rec.StringData(10)  = vText1;              // Text
        rec.StringData(11)  = "";                  // Control_Next
        rec.StringData(12)  = "";                  // Help
        controlView.Modify(MsiViewModify.InsertTemporary, rec);

        rec                 = Session.Installer.CreateRecord(12);
        rec.StringData(1)   = "VerifyReadyDlg";    // Dialog_
        rec.StringData(2)   = "CustomVerifyText2"; // Control - any unique name
        rec.StringData(3)   = "Text";              // Type
        rec.IntegerData(4)  = 25;                  // X
        rec.IntegerData(5)  = 160;                 // Y
        rec.IntegerData(6)  = 320;                 // Width
        rec.IntegerData(7)  = 65;                  // Height
        rec.IntegerData(8)  = 2;                   // Attributes
        rec.StringData(9)   = "";                  // Property
        rec.StringData(10)  = text2;               // Text
        rec.StringData(11)  = "";                  // Control_Next
        rec.StringData(12)  = "";                  // Help
        controlView.Modify(MsiViewModify.InsertTemporary, rec);

        controlView.Close();
    }
    catch (exc1)
    {
        Session.Property("CA_EXCEPTION") = exc1.message ;
        LogException("UpdatePropsWithSelectedWebSite", exc1);
        return MsiActionStatus.Abort;
    }
    return MsiActionStatus.Ok;
}


function LogException(loc, exc)
{
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "Exception {" + loc + "}: " + exc.number + " : " + exc.message;
    Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}

Deklarieren Sie die Javascript-Zertifizierungsstelle:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.UpdateReadyDialog"
              BinaryKey="IisScript_CA"
              JScriptCall="UpdateReadyDialog_CA"
              Execute="immediate"
              Return="check" />
</Fragment>

Befestigen Sie die Zertifizierungsstelle an einer Schaltfläche. In diesem Beispiel wird die Zertifizierungsstelle ausgelöst, wenn in CustomizeDlg auf Weiter geklickt wird:

<UI ...>
  <Publish Dialog="CustomizeDlg" Control="Next" Event="DoAction" 
           Value="CA.UpdateReadyDialog" Order="1"/>
</UI>

Verwandte SO Frage: Wie kann ich zur Laufzeit den Text festlegen, der in VerifyReadyDlg angezeigt werden soll?


Dies sollte sicherlich nicht JScript, die Windows-Skriptsprache, sein, sondern JavaScript, die DHTML-Skriptsprache. Möglicherweise etwas pedantisch, könnte aber für manche Leute etwas verwirrend sein.
caveman_dick

5

Fügen Sie Komponenten, die einzeln gepatcht werden können, in ihre eigenen Fragmente ein

Sowohl für Produktinstallationsprogramme als auch für Patches gilt: Wenn Sie eine Komponente in ein Fragment aufnehmen, müssen Sie alle Komponenten in dieses Fragment aufnehmen. Wenn Sie beim Erstellen eines Installationsprogramms Komponentenreferenzen verpassen, wird von light.exe ein Verknüpfungsfehler angezeigt. Wenn Sie jedoch einen Patch erstellen und eine einzelne Komponentenreferenz in ein Fragment aufnehmen, dann alle geänderten Komponenten dieses Fragments in Ihrem Patch angezeigt.

so was:

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
            <File Id="SampleFile1" Source=".\$(var.Version)f\Sample1.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
           <File Id="SampleFile2" Source=".\$(var.Version)f\Sample2.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
           <File Id="SampleFile3" Source=".\$(var.Version)f\Sample3.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

an Stelle von:

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
            <File Id="SampleFile1" Source=".\$(var.Version)\Sample1.txt" />
        </Component>

        <Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
           <File Id="SampleFile2" Source=".\$(var.Version)\Sample2.txt" />
        </Component>

        <Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
           <File Id="SampleFile3" Source=".\$(var.Version)\Sample3.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

Wenn Sie mit dem Thema "Verwenden von reinem WiX" aus der Hilfedatei "WiX.chm" patchen, verwenden Sie dieses Verfahren, um den Patch zu generieren:

torch.exe -p -xi 1.0\product.wixpdb 1.1\product.wixpdb -out patch\diff.wixmst
candle.exe patch.wxs
light.exe patch.wixobj -out patch\patch.wixmsp
pyro.exe patch\patch.wixmsp -out patch\patch.msp -t RTM patch\diff.wixmst

Es reicht nicht aus, nur die Version 1.1 von product.wixpdb zu erstellen, bei der die Komponenten in separaten Fragmenten verwendet werden. Stellen Sie daher sicher, dass Sie Ihr Produkt vor dem Versand korrekt fragmentieren.


5

Drucken von EULA ab Wix3.0 und höher

1) Wenn Sie Ihren Wix-Quellcode kompilieren, muss die Datei light.exe in der Befehlszeile auf die Datei WixUIExtension.dll verweisen. Verwenden Sie dazu den Befehlszeilenschalter -ext.

2) Wenn beim Hinzufügen des Verweises auf die Datei WixUIExtension.dll Ihr Projekt nicht kompiliert werden kann, liegt dies höchstwahrscheinlich an Konflikten zwischen Dialog-IDs, dh Ihr Projekt verwendete dieselben IDs von Dialogen wie einige Standarddialoge in WixUIExtension.dll. Geben Sie Ihren Dialogen unterschiedliche IDs. Dies ist ein recht häufiges Problem.

3) Ihr Lizenzdialog muss über ein ScrollableText-Steuerelement mit der ID "LicenseText" verfügen. Wix sucht beim Drucken nach genau diesem Kontrollnamen.

<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="160" Sunken="yes" TabSkip="no">
    <Text SourceFile="License.rtf" />
</Control>

und einen PushButton, der auf die benutzerdefinierte Aktion verweist

<Control Type="PushButton" Id="PrintButton" Width="57" Height="17" X="19" Y="244" Text="Print">
    <Publish Event="DoAction" Value="PrintEula">1</Publish>
</Control>

4) Definieren Sie CustomAction mit der ID = "PrintEula" wie folgt:

<CustomAction Id="PrintEula" BinaryKey="WixUIWixca" DllEntry="PrintEula" Return="ignore" Execute="immediate" />

Hinweis: BinaryKey unterscheidet sich in Wix3.0 von Wix2.0 und muss genau "WixUIWixca" sein (Groß- und Kleinschreibung beachten).

Wenn der Benutzer die Taste drückt, wird ihm das Standarddialogfeld "Drucker auswählen" angezeigt, und er kann von dort aus drucken.


5
  • Wir zeigen die Produktversion irgendwo (winzig) im ersten Bildschirm der GUI an. Weil die Leute jedes Mal Fehler machen, wenn sie die richtige Version auswählen. (Und halten Sie uns Entwickler auf der Suche nach Ewigkeiten ..)

  • Wir haben TFSBuild so eingerichtet, dass auch Transformationen (.mst-Dateien) mit der Konfiguration für unsere verschiedenen Umgebungen generiert werden. (Wir kennen alle Umgebungen, in denen wir bereitstellen müssen).

Da der ursprüngliche Weblog-Beitrag von Grant Holliday nicht verfügbar ist, habe ich seinen Inhalt hier kopiert:


MSBuild-Aufgabe zum Generieren von MSI-Transformationsdateien aus XMLMarch 11 2008

In meinem vorherigen Beitrag habe ich beschrieben, wie Sie MSI-Transformationsdateien (* .mst) verwenden können, um umgebungsspezifische Konfigurationseinstellungen von einem generischen MSI-Paket zu trennen.

Obwohl dies ein gewisses Maß an Flexibilität in Ihrer Konfiguration bietet, gibt es zwei Nachteile von Transformationsdateien:

  1. Sie sind ein Binärformat
  2. Sie können eine Transformationsdatei nicht „bearbeiten“ oder „anzeigen“. Sie müssen es anwenden oder neu erstellen, um zu sehen, welche Änderungen es enthält.

Glücklicherweise können wir die Microsoft Windows Installer-Objektbibliothek (c: windowssystem32msi.dll) verwenden, um MSI-Datenbanken zu öffnen und Transformationsdateien zu erstellen.

Die Credits gehen wieder an Alex Shevchuk - Von MSI zu WiX - Teil 7 - Anpassen der Installation mithilfe von Transformationen, um zu zeigen, wie dies mit VbScript erreicht werden kann. Im Wesentlichen habe ich nur Alex 'Beispiel genommen und mit Interop.WindowsInstaller.dll eine MSBuild-Aufgabe implementiert. Die MSBuild-Aufgabe

Laden Sie hier den Quellcode und die Beispieltransformation transforms.xml herunter (~ 7Kb Zipped VS2008 Solution)



2
Wir definieren WelcomeDlgTitle in meiner Lokalisierungsdatei neu - funktioniert hervorragend! <String Id = "WelcomeDlgTitle"> {\ WixUI_Font_Bigger} Willkommen beim Setup-Assistenten [ProductName] [ProductVersion] </ String>
saschabeaumont

5

Vor dem Bereitstellen eines Installationspakets kontrolliere ich immer den Inhalt.

Es ist nur ein einfacher Aufruf an der Befehlszeile (laut Terrences-Post), die Befehlszeile zu öffnen und einzugeben

msiexec /a Package.msi /qb TARGETDIR="%CD%\Extract" /l*vx "%CD\install.log%"

Dadurch wird der Paketinhalt in ein Unterverzeichnis 'Extract' mit dem aktuellen Pfad extrahiert.


4

Verwenden Sie anstelle von ORCA InstEd , ein gutes Tool zum Anzeigen von MSI-Tabellen. Es hat auch die Möglichkeit, zwei Pakete durch Transformieren -> Vergleichen mit ... zu unterscheiden.

Zusätzlich ist eine Plus-Version mit zusätzlichen Funktionen verfügbar. Aber auch die kostenlose Version bietet eine gute Alternative für Orca.


4

Registrieren von .NET-Assemblys für COM Interop mit x86 / x64-Kompatibilität

NB Dieses Fragment ist im Wesentlichen dasselbe wie REGASM Assembly.dll / codebase

In diesem Beispiel sind einige Dinge im Gange. Hier ist der Code und ich werde ihn anschließend erklären ...

  <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <?include $(sys.CURRENTDIR)\Config.wxi?>
  <?if $(var.Win64) ?>
  <?define CLSIDRoots = "CLSID;Wow6432Node\CLSID"?>
  <?else ?>
  <?define CLSIDRoots = "CLSID"?>
  <?endif?>
  <!-- ASCOM Driver Assembly with related COM registrations -->
  <Fragment>
    <DirectoryRef Id="INSTALLLOCATION" />
  </Fragment>
  <Fragment>
    <ComponentGroup Id="cgAscomDriver">
      <Component Id="cmpAscomDriver" Directory="INSTALLLOCATION" Guid="{0267031F-991D-4D88-A748-00EC6604171E}">
        <File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly"  />
        <RegistryKey Root="HKCR" Key="$(var.DriverId)"  Action="createAndRemoveOnUninstall">
          <RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
          <RegistryKey Key="CLSID">
            <RegistryValue Type="string" Value="$(var.DriverGuid)" />
          </RegistryKey>
        </RegistryKey>
        <?foreach CLSID in $(var.CLSIDRoots) ?>
        <RegistryKey Root="HKCR" Key="$(var.CLSID)" Action="none">
          <RegistryKey Key="$(var.DriverGuid)" Action="createAndRemoveOnUninstall">
            <RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
            <RegistryKey Key="InprocServer32">
              <RegistryValue Type="string" Value="mscoree.dll" />
              <RegistryValue Type="string" Name="ThreadingModel" Value="Both"/>
              <RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
              <RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
              <RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
              <RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
              <RegistryKey Key="!(bind.fileVersion.filDriverAssembly)" >
                <RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
                <RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
                <RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
                <RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
              </RegistryKey>
            </RegistryKey>
            <RegistryKey Key="ProgId" Action="createAndRemoveOnUninstall">
              <RegistryValue Type="string" Value="$(var.DriverId)" />
            </RegistryKey>
            <RegistryKey Key="Implemented Categories" Action="createAndRemoveOnUninstall" >
              <RegistryKey Key="{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Action="createAndRemoveOnUninstall" />
            </RegistryKey>
          </RegistryKey>
        </RegistryKey>
        <?endforeach?>
      </Component>
    </ComponentGroup>
  </Fragment>
</Wix>

Wenn Sie sich gefragt haben, ist dies tatsächlich für einen ASCOM-Teleskoptreiber .

Zuerst habe ich mich von oben beraten lassen und einige Plattformvariablen in einer separaten Datei erstellt. Sie können diese im XML verstreut sehen.

Der Wenn-Dann-Sonst-Teil oben befasst sich mit der Kompatibilität von x86 und x64. Meine Assembly zielt auf "Beliebige CPU" ab. Auf einem x64-System muss ich sie also zweimal registrieren, einmal in der 64-Bit-Registrierung und einmal in den 32-Bit- Wow6432NodeBereichen. Das Wenn-Dann-Sonst bereitet mich darauf vor, die Werte werden foreachspäter in einer Schleife verwendet. Auf diese Weise muss ich die Registrierungsschlüssel nur einmal erstellen (DRY-Prinzip).

Das Dateielement gibt die tatsächliche Assembly-DLL an, die installiert und registriert wird:

<File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly"  />

Nichts Revolutionäres, aber beachten Sie das Assembly=".net"- dieses Attribut allein würde dazu führen, dass die Versammlung in den GAC aufgenommen wird, was NICHT das ist, was ich wollte. Die Verwendung des AssemblyApplicationAttributs, um auf sich selbst zurückzuweisen, ist einfach eine Möglichkeit, Wix daran zu hindern, die Datei in den GAC zu stellen. Jetzt, da Wix weiß, dass es sich um eine .net-Assembly handelt, kann ich bestimmte Binder-Variablen in meinem XML verwenden, z. B. die !(bind.assemblyFullname.filDriverAssembly), um den vollständigen Namen der Assembly abzurufen.


3

Stellen Sie die DISABLEADVTSHORTCUTSEigenschaft so ein, dass alle in Ihrem Installationsprogramm angekündigten Verknüpfungen zu regulären Verknüpfungen werden, und Sie müssen keinen Dummy-Registrierungsschlüssel angeben, der als Schlüsselpfad verwendet werden soll.

<Property Id="DISABLEADVTSHORTCUTS" Value="1"/>

Ich denke, Windows Installer 4.0 oder höher ist eine Voraussetzung .


2

Es ist eine schöne Struktur, aber aufgrund meiner Erfahrung frage ich mich, wie Sie diese Bedingungen angehen:

A. Ihre Installationen scheinen alle am selben Ziel zu landen. Wenn ein Benutzer alle 3 Versionen gleichzeitig installieren muss, lässt Ihr Prozess dies zu. Können sie eindeutig erkennen, welche Version jeder ausführbaren Datei sie auslösen?

B. Wie gehen Sie mit neuen Dateien um, die in TEST und / oder TRAINING vorhanden sind, aber noch nicht in LIVE?


Hallo Blaine, A. Nein, tun sie nicht. InstallName befindet sich in Config.wxi. Dies ist die einzige Datei, auf die svn: externals nicht verweist. Dies ist also für jede Installation, dh für jedes Produkt, einzigartig. Aus diesem Grund ändern wir auch die Guids für jede Version. B. GOTO A. :) Sie sind separate MSIs mit ihrem eigenen UpgradeCode.
Si618

1
Ich verstehe übrigens, warum Sie meine Frage mit einer Frage beantwortet haben, aber sobald Sie genügend Wiederholungspunkte erhalten haben, verschieben Sie Ihre Frage bitte in die Antwortkommentare, da sonst der Thread schwer zu verfolgen ist.
Si618

2

Hier ist Weise können große Webprojekte überprüfen, ob die Anzahl der bereitgestellten Dateien mit der Anzahl der in eine MSI (oder ein Zusammenführungsmodul) integrierten Dateien übereinstimmt. Ich habe gerade die benutzerdefinierte MSBuild-Aufgabe für unsere Hauptanwendung ausgeführt (die sich noch in der Entwicklung befindet) und sie hat einige fehlende Dateien, hauptsächlich Bilder, gefunden, aber einige Javascript-Dateien waren durchgegangen!

Dieser Ansatz (Blick in die Dateitabelle von MSI durch Einbinden in das AfterBuild-Ziel des WiX-Projekts) kann für andere Anwendungstypen funktionieren, bei denen Sie Zugriff auf eine vollständige Liste der erwarteten Dateien haben.


2

Durchführen einer erzwungenen Neuinstallation, wenn eine Installation keine Deinstallation oder Neuinstallation zulässt und kein Rollback durchführt.

VBscript-Skript zum Überschreiben einer Installation, die aus irgendeinem Grund nicht deinstalliert wird.

Dim objShell
set objShell = wscript.createObject("wscript.shell")

iReturn = objShell.Run("CMD /K MsiExec.exe /I ""C:\Users\TheUser\Documents\Visual Studio 2010\Projects\InstallationTarget\HelloInstaller\bin\Debug\HelloInstaller.msi"" REINSTALLMODE=vomus REINSTALL=ALL",,True)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.