Dnešním článkem navážeme na předchozí o nástroji WIX, který slouží k vytváření klasických MSI windows instalátorů. Pomocí konfiguračního XML, které vytvoříte, můžete velice snadno vytvářet sofistikované instalátory pro vaše produkty. V tomto díle se zaměřím na pár triků, které jsem použil při vytváření mého instalátoru a které by se vám mohly hodit.

Spuštění vlastní akce

Můžete se setkat s nutností spuštění vlastního programu, který je umístěn v adresářové struktuře instalace. Může se jednat například o skript, který naplní databázi apod. Pro to, abychom mohli tento program spustit, budeme muset definovat tzv. custom action a také vytvořit sekvenci, která tuto akci spustí. Prvně si tedy vytvoříme akci, která spustí námi zadaný soubor:

<CustomAction Id="InstallDBScript" Directory="dbdir" Execute="deferred"
    Return="check" ExeCommand="sql.exe aaa.sql" />

Tímto XML kódem jsme vytvořili akci, který v adresáři dbdir (toto id musí odkazovat na id adresáře, který instalujeme) spustí příkaz sql.exe s parametrem aaa.sql. Samozřejmě předpokládáme, že sql.exe je buď v PATH nebo je v onom adresáři. Atribut return udává, zdali má instalátor čekat na návratovou hodnotu a zkontrolovat, zdali je v pořádku (existuje více možností – více v dokumentaci). Atribut execute nastavuje způsob spuštění daného příkazu (zde nastaveno spuštění pro externí skript – opět viz dokumentace).

Nyní je třeba upravit sekvenci zpracování instalace tak, aby se náš skript spustil v pravou chvíli. K tomu dospějeme následující konstrukcí:

<InstallExecuteSequence>
    <Custom Action="InstallDBScript" Before="InstallFinalize">
       (NOT INSTALLED) AND (NOT UPGRADINGPRODUCTCODE) AND (NOT REMOVE="ALL")
    </Custom>
</InstallExecuteSequence>

Tímto XML kódem zajístíme, že se spustí naše akce před fází InstallFinalize (atribut before; existuje i atribut after – viz dokumentace; jednotlivé fáze jsou popsány také v dokumentaci a nebudu je zde zmiňovat). Text uvnitř tagu pak udává, kdy se má daná akce spustit – zde ji chceme spouštět jenom v případě, že daný produkt není nainstalovaný, neupgradujeme a nejedná se o odinstalaci (tzn. tato akce se spustí pouze při nové instalaci). Opět odkazuji na dokumentaci pro detailnější popis.

Čtení registru

Pokud potřebujeme zapisovat, či číst z registru Windows, WIX nám pro toto nabízí celkem sofistikované a jednoduché nástroje, které lze velmi jednoduše použít. V následujícím příkladě si přečteme z registru informaci o tom, jestli je nainstalovaná Java. Stačí nám na to jeden příkaz, který přečte klíč registru a pokud tento klíč neexistuje, tak pomocí podmínky zobrazíme dialog o chybě. V dalším tipu k tomu pak přidáme ještě kontrolu verze Javy.

<Property Id="JAVA">
    <RegistrySearch Id="Java6" Root="HKLM"
        Key="SOFTWARE\JavaSoft\Java Runtime Environment"
        Name="CurrentVersion" Type="raw" />
</Property>
<Condition Message="JRE is not installed on this machine. Please install it!">
  <![CDATA[Installed OR JAVA]]>
</Condition>

Jak vidíte, nejdříve se definujeme proměnnou JAVA, která nám pomocí příkazu RegistrySearch přečte z určitého klíče informace. Tato informace je nahrána do naší proměnné (v tomto případě konkrétní verze nainstalované Javy).

Následuje příkaz  Condition, který zobrazí hlášku definovanou v atributu Message pouze tehdy, pokud není definována proměnná JAVA. Zde je ještě doplněna jedna podmínka a to je ta, že pokud je již aplikace nainstalovaná, tak se tato podmínka již dále nekontroluje. Jenom pro pořádek uvedu, že zde jsou podmínky defaultně znegované.

Provedení skriptu

Někdy je třeba během instalace ověřit určitou podmínku. Toto ověření nemusí být zcela triviální, a proto může vyžadovat logiku. Bohužel jediným způsobem jak vytvářet logiku v instalaci je použít JavaScript nebo VBScript. Zato nám tento způsob umožňuje dělat velice zajímavé operace, které bychom jinak museli řešit externím programem. V našem případě budeme kontrolovat, zdali verze Javy, která je nainstalována v systému, odpovídá požadované verzi (navazuje na předchozí příklad – použitá proměnná JAVA):

<Property Id="JAVAVERSION"><![CDATA[
    function main()
    {
        var version = Session.Property("JAVA");
        if (version < 1.6) {
            return 3;
        } else {
            return 0;
        }
     }
]]></Property>

Rozeberme si tedy nyní to, co jsme vytvořili. V podstatě jsme definovali novou proměnnou, která ovšem obsahuje kód JavaScriptu. Tento kód již používá námi definovanou proměnnou JAVA, kterou porovnává proti naší verzi (1.6). Pokud je verze v pořádku vracíme 0 – defaultní návratovou hodnotu. Naopak když kontrola selhala, skript musí vrátit 3 což je chybový kód pro ukončení instalace. Nyní už nám stačí skript na správném místě spustit:

<CustomAction Id="CheckJavaVersion" JScriptCall="main" Property="JAVAVERSION" />
<InstallExecuteSequence>
    <Custom Action="CheckJavaVersion" After="LaunchConditions">
      NOT INSTALLED
    </Custom>
    …
</InstallExecuteSequence>

V podstatě již známým způsobem (první příklad) jsme vytvořili novou CustomAction s tím rozdílem, že zde definujeme atribut JScriptCall, který WIXu říká, jako metodu v našem JS kódu má při této akci spustit (main) a atribut Property, který určuje proměnnou kde je kód uložen. Tímto způsobem je pak možné mít v jedné proměnné více funkcí a ty nezávisle spouštět v různých akcích.

Úplně nakonec přidáme naši kontrolu do InstallExecuteSequence. Jelikož se jedná o kontrolu předinstalační – tedy tuto kontrolu chceme provést ještě před tím, než se uživatel začne proklikávat instalací, spustíme ji hned po LaunchConditions (po výpočtu potřebného místa pro instalaci apod. – v podstatě při zobrazení úvodního dialogu instalace). Pokud kontrola selže, díky návratovému kódu 3 se instalace ukončí.

Vložení vlastního dialogu

Zcela zásadní změnou, kterou je třeba v určitých chvílích udělat je vložení či změna instalačního dialogu do/v již existující sekvenci dialogů, které standardně nabízí WIX. Tato operace již není zcela triviální a vyžaduje přinejmenším použití editoru, o kterém jsme mluvili v prvním díle. Tento editor nám bude velkým pomocníkem při návrhu vzhledu dialogu a jeho vložení do sekvence. V následujícím příkladě si ukážeme jak změnit vzhled finálního dialogu při ukončení instalace. Tento dialog se zobrazí pouze při neúspěšném ukončení instalace (např. při selhání kontroly veze Javy).

<UI>
    <Dialog Width="370" Height="270" Title="!(loc.FatalError_Title)">
        <Control Type="PushButton" X="236" Y="243" Width="56" Height="17"
          Default="yes" Cancel="yes" Text="!(loc.WixUIFinish)">
            <Publish Event="EndDialog" Value="Exit">1</Publish>
        </Control>
        <Control Type="PushButton" X="304" Y="243" Width="56" Height="17"
          Disabled="yes" Text="!(loc.WixUICancel)" />
        <Control Type="Bitmap" X="0" Y="0" Width="370" Height="234"
          TabSkip="no" Text="!(loc.FatalErrorBitmap)" />
        <Control Type="PushButton" X="180" Y="243" Width="56" Height="17"
          Disabled="yes" Text="!(loc.WixUIBack)" />
        <Control Type="Line" X="0" Y="234" Width="370" Height="0" />
        <Control Type="Text" X="135" Y="20" Width="220" Height="60"
          Transparent="yes" NoPrefix="yes" Text="!(loc.FatalErrorTitle)" />
        <Control Type="Text" X="135" Y="160" Width="220" Height="80"
          Transparent="yes" NoPrefix="yes">
            <Text>ERROR SCREEN TEXT</Text>
        </Control>
    </Dialog>

    <InstallUISequence>
        <Show Dialog="MyFatalError" OnExit="error" />
    </InstallUISequence>

    <AdminUISequence>
        <Show Dialog="MyFatalError" OnExit="error" />
    </AdminUISequence>
    …
</UI>

Jak vidíte sami, vytvoření dialogu vyžaduje již více „psaní“ a proto je lépe použít editor. Co se tedy přesně v tomto XML kódu děje? Vytváříme vlastní dialog jménem MyFatalError, kterým nahradíme chybový dialog na konci instalace. Celý kód je uzavřen v UI tagu, kde se definuje vzhled instalace. Můžete si zde všimnout, že se vytváří tlačítka, obrázek a další prvky dialogu. Je zde také text, který se vypíše ve spodní části dialogu – o ten nám především jde. Můžeme zde vypsat hlášku o tom, proč se instalace nepovedla a co má uživatel dělat dál. Atributy text, které jsou vypadají jako (loc.XXX) jsou lokalizovatelné zprávy, které můžete buď nahradit či lokalizovat pomocí WIXu (více v dokumentaci).

Zcela na konci je nejdůležitější část a to je samotné nahrazení v sekvenci dialogů (InstallUISequence a AdminUISequence). Zde definujeme, že pokud instalace skončí chybovým stavem, pak se zobrazí námi vytvořený dialog.

Pochopitelně lze vkládat i vlastní dialogy, které rozšíří standardní sadu dialogů, ale to je již na rámec tohoto článku. Více se dozvíte v dokumentaci.

Smazání adresáře při odinstalaci

Nyní uvedu zcela konkrétní použití příkazu CustomAction a tím je smazání instalačního adresáře při odinstalaci. Situace je následující. Během života programu mohou vzniknout soubory, které během instalace nebyly vytvořeny. Jedná se obecně o logy a podobné. Díky tomu se pak během odinstalace nesmaže celý instalační adresář a je třeba to udělat ručně. Pojďme se tedy podívat jak na to:

<Property Id='COMMANDEXE' Admin='no' Hidden='no' Secure='no'>
    <DirectorySearch Id='CmdExe_DirSearch' Depth='3' Path='c:\windows\system32'>
        <FileSearch Name='cmd.exe'/>
    </DirectorySearch>
</Property>

<CustomAction Id="DeleteFolder" Property='COMMANDEXE' Return="ignore"
  Execute="deferred" ExeCommand=' /C rmdir /S /Q "[DIRID]"' />

<InstallExecuteSequence>
    …
    <Custom Action="DeleteFolder" After="RemoveFiles">
      (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
    </Custom>
</InstallExecuteSequence>

Nejdříve jsme nalezli příkaz CMD, který použijeme pro vykonávání akcí s příkazovou řádkou. Zde si můžete všimnout použití příkazu FileSearch, který je velmi užitečný při hledání jakýchkoliv souborů. Dále jsme definovali CustomAction, která provede daný příkaz rmdir, který smaže adresář definovaný jeho id (ve složených závorkách a odpovídající id v instalační struktuře – viz první díl tohoto článku).

Závěr

Sami můžete vidět, že možnosti WIXu jsou poměrně velké a pomocí jeho služeb lze dosáhnout mnoha zajímavých použití. Jediné nad čím si může Java programátor povzdechnout je to, že se jedná o MSI instalátor, který je použitelný pouze ve Windows. Dejte nám v komentářích vědět, zdali používáte jiné (např. i multiplatformní) instalační systémy a jaké jsou vaše zkušenosti s nimi.