Trucchi e suggerimenti WiX


264

Usiamo WiX da un po 'di tempo e, nonostante le solite lamentele sulla facilità d'uso, sta andando abbastanza bene. Quello che sto cercando è un consiglio utile per quanto riguarda:

  • Impostazione di un progetto WiX (layout, riferimenti, modelli di file)
  • Integrazione di WiX in soluzioni e processi di creazione / rilascio
  • Configurazione dei programmi di installazione per nuove installazioni e aggiornamenti
  • Eventuali buoni hack WiX che vorresti condividere

dai un'occhiata a gui4wix.codeplex.com
TarunG,

10
Chiuso come non costruttivo? Ho imparato un sacco da questa domanda! Un po 'di coerenza da StackOverflow sarebbe anche bello ... ad esempio stackoverflow.com/questions/550632/...
si618

15
Ha ottenuto '203' Ups, è abbastanza per dimostrare la sua utilità.
TarunG,

Quindi le domande devono avere una risposta definitiva e corretta; le domande a risposta aperta fanno in modo che le domande poste dalle persone sui problemi reali scompaiano dalla prima pagina. faq @Si .: Questa politica è sempre stata lì, AFAIK, ma ora è meglio applicarla; quella domanda ha quasi tre anni.
Jim Dagg,

Abbastanza giusto Jim, è una domanda a risposta aperta, e credo che spetti alla comunità SO decidere, ma devo dire che chiuderlo come non costruttivo sembra strano, dato che io e, a quanto pare, molte altre persone ho trovato utile questa domanda (ad es. goo.gl/Zqp2X ) e che si adatta molto bene alla practical, answerable questions based on actual problems that you faceparte delle FAQ.
si618,

Risposte:


157
  1. Conserva le variabili in un wxifile include separato . Consente il riutilizzo, le variabili sono più veloci da trovare e (se necessario) consente una più facile manipolazione da parte di uno strumento esterno.

  2. Definire le variabili della piattaforma per build x86 e x64

    <!-- 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. Memorizzare la posizione di installazione nel registro, consentendo agli aggiornamenti di trovare la posizione corretta. Ad esempio, se un utente imposta una directory di installazione personalizzata.

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

    Nota : guru WiX Rob Mensching ha pubblicato un eccellente post nel blog che approfondisce e corregge un caso limite quando le proprietà sono impostate dalla riga di comando.

    Esempi usando 1. 2. e 3.

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

    e

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
  4. L'approccio più semplice prevede sempre importanti aggiornamenti , poiché consente sia nuove installazioni che aggiornamenti nel singolo MSI.UpgradeCode è fissato su un unico Guid e non cambierà mai, a meno che non desideriamo aggiornare il prodotto esistente.

    Nota : in WiX 3.5 è presente un nuovo elemento MajorUpgrade che semplifica ulteriormente la vita !

  5. Creazione di un'icona in Installazione applicazioni

    <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. Al momento del rilascio delle versioni, eseguiamo la versione dei nostri programmi di installazione, copiando il file msi in una directory di distribuzione. Un esempio di ciò che utilizza una destinazione wixproj chiamata dalla destinazione AfterBuild:

    <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. Usa il calore per raccogliere i file con il jolly (*) Guid. Utile se si desidera riutilizzare i file WXS su più progetti (vedere la mia risposta su più versioni dello stesso prodotto). Ad esempio, questo file batch raccoglie automaticamente l'output di RoboHelp.

    @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 

    Sta succedendo un po ', robocopysta rimuovendo i metadati della copia di lavoro di Subversion prima della raccolta; il -drriferimento alla directory principale è impostato sulla nostra posizione di installazione anziché su TARGETDIR predefinito; -varviene utilizzato per creare una variabile per specificare la directory di origine (output di distribuzione Web).

  8. Modo semplice per includere la versione del prodotto nel titolo della finestra di dialogo di benvenuto utilizzando Strings.wxl per la localizzazione. (Credito: saschabeaumont . Aggiunto come questo grande suggerimento è nascosto in un commento)

    <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. Risparmiati un po 'di dolore e segui i consigli di Wim Coehen su un componente per file. Ciò consente anche di escludere (o jolly *) il GUID del componente .

  10. Rob Mensching ha un modo preciso per rintracciare rapidamente i problemi nei file di registro MSI cercando value 3. Nota i commenti riguardanti l'internazionalizzazione.

  11. Quando si aggiungono funzionalità condizionali, è più intuitivo impostare il livello di funzionalità predefinito su 0 (disabilitato) e quindi impostare il livello di condizione sul valore desiderato. Se imposti il ​​livello di funzionalità predefinito> = 1, il livello della condizione deve essere 0 per disabilitarlo, il che significa che la logica della condizione deve essere l'opposto di quello che ti aspetteresti, il che può essere fonte di confusione :)

    <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>

Informazioni sull'aggiunta dell'icona in Installazione applicazioni, ESATTAMENTE quello che stavo cercando. Dove attacchi queste tre righe? +1 per la pura bellezza.
Everett,

Tendo a posizionarli subito dopo (e ovviamente sotto) l'elemento <Package>. Dai
si618

+1, vorrei poter fare +100, questa è la singola informazione più utile su Wix su cui mi sono imbattuto.
Tim Long

Grazie Tim! Rob Mensching, Bob Arson, Wim Coehen e altri meritano i complimenti per aver condiviso le loro conoscenze.
si618,

38

Verifica se IIS è installato:

<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>

Verifica se la compatibilità della metabase di IIS 6 è installata su Vista +:

<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

Conserva tutti gli ID in spazi dei nomi separati

  • Le funzionalità iniziano con F. esempi: Documentazione F., F.Binaries, F.SampleCode.
  • I componenti iniziano con C. Ex: C.ChmFile, C.ReleaseNotes, C.LicenseFile, C.IniFile, C.Registry
  • Le azioni personalizzate sono CA. Ex: CA.LaunchHelp, CA.UpdateReadyDlg, CA.SetPropertyX
  • I file sono Fi.
  • Le directory sono Di.
  • e così via.

Trovo che questo aiuti immensamente a tenere traccia di tutti i vari ID in tutte le varie categorie.


Non uso gli spazi dei nomi ma aggiungo gli ID; ad esempio: EsempiFunzione, ChmFileComponent. Immagino che adoro scrivere ;-)
dvdvorle

25

Domanda fantastica. Mi piacerebbe vedere alcune buone pratiche mostrate.

Ho molti file che distribuisco, quindi ho impostato il mio progetto in diversi file sorgente di wxs.

Ho un file sorgente di livello superiore che chiamo Product.wxs che sostanzialmente contiene la struttura per l'installazione, ma non i componenti effettivi. Questo file ha diverse sezioni:

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

Il resto dei file .wix sono composti da frammenti che contengono ComponentGroups a cui si fa riferimento nel tag Feature in Product.wxs. Il mio progetto contiene un bel raggruppamento logico dei file che distribuisco

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

Questo non è perfetto, il mio senso del ragno OO formicola un po 'perché i frammenti devono fare riferimento ai nomi nel file Product.wxs (ad esempio DirectoryRef) ma trovo più facile mantenere quel singolo file di origine di grandi dimensioni.

Mi piacerebbe sentire commenti su questo, o se qualcuno ha anche dei buoni consigli!


Anche la nostra configurazione è molto simile a questo approccio. È buono perché possiamo usare il nostro equivalente di Products.wxs come configurazione di base per una varietà di prodotti.
si618

@Peter Tate: il tuo senso del ragno è corretto. Vedi la mia risposta sull'aliasing delle directory.
Wim Coenen,

Prendo lo stesso approccio: Product.wxs con layout è statico e un'attività di generazione (heat.exe) genera il mio file Content.wxs
timvw

20

Aggiungi una casella di controllo alla finestra di dialogo di uscita per avviare l'app o il file della guida.

...

<!-- 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>

Se lo fai in questo modo, l'aspetto "standard" non è del tutto corretto. La casella di controllo è sempre uno sfondo grigio, mentre la finestra di dialogo è bianca:

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

Un modo per aggirare questo è specificare il proprio ExitDialog personalizzato, con una casella di controllo in una posizione diversa . Funziona, ma sembra un sacco di lavoro solo per cambiare il colore di un controllo. Un altro modo per risolvere la stessa cosa è post-elaborazione dell'MSI generato per modificare i campi X, Y nella tabella Controllo per quel particolare controllo CheckBox. Il codice javascript è simile al seguente:

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();

L'esecuzione di questo codice come script da riga di comando (utilizzando cscript.exe) dopo la generazione dell'MSI (da light.exe) produrrà un ExitDialog che sembra più professionale:

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


Ha! Non il mio blog. L'ho letto anch'io. E ho un link alla voce del blog nel testo sopra. Ma l'hanno fatto diversamente da me. Mi piace la mia strada migliore. !!
Cheeso,

1
Grazie per il js, molto utile! Una cosa che ho dovuto cambiare nel wxs è sostituire WIXUI_EXITDIALOGOPTIONALCHECKBOXcon WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installeddentro<Publish>
Alexander Kojevnikov

C'è un modo per rendere la casella di controllo selezionata per impostazione predefinita?
Alek Davis,

Per selezionare la casella di default, ho usato questo: <Property Id = "WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value = "1" />
Alek Davis

Sembra una soluzione elegante, ma come la uso? C'è un modo per mettere js all'interno dell'elemento <AfterBuild> nel mio wixproj? O dal momento che ti riferisci a eseguirlo dalla riga di comando, è meglio come un evento post-build, nel qual caso, qual è un buon interprete della riga di comando js per Windows?
Vanmelle,

18

Creazione di versioni Live, Test, Training, ... usando gli stessi file sorgente.

In poche parole: crea un UpgradeCode univoco per ciascun installatore e definisci automagicamente il primo carattere di ogni Guid per ciascun installatore, lasciando i restanti 31 unici.

Prerequisiti

ipotesi

  • Le variabili WiX vengono utilizzate per definire UpgradeCode, ProductName, InstallName.
  • Hai già un programma di installazione funzionante. Non ci proverei fino a quando non lo farai.
  • Tutti i componenti sono conservati in un unico file (Components.wxs). Questo processo funzionerà se hai più file, ci sarà solo più lavoro da fare.

Struttura delle directory

  • Setup.Library
    • Tutti i file wxs (componenti, funzionalità, finestre di dialogo dell'interfaccia utente, ...)
    • Common.Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder, ...)
  • Setup.Live (wixproj)
    • Collega tutti i file Setup.Library usando "Aggiungi file esistente" -> "Aggiungi come collegamento" (il piccolo pulsante freccia giù proprio accanto al pulsante Aggiungi in Visual Studio)
    • Config.wxi (ha un unico UpgradeCode, ProductName, InstallName, ...)
  • Setup.Test , ...
    • come da live ma Config.wxi è configurato per l'ambiente di test.

Processi

  • Creare la directory Setup.Library e spostare tutti i file wxs e wxi (tranne Config.wxi) dal progetto esistente.
  • Crea Setup.Live, Setup.Test, ecc. Come da normale wixproj.
  • Aggiungi la destinazione BeforeBuild in wixproj in Setup.Live, ecc. Per eseguire FileUpdate della community di MSBuild per modificare le guide (ho usato A per Live, B per Test e C per allenamento)
  • Aggiungi destinazione AfterBuild per ripristinare le Guide di Components.wxs su 0.
  • Verificare con Orca che ogni componente di ciascun MSI abbia la guida modificata.
  • Verificare che le guide originali siano state ripristinate.
  • Verificare che ciascun MSI stia installando (e aggiornando) il prodotto e la posizione corretti.

Esempio 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>

Esempio 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>

Esempio 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">
          ...

Nota: ora suggerirei di lasciare l'attributo Guid fuori dal componente (equivalente di *), usando un file per componente e impostando il file come chiave di volta. Ciò elimina la necessità di chiamare ModifyComponentsGuidse RevertComponentsGuidstarget mostrati di seguito. Tuttavia, ciò potrebbe non essere possibile per tutti i componenti.

Esempio 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>

Pensieri finali

  • Questo processo dovrebbe funzionare anche per la creazione di programmi di installazione diversi per moduli di unione diversi (Live, Test, ... come funzionalità) per lo stesso programma di installazione. Sono andato con diversi installatori in quanto sembrava un'opzione più sicura, c'è più rischio che qualcuno possa aggiornare Live invece di Training se si trovano sulla stessa scatola e si usano solo le funzionalità per i diversi moduli di unione.
  • Se si utilizza MSI per eseguire aggiornamenti e nuove installazioni, ad esempio l'approccio di solo aggiornamento principale, e si salva la posizione di installazione nel registro, ricordare di creare una variabile per il nome della chiave per ogni installazione.
  • Creiamo anche variabili in ogni Config.wxi per abilitare nomi di directory virtuali, pool di applicazioni, nomi di database, ecc. Univoci per ciascun installatore.

AGGIORNAMENTO 1: Le guide del componente di generazione automatica rimuovono la necessità di chiamare l'attività FileUpdate se si crea un componente con Guid = "*" per ogni file, impostando il file come chiave di volta.

AGGIORNAMENTO 2: Uno dei problemi che abbiamo riscontrato è se non si genera automaticamente la guida del componente e la compilazione non riesce, quindi i file temporanei devono essere eliminati manualmente.

AGGIORNAMENTO 3: trovato un modo per rimuovere la dipendenza da svn: externals e creazione temporanea di file. Ciò rende il processo di compilazione più resiliente (ed è l'opzione migliore se non è possibile eseguire il jolly delle guide) e meno fragile se si verifica un errore di compilazione in luce o candela.

AGGIORNAMENTO 4: Il supporto per più istanze che utilizzano trasformazioni di istanza è in WiX 3.0+, sicuramente vale anche la pena dare un'occhiata.


+1 per il riferimento alle attività della community di MSBuild, adoro quel pacchetto
BozoJoe,

17

Utilizzo della registrazione diagnostica Msi per ottenere informazioni dettagliate sugli errori

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

Dove

Package.msi
è il nome del pacchetto e
c: \ Package.log
è dove vuoi l'output del registro

Codici di errore Msi

Video
introduttivo su Wix Oh e video introduttivo casuale su Wix con "Mr. WiX" Rob Mensching è utile "quadro generale concettuale".


2
+1 Sarebbe molto meglio se potessimo abilitare la registrazione da Wix anziché dalla riga di comando.
si618

3
WiX lo fa. Impostare la proprietà MsiLogging. Supportato solo da Windows Installer 4.0+.
Rob Mensching,

Grazie mille "Mr. Wix". Devo dare un'occhiata.
Terrance,

17

Usa Javascript CustomActions perché sono davvero facili

La gente ha detto che Javascript è la cosa sbagliata da usare per MSI CustomActions . Motivi indicati: difficile da eseguire il debug, difficile da renderlo affidabile. Non sono d'accordo Non è difficile eseguire il debug, certamente non più difficile del C ++. È solo diverso. Ho trovato la scrittura di CustomActions in Javascript molto semplice, molto più semplice dell'uso del C ++. Più veloce. E altrettanto affidabile.

C'è solo un inconveniente: Javascript CustomActions può essere estratto tramite Orca, mentre una CA C / C ++ richiederebbe il reverse engineering. Se consideri il tuo programma di installazione magico come proprietà intellettuale protetta, vorrai evitare gli script.

Se usi lo script, devi solo iniziare con una struttura. Ecco alcuni per iniziare.


Codice Javascript "boilerplate" per 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
    };
}

Quindi, registra l'azione personalizzata con qualcosa del genere:

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

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

Ovviamente puoi inserire tutte le funzioni Javascript che desideri per più azioni personalizzate. Un esempio: ho usato Javascript per eseguire una query WMI su IIS, per ottenere un elenco di siti Web esistenti in cui è possibile installare un filtro ISAPI. Questo elenco è stato quindi utilizzato per popolare una casella di riepilogo mostrata più avanti nella sequenza dell'interfaccia utente. Tutto molto semplice

Su IIS7, non esiste un provider WMI per IIS, quindi ho usato l' shell.Run()approccio per invocare appcmd.exe per eseguire il lavoro. Facile.

Domanda correlata: Informazioni su JavaScript CustomActions


2
+1 Trovo che l'approccio DTF sia facile da configurare, ma anche javascript potrebbe essere utile.
si618,

12

Peter Tate ha già mostrato come definire definizioni riutilizzabili di ComponentGroup in frammenti wix separati. Alcuni trucchi aggiuntivi relativi a questo:

Aliasing delle directory

I frammenti del gruppo di componenti non devono conoscere le directory definite dai principali prodotti wx. Nel frammento del tuo gruppo di componenti puoi parlare di una cartella come questa:

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

Quindi il prodotto principale può alias una delle sue directory (ad esempio "productInstallFolder") in questo modo:

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

Grafico delle dipendenze

Gli elementi ComponentGroup possono contenere elementi figlio ComponentGroupRef. Questo è fantastico se hai un grande pool di componenti riutilizzabili con un grafico di dipendenza complesso tra di loro. Basta impostare un ComponentGroup nel suo frammento per ciascun componente e dichiarare le dipendenze in questo modo:

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

Se ora fai riferimento al gruppo di componenti "B" nella tua configurazione perché è una dipendenza diretta della tua applicazione, attirerà automaticamente il gruppo di componenti "A" anche se l'autore dell'applicazione non ha mai capito che si trattava di una dipendenza di "B". "Funziona" solo se non si hanno dipendenze circolari.

Wixlib riutilizzabile

L'idea del grafico delle dipendenze sopra funziona meglio se compili i componenti big-pool-o-riutilizzabili in un wixlib riutilizzabile con lit.exe. Quando si crea un'impostazione dell'applicazione, è possibile fare riferimento a questo wixlib in modo molto simile a un file wixobj. Il linker candle.exe eliminerà automaticamente tutti i frammenti che non vengono "estratti" dai file wxs del prodotto principale.


12

Sono sorpreso che nessuno abbia menzionato l'uso di T4 per generare il file WXS durante la compilazione. Ho imparato questo grazie a Henry Lee @ New Age Solutions .

In sostanza, si crea un'attività MSBuild personalizzata per eseguire un modello T4 e quel modello genera WXS appena prima che il progetto Wix sia compilato. Ciò consente di (a seconda della modalità di implementazione) includere automaticamente tutti gli output degli assembly dalla compilazione di un'altra soluzione (il che significa che non è più necessario modificare i wx ogni volta che si aggiunge un nuovo assembly).


2
+1 è davvero bello, non sono così preoccupato per gli assemblaggi, ma i nostri progetti web possono avere problemi con le pagine aspx e altri artefatti (immagini, css) che vengono aggiunti al progetto ma non WiX.
si618,

4
Per i futuri visitatori, Wix 3.5 ha un'utility heat.exe che esegue automaticamente questa raccolta
Mrchief,

@Mrchief - Non credo che Heat raccolga assiemi referenziati che vengono copiati in locale - questo sembra però pianificato per 4.0. Riferimento: sourceforge.net/tracker/…
Peter T. LaComb Jr.

Il calore non raccoglie gli assiemi di riferimento.
tofutim,

Quali sono alcuni buoni esempi dell'utilizzo di T4 per generare il file WXS?
tofutim,

12

Utilizzo di Heat.exe per distruggere il viso e infliggere "Epic Pwnage" su installazioni dolorosamente grandi

Espandendo le risposte di Si e Robert-P sul calore.

Traduzione: (Uso del calore per evitare di digitare manualmente singoli file nel progetto e per automatizzare le build per un processo complessivamente più semplice.)

Sintassi del calore WiX 2.0 dettagliata

Per le versioni più recenti (non tutte così diverse dalle versioni precedenti ma ci sono modifiche alla sintassi potenzialmente fastidiose ....) vai alla directory Heat is in from the cmd.exe e basta digitare heat ma ne ho un esempio proprio qui per aiuto con versioni più recenti, se necessario.

Aggiungendo quanto segue all'evento Build in Visual Studio 2010.
(Fare clic con il pulsante destro del mouse su Progetto-> Proprietà -> Crea eventi-> Eventi pre-build)

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

-Gg 

Genera guide quando viene eseguito il calore (come in quando si esegue il comando sopra)

-scom 

Non afferrare "file COM"

-sreg 

Non prendere "File di registro"

-sfrag 

Non afferrare "Frammenti"

-srd 

Non afferrare il "Dir Dir"

dir

dir indica che si desidera che Heat venga visualizzato in una cartella

"$ (EnviromentVariable)"

Il nome della variabile da aggiungere alle variabili del preprocessore nelle proprietà del progetto (Progetto clic destro, Vai a proprietà )-> Costruisci dove si dice Definisci variabili preprocessore (assume Visual Studio 2010)

Esempio:
EnviromentVariable = C: \ Project \ bin \ debug;
Non ci sono virgolette doppie ma finiscono con un punto e virgola

-cg GroupVariable 

ComponentGroup a cui verrà fatto riferimento dal frammento creato nel file wxs principale

FragmentDir

La directory del frammento in cui verrà memorizzato il frammento di output wxs

FileName.wxs

Il nome del file

Tutorial completo qui, quindi molto utile

Parte 1 Parte 2


Esiste un altro strumento utile per scopi leggermente diversi: Paraffin ( wintellect.com/CS/blogs/jrobbins/archive/2010/03/10/4107.aspx )
ralf.w.

9

Compresi oggetti COM:

heatgenera la maggior parte (se non tutte) le voci di registro e altre configurazioni necessarie per esse. Rallegrarsi!

Compresi gli oggetti COM gestiti (ovvero oggetti COM, .NET o C #)

L'utilizzo heatsu un oggetto COM gestito ti darà un documento wix quasi completo.

Se non hai bisogno della libreria disponibile nel GAC (vale a dire, disponibile a livello globale: la maggior parte delle volte non ti serve comunque con i tuoi assiemi .NET - probabilmente hai fatto qualcosa di sbagliato a questo punto se non è destinato a essere una libreria condivisa) ti consigliamo di aggiornare la CodeBasechiave di registro su cui impostare [#ComponentName]. Se stai pensando di installarlo sul GAC (ad esempio, hai creato una nuova fantastica libreria comune che tutti vorranno usare) devi rimuovere questa voce e aggiungere due nuovi attributi Fileall'elemento: Assemblye KeyPath. Assembly deve essere impostato su ".net" e KeyPathsu "yes".

Tuttavia, alcuni ambienti (specialmente qualsiasi cosa con memoria gestita come i linguaggi di scripting) dovranno accedere anche a Typelib. Assicurati di eseguire heatsul tuo typelib e includerlo. heatgenererà tutte le chiavi di registro necessarie. Quant'è fico?


8

Installazione su C:\ProductName

Alcune applicazioni devono essere installate C:\ProductNameo qualcosa di simile, ma il 99,9% (se non il 100%) degli esempi nella rete installa perC:\Program Files\CompanyName\ProductName .

Il seguente codice può essere utilizzato per impostare la TARGETDIRproprietà sulla radice C:dell'unità (presa dall'elenco degli utenti WiX ):

<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>

NOTA: per impostazione predefinita, TARGETDIR non punta a C:\! Indica piuttosto ROOTDRIVEche a sua volta punta alla radice dell'unità con lo spazio più libero ( vedere qui ) - e questa non è necessariamente l' C:unità. Potrebbe esserci un altro disco rigido, partizione o unità USB!

Quindi, da qualche parte sotto il <Product ...>tag, sono necessari i seguenti tag di directory come al solito:

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

Non sarebbe più semplice installarlo WindowsVolume?
Wim Coenen,

1
Sì, ma dovresti usare una soluzione alternativa perché la WindowsVolumeproprietà non può essere utilizzata come Directory(il compilatore fornisce un errore / avviso), come indicato qui e qui . Personalmente, trovo questa soluzione confusa.
gehho,

7

Variabili ambientali

Durante la compilazione dei documenti Wxs in codice wixobj, è possibile utilizzare variabili ambientali per determinare varie informazioni. Ad esempio, supponiamo che tu voglia cambiare quali file vengono inclusi in un progetto. Supponiamo che tu abbia una variabile ambientale chiamata RELEASE_MODE, che hai impostato subito prima di creare il tuo MSI (con uno script o manualmente, non importa) Nella tua fonte di wix, puoi fare qualcosa del tipo:

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

e successivamente nel tuo codice, usalo sul posto per cambiare al volo il tuo documento wxs, ad esempio:

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

1
Sono disponibili anche variabili di compilazione come $ (Configurazione) e $ (Piattaforma). Anche un gruppo più a msdn.microsoft.com/en-us/library/aa302186.aspx
si618

1
@Si - Qualche tempo prima, quel link non è più attivo. Non sono riuscito a trovare l'ultimo.
Peter M,



7

Finestre di modifica

Una buona capacità di modificare le finestre di dialogo è l'utilizzo di SharpDevelop in una versione 4.0.1.7090 (o successiva). Con l'aiuto di questo strumento è possibile aprire, visualizzare in anteprima e modificare una vista autonoma (file wxs da fonti WiX come ad esempio InstallDirDlg.wxs) nella vista Progettazione.


Fantastico, non sapevo che SharpDevelop lo supportasse.
anton.burger,

6

Impostazione del flag abilitazione IIS32BitAppOnWin64 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

Modifica "Pronto per l'installazione?" finestra di dialogo (aka VerifyReadyDlg) per fornire un riepilogo delle scelte fatte.

È simile al seguente:
alt text http://i46.tinypic.com/s4th7t.jpg

Fallo con Javascript CustomAction:


Codice Javascript:

// 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);
}

Dichiarare la CA Javascript:

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

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

Collegare la CA a un pulsante. In questo esempio, la CA viene attivata quando si fa clic su Avanti da CustomizeDlg:

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

Domanda SO correlata: Come posso impostare, in fase di esecuzione, il testo da visualizzare in VerifyReadyDlg?


Sicuramente non dovrebbe essere JScript il linguaggio di scripting di Windows piuttosto che JavaScript il linguaggio di scripting DHTML. Forse un po 'pedante, ma potrebbe essere un po' confuso per alcune persone.
caveman_dick,

5

Inserisci i componenti che possono essere rattoppati individualmente all'interno dei propri frammenti

Vale sia per i programmi di installazione del prodotto che per le patch che se si include qualsiasi componente in un frammento, è necessario includere tutti i componenti in quel frammento. Nel caso di compilazione di un programma di installazione, se si mancano riferimenti ai componenti, verrà visualizzato un errore di collegamento da light.exe. Tuttavia, quando crei una patch, se includi un riferimento a un singolo componente in un frammento, tutti i componenti modificati da quel frammento verranno visualizzati nella patch.

come questo:

<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>

Invece di questo:

<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>

Inoltre, durante l'applicazione di patch utilizzando l'argomento "Utilizzo di WiX puramente" dal file della guida di WiX.chm, utilizzare questa procedura per generare la patch:

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

non è sufficiente avere la versione 1.1 di product.wixpdb creata usando i componenti in frammenti separati. Quindi assicurati di frammentare correttamente il tuo prodotto prima della spedizione.


5

Stampa EULA da Wix3.0 e successive

1) Quando compili il codice sorgente di wix, light.exe deve fare riferimento a WixUIExtension.dll nella riga di comando. Utilizzare l'opzione della riga di comando -ext per questo.

2) Se quando aggiungi il riferimento a WixUIExtension.dll, il tuo progetto non riesce a essere compilato, ciò è molto probabilmente dovuto agli scontri degli ID di dialogo, ovvero il tuo progetto utilizzava gli stessi ID di finestre di dialogo di alcune finestre di dialogo standard in WixUIExtension.dll, assegnare ID diversi alle finestre di dialogo. Questo è un problema abbastanza comune.

3) La finestra di dialogo della licenza deve avere il controllo ScrollableText con l'id "LicenseText". Wix cerca esattamente questo nome di controllo quando stampa.

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

e un pulsante che fa riferimento all'azione personalizzata

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

4) Definire CustomAction con Id = "PrintEula" in questo modo:

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

Nota: BinaryKey è diverso in Wix3.0 rispetto a Wix2.0 e deve essere esattamente "WixUIWixca" (case sensitive).

Quando l'utente preme il pulsante, verrà visualizzata la finestra di dialogo Seleziona stampante standard e sarà in grado di stampare da lì.


5
  • Visualizziamo la versione del prodotto da qualche parte (minuscola) nella prima schermata della GUI. Perché le persone tendono a commettere errori nella scelta della versione giusta ogni volta. (E tienici sviluppatori alla ricerca di età ..)

  • Abbiamo impostato TFSBuild per generare anche trasformazioni (file .mst) con la configurazione per i nostri diversi ambienti. (Conosciamo tutti gli ambienti in cui dobbiamo implementare).

Poiché il post del blog originale di Grant Holliday è inattivo, copio incollato il suo contenuto qui:


Compito MSBuild per generare file di trasformazione MSI da XMLMarch 11 2008

Nel mio post precedente ho descritto come utilizzare i file MSI Transform (* .mst) per separare le impostazioni di configurazione specifiche dell'ambiente da un pacchetto MSI generico.

Sebbene ciò fornisca un livello di flessibilità nella configurazione, ci sono due lati negativi dei file Transform:

  1. Sono un formato binario
  2. Non è possibile "modificare" o "visualizzare" un file di trasformazione. Devi applicarlo o ricrearlo per vedere quali modifiche include.

Fortunatamente possiamo usare la libreria di oggetti di Microsoft Windows Installer (c: windowssystem32msi.dll) per aprire "database" MSI e creare file di trasformazione.

I crediti vanno di nuovo ad Alex Shevchuk - Da MSI a WiX - Parte 7 - Personalizzare l'installazione usando Transforms per mostrarci come ottenere questo risultato con VbScript. Essenzialmente tutto ciò che ho fatto è stato preso l'esempio di Alex e usando Interop.WindowsInstaller.dll ho implementato un'attività MSBuild. L'attività MSBuild

Scarica qui il codice sorgente e l'esempio transforms.xml (soluzione VS2008 con zip ~ 7Kb)



2
Abbiamo ridefinito WelcomeDlgTitle nel mio file di localizzazioni - funziona alla grande! <String Id = "WelcomeDlgTitle"> {\ WixUI_Font_Bigger} Benvenuto nella procedura guidata di installazione [ProductName] [ProductVersion] </String>
saschabeaumont

5

Prima di distribuire un pacchetto di installazione ne controllo sempre il contenuto.

È solo una semplice chiamata dalla riga di comando (secondo il post di Terrences) aprire la riga di comando ed entrare

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

Ciò estrarrà il contenuto del pacchetto in un sottodir "Estrai" con il percorso corrente.


4

Invece di ORCA usa InstEd che è un buon strumento per visualizzare le tabelle MSI. Inoltre ha la possibilità di diffondere due pacchetti trasformando -> Confronta con ...

Inoltre è disponibile una versione Plus con funzionalità aggiuntive. Ma anche la versione gratuita offre una buona alternativa per Orca.


4

Registrazione di assembly .NET per COM Interop con compatibilità x86 / x64

NB Questo frammento è essenzialmente uguale a REGASM Assembly.dll / codebase

In questo esempio stanno succedendo un paio di cose, quindi ecco il codice e lo spiegherò in seguito ...

  <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>

Se ti stavi chiedendo, questo è in realtà per un driver ASCOM Telescope .

Innanzitutto, ho seguito i consigli dall'alto e ho creato alcune variabili platforma in un file separato, puoi vederle sparse nell'XML.

La parte if-then-else vicino alla parte superiore si occupa della compatibilità x86 vs x64. Il mio assembly ha come target "Any CPU", quindi su un sistema x64, devo registrarlo due volte, una volta nel registro a 64 bit e una volta nelle Wow6432Nodearee a 32 bit . L'if-then-else mi prepara per questo, i valori verranno utilizzati in un foreachciclo in seguito. In questo modo, devo solo creare le chiavi di registro una sola volta (principio DRY).

L'elemento file specifica la DLL di assembly effettiva che viene installata e registrata:

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

Niente di rivoluzionario, ma nota che Assembly=".net"questo attributo da solo causerebbe l'assemblaggio dell'assemblea nel GAC, che NON è quello che volevo. Usare l' AssemblyApplicationattributo per indicare se stesso è semplicemente un modo per fermare Wix inserendo il file nel GAC. Ora che Wix sa che è un assembly .net, tuttavia, mi consente di utilizzare alcune variabili di binder nel mio XML, come ad esempio !(bind.assemblyFullname.filDriverAssembly)per ottenere il nome completo dell'assembly.


3

Imposta la DISABLEADVTSHORTCUTSproprietà per forzare tutte le scorciatoie pubblicizzate nel tuo programma di installazione a diventare scorciatoie regolari e non è necessario includere una chiave reg fittizia da usare come chiave di volta.

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

Penso che Windows Installer 4.0 o successivo sia un requisito .


2

È una bella struttura ma in base alla mia esperienza mi chiedo come affronti queste condizioni:

R. Tutte le installazioni sembrano atterrare nella stessa destinazione. Se un utente deve installare tutte e 3 le versioni contemporaneamente, il processo lo consentirà. Possono dire in modo inequivocabile quale versione di ogni eseguibile stanno innescando?

B. Come gestisci i nuovi file esistenti in TEST e / o TRAINING ma non ancora in LIVE?


Ciao Blaine, A. No, non lo fanno. InstallName è in Config.wxi che è l'unico file a cui non fa riferimento svn: externals. Quindi questo è unico per ogni installazione, cioè per ogni prodotto. Questo è anche il motivo per cui modificiamo le guide per ogni versione. B. GOTO A. :) Sono MSI separati con il proprio UpgradeCode.
si618,

1
a proposito, capisco perché hai risposto alla mia domanda con una domanda, ma una volta che hai ottenuto abbastanza punti rep, ti preghiamo di spostare la tua domanda nei commenti di risposta, altrimenti sarà difficile seguire la discussione.
si618,

2

Ecco un modo per aiutare i progetti Web di grandi dimensioni a verificare che il numero di file distribuiti corrisponda al numero di file integrati in un MSI (o modulo di unione). Ho appena eseguito l'attività personalizzata di MSBuild contro la nostra applicazione principale (ancora in fase di sviluppo) e ha raccolto parecchi file mancanti, per lo più immagini, ma alcuni file javascript sono passati a!

Questo approccio (che dà una occhiata alla tabella File di MSI collegandosi al target AfterBuild del progetto WiX) potrebbe funzionare per altri tipi di applicazioni in cui si ha accesso a un elenco completo dei file previsti.


2

Esecuzione di una reinstallazione forzata quando un'installazione non consente la disinstallazione o la reinstallazione e non esegue il rollback.

Script VBscript utilizzato per sovrascrivere un'installazione che non viene disinstallata per qualsiasi motivo.

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)

2

Crea un'interfaccia utente con un'azione personalizzata che imposterà una variabile e l'interfaccia utente disabiliterà / abiliterà il pulsante successivo (o simile) in base alla variabile impostata nell'azione personalizzata.

Non così semplice come si potrebbe pensare, non troppo difficile, semplicemente non documentato da nessuna parte!

Interazioni Wix con condizioni, proprietà e azioni personalizzate

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.