Sztuczki i wskazówki dotyczące WiX


264

Używamy WiX już od jakiegoś czasu i pomimo zwykłych problemów związanych z łatwością użycia, wszystko idzie dobrze. To, czego szukam, to przydatne porady dotyczące:

  • Konfigurowanie projektu WiX (układ, referencje, wzorce plików)
  • Integracja WiX z rozwiązaniami oraz procesy kompilacji / wydania
  • Konfigurowanie instalatorów dla nowych instalacji i aktualizacji
  • Wszelkie dobre hacki WiX, które chcesz udostępnić

spójrz na gui4wix.codeplex.com
TarunG

10
Zamknięty, ponieważ nie jest konstruktywny? Nauczyłem się stosów, zadając to pytanie! Trochę spójność z StackOverflow byłoby również miłe ... np stackoverflow.com/questions/550632/...
si618

15
Ma „203” wzwyż, to wystarczy, aby udowodnić swoją przydatność.
TarunG

SO pytania muszą mieć ostateczną, poprawną odpowiedź; pytania otwarte powodują, że pytania, które ludzie zadają na temat rzeczywistych problemów, opuszczają pierwszą stronę. faq @ Si .: Ta polityka zawsze istniała AFAIK, ale teraz lepiej ją egzekwować; to pytanie ma prawie trzy lata.
Jim Dagg

W porządku, Jim, jest to pytanie otwarte, i myślę, że decyzja należy do społeczności SO, ale muszę powiedzieć, że zamknięcie go jako niekonstruktywne wydaje się dziwne, biorąc pod uwagę, że ja i na pierwszy rzut oka wiele innych osób uznałem to pytanie za przydatne (np. goo.gl/Zqp2X ) i że bardzo dobrze pasuje do practical, answerable questions based on actual problems that you faceczęści FAQ.
si618,

Odpowiedzi:


157
  1. Zmienne przechowuj w osobnym wxipliku dołączania Umożliwia ponowne użycie, zmienne można szybciej znaleźć i (w razie potrzeby) umożliwia łatwiejszą manipulację przez narzędzie zewnętrzne.

  2. Zdefiniuj zmienne platformy dla kompilacji x86 i 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. Zapisz lokalizację instalacji w rejestrze, umożliwiając aktualizacjom znalezienie właściwej lokalizacji. Na przykład, jeśli użytkownik ustawi niestandardowy katalog instalacyjny.

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

    Uwaga : guru WiX Rob Mensching opublikował doskonały wpis na blogu, który zawiera więcej szczegółów i naprawia przypadek krawędzi, gdy właściwości są ustawiane z wiersza poleceń.

    Przykłady z wykorzystaniem 1. 2. i 3.

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

    i

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
  4. Najprostszym podejściem jest zawsze przeprowadzanie poważnych aktualizacji , ponieważ umożliwia zarówno nowe instalacje, jak i aktualizacje w jednym MSI. UpgradeCode jest przypisany do unikalnego Guid i nigdy się nie zmieni, chyba że nie chcemy uaktualnić istniejącego produktu.

    Uwaga : W WiX 3.5 jest nowy element MajorUpgrade , który jeszcze bardziej ułatwia życie !

  5. Tworzenie ikony w Dodaj / Usuń programy

    <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. W kompilacjach wersji instalujemy nasze instalatory, kopiując plik msi do katalogu wdrażania. Przykładem tego jest użycie celu wixproj wywołanego z celu 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. Używaj ciepła do zbierania plików za pomocą symboli wieloznacznych (*) Guid. Przydatne, jeśli chcesz ponownie wykorzystać pliki WXS w wielu projektach (zobacz moją odpowiedź na temat wielu wersji tego samego produktu). Na przykład ten plik wsadowy automatycznie zbiera dane wyjściowe 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 

    Trochę się dzieje, robocopyusuwa się metadane kopii roboczej Subversion przed zebraniem; -drodniesienie katalog główny jest ustawiony na naszym miejscu instalacji zamiast domyślnej TARGETDIR; -varsłuży do tworzenia zmiennej określającej katalog źródłowy (wynik wdrożenia sieciowego).

  8. Łatwy sposób umieszczenia wersji produktu w oknie dialogowym powitalnym za pomocą Strings.wxl do lokalizacji. ( Źródło : saschabeaumont . Dodano, ponieważ ta wielka wskazówka jest ukryta w komentarzu)

    <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. Oszczędź sobie bólu i postępuj zgodnie z radą Wima Coehena dotyczącą jednego składnika na plik. Pozwala to również pominąć identyfikator GUID komponentu (lub symbol wieloznaczny *) .

  10. Rob Mensching ma zgrabny sposób szybkiego wyszukiwania problemów w plikach dziennika MSI poprzez wyszukiwanie value 3. Zwróć uwagę na komentarze dotyczące internacjonalizacji.

  11. Podczas dodawania funkcji warunkowych bardziej intuicyjnie jest ustawić domyślny poziom funkcji na 0 (wyłączony), a następnie ustawić poziom warunku na żądaną wartość. Jeśli ustawisz domyślny poziom funkcji> = 1, poziom warunku musi wynosić 0, aby go wyłączyć, co oznacza, że ​​logika warunku musi być przeciwna do oczekiwanej, co może być mylące :)

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

O dodaniu ikony w Dodaj / Usuń programy, DOKŁADNIE to, czego szukałem. Gdzie trzymasz te trzy linie? +1 za czystą niesamowitość.
Everett,

Zazwyczaj umieszczam je tuż za (i oczywiście poniżej) elementem <Package>. Spójrz na schemat ważności wix.sourceforge.net/manual-wix3/schema_index.htm
si618

+1, chciałbym móc zrobić +100, to jest najbardziej użyteczny fragment informacji Wix, na który natknąłem się.
Tim Long

Dzięki Tim! Rob Mensching, Bob Arson, Wim Coehen i inni zasługują na uznanie za dzielenie się swoją wiedzą.
si618,

38

Sprawdzanie, czy IIS jest zainstalowany:

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

Sprawdzanie, czy w systemie Vista + jest zainstalowana zgodność metabazy IIS 6:

<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

Wszystkie identyfikatory przechowuj w osobnych przestrzeniach nazw

  • Funkcje zaczynają się od F. przykładów: F.Documentation, F.Binaries, F.SampleCode.
  • Składniki zaczynają się od C. np .: C.ChmFile, C.ReleaseNotes, C.LicenseFile, C.IniFile, C.Registry
  • Funkcje niestandardowe to CA. np .: CA.LaunchHelp, CA.UpdateReadyDlg, CA.SetPropertyX
  • Pliki są Fi.
  • Katalogi są Di.
  • i tak dalej.

Uważam, że to bardzo pomaga w śledzeniu wszystkich różnych identyfikatorów we wszystkich różnych kategoriach.


Nie używam przestrzeni nazw, ale dołączam identyfikatory; na przykład: ExamplesFeature, ChmFileComponent. Chyba uwielbiam pisać ;-)
dvdvorle

25

Fantastyczne pytanie. Chciałbym zobaczyć niektóre najlepsze praktyki.

Mam wiele plików, które dystrybuuję, więc skonfigurowałem swój projekt w kilku plikach źródłowych wxs.

Mam plik źródłowy najwyższego poziomu, który nazywam Product.wxs, który zasadniczo zawiera strukturę instalacji, ale nie rzeczywiste komponenty. Ten plik ma kilka sekcji:

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

Reszta plików .wix składa się z fragmentów zawierających grupy składowe, do których odwołuje się znacznik Feature w pliku Product.wxs. Mój projekt zawiera ładne logiczne grupowanie plików, które dystrybuuję

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

Nie jest to idealne, moje wyczucie pająka OO trochę mrowi, ponieważ fragmenty muszą odwoływać się do nazw w pliku Product.wxs (np. DirectoryRef), ale łatwiej mi utrzymać pojedynczy duży plik źródłowy.

Chciałbym usłyszeć komentarze na ten temat, a jeśli ktoś też ma jakieś dobre wskazówki!


Nasza konfiguracja jest również bardzo podobna do tego podejścia. To dobrze, ponieważ możemy użyć naszego odpowiednika Products.wxs jako naszej podstawowej konfiguracji dla różnych produktów.
si618

@Peter Tate: twoje wyczucie pająka jest prawidłowe. Zobacz moją odpowiedź na temat aliasingu katalogów.
Wim Coenen

Stosuję to samo podejście: Product.wxs z układem jest statyczny, a zadanie kompilacji (heat.exe) generuje mój plik
Content.wxs

20

Dodaj pole wyboru do okna dialogowego wyjścia, aby uruchomić aplikację lub plik pomocy.

...

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

Jeśli zrobisz to w ten sposób, „standardowy” wygląd nie będzie do końca odpowiedni. Pole wyboru jest zawsze szare tło, a okno dialogowe jest białe:

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

Jednym ze sposobów jest określenie własnego niestandardowego ExitDialog z polem wyboru o innej lokalizacji . Działa to, ale wydaje się, że dużo pracy wymaga tylko zmiany koloru jednego elementu sterującego. Innym sposobem rozwiązania tego samego problemu jest przetworzenie wygenerowanego pliku MSI w celu zmiany pól X, Y w tabeli Control dla tego konkretnego kontrolki CheckBox. Kod javascript wygląda następująco:

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

Uruchomienie tego kodu jako skryptu wiersza polecenia (przy użyciu cscript.exe) po wygenerowaniu pliku MSI (z light.exe) spowoduje wygenerowanie ExitDialog, który wygląda bardziej profesjonalnie:

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


Ha! Nie mój blog. Też to czytam. W powyższym tekście mam link do wpisu na blogu. Ale zrobili to inaczej niż ja. Lubię swoją drogę. !!
Cheeso

1
Dzięki za js, bardzo pomocne! Jedną z rzeczy, które miałem do zmian w wxs ma zastąpić WIXUI_EXITDIALOGOPTIONALCHECKBOXz WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installedwewnątrz<Publish>
Alexander Kojevnikov

Czy istnieje sposób domyślnego zaznaczenia pola wyboru?
Alek Davis,

Aby domyślnie zaznaczyć to pole, użyłem tego: <Identyfikator właściwości = "WIXUI_EXITDIALOGOPTIONALCHECKBOX" Wartość = "1" />
Alek Davis

Wygląda na fajne rozwiązanie, ale jak mam z niego korzystać? Czy jest jakiś sposób na umieszczenie js wewnątrz elementu <AfterBuild> w moim wixproj? Lub skoro odwołujesz się do uruchamiania go z wiersza polecenia, czy jest to lepsze jako zdarzenie po kompilacji, w takim przypadku, co to jest dobry interpreter wiersza polecenia js dla systemu Windows?
vanmelle,

18

Tworzenie wersji Live, Test, Training, ... przy użyciu tych samych plików źródłowych.

W skrócie: utwórz unikalny kod aktualizacji dla każdego instalatora i automatycznie zdefiniuj pierwszy znak każdego przewodnika dla każdego instalatora, pozostawiając pozostałe 31 unikalnych.

Wymagania wstępne

Założenia

  • Zmienne WiX służą do definiowania kodu aktualizacji, nazwy produktu, nazwy instalacji.
  • Masz już działającego instalatora. Nie spróbowałbym tego, dopóki ty tego nie zrobisz.
  • Wszystkie twoje komponenty są przechowywane w jednym pliku (Components.wxs). Ten proces zadziała, jeśli masz wiele plików, będzie po prostu więcej pracy do zrobienia.

Struktura katalogów

  • Setup.Library
    • Wszystkie pliki wxs (komponenty, funkcje, okna dialogowe interfejsu użytkownika, ...)
    • Common.Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder, ...)
  • Setup.Live (wixproj)
    • Połącz wszystkie pliki Setup.Library za pomocą „Dodaj istniejący plik” -> „Dodaj jako łącze” (mały przycisk strzałki w dół obok przycisku Dodaj w programie Visual Studio)
    • Config.wxi (ma unikalny kod aktualizacji, nazwa produktu, nazwa instalacji, ...)
  • Setup.Test , ...
    • jak na żywo, ale Config.wxi jest skonfigurowany dla środowiska testowego.

Proces

  • Utwórz katalog Setup.Library i przenieś wszystkie pliki wxs i wxi (oprócz Config.wxi) z istniejącego projektu.
  • Utwórz Setup.Live, Setup.Test itp. Zgodnie z normalnym wixproj.
  • Dodaj cel BeforeBuild w wixproj w Setup.Live itp., Aby wykonać MSBuild Community Task FileUpdate, aby zmodyfikować Guids (użyłem A do Live, B do Testu i C do treningu)
  • Dodaj cel AfterBuild, aby przywrócić Guids Components.wxs z powrotem do 0.
  • Sprawdź w Orca, czy każdy komponent w każdym MSI ma zmodyfikowany identyfikator GUID.
  • Sprawdź, czy przywrócono oryginalne prowadnice.
  • Sprawdź, czy każdy MSI instaluje (i aktualizuje) prawidłowy produkt i lokalizację.

Przykład 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>

Przykład 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>

Przykład 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">
          ...

Uwaga: sugerowałbym teraz pozostawienie atrybutu Guid poza Komponentem (odpowiednik *), użycie jednego pliku na komponent i ustawienie pliku jako ścieżki klucza. Eliminuje to potrzebę wywoływania ModifyComponentsGuidsi RevertComponentsGuidscelów pokazanych poniżej. Może to nie być możliwe w przypadku wszystkich komponentów.

Przykład 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>

Końcowe przemyślenia

  • Ten proces powinien również działać w celu tworzenia różnych instalatorów dla różnych modułów scalających (funkcje Live, Test, ... jako funkcje) dla tego samego instalatora. Poszedłem z różnymi instalatorami, ponieważ wydawało się to bezpieczniejszą opcją, istnieje większe ryzyko, że ktoś może uaktualnić Live zamiast Training, jeśli są na tym samym pudełku, a ty po prostu korzystasz z funkcji różnych modułów scalających.
  • Jeśli używasz MSI do przeprowadzania aktualizacji, a także nowych instalacji, tj. Podejście oparte tylko na głównej aktualizacji, i zapisujesz lokalizację instalacji w rejestrze, pamiętaj o utworzeniu zmiennej dla nazwy klucza dla każdej instalacji.
  • Tworzymy również zmienne w każdym pliku Config.wxi, aby umożliwić unikalne wirtualne nazwy katalogów, pule aplikacji, nazwy baz danych itp. Dla każdego instalatora.

AKTUALIZACJA 1: Automatyczne generowanie Guids komponentu eliminuje potrzebę wywoływania zadania FileUpdate, jeśli tworzysz komponent z Guid = „*” dla każdego pliku, ustawiając plik jako ścieżkę do klucza.

AKTUALIZACJA 2: Jednym z problemów, z którymi się zetknęliśmy, jest to, że jeśli nie wygenerujesz automatycznie swojego komponentu Guid, a kompilacja się nie powiedzie, pliki tymczasowe należy ręcznie usunąć.

AKTUALIZACJA 3: Znaleziono sposób na usunięcie zależności od svn: externals i tworzenia plików tymczasowych. To sprawia, że ​​proces kompilacji jest bardziej odporny (i jest najlepszą opcją, jeśli nie możesz użyć symboli zastępczych w swoich przewodnikach) i mniej kruchy, jeśli kompilacja zakończy się niepowodzeniem w świetle lub świecy.

AKTUALIZACJA 4: Obsługa wielu instancji za pomocą transformacji instancji jest dostępna w WiX 3.0+, na pewno również warto ją sprawdzić.


+1 za odniesienie do MSBuild Community Tasks, uwielbiam ten pakiet
BozoJoe,

17

Korzystanie z rejestrowania diagnostycznego Msi w celu uzyskania szczegółowych informacji o awarii

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

Gdzie

Package.msi
to nazwa twojego pakietu i
c: \ Package.log
to miejsce, w którym chcesz uzyskać wynik dziennika

Kody błędów Msi

Wideo wprowadzające Wix Wideo wprowadzające
Oh i Random Wix przedstawiające Roba Menschinga „Mr. WiX” jest „koncepcyjnym dużym obrazem”.


2
+1 Byłoby znacznie lepiej, gdybyśmy mogli włączyć logowanie z Wix zamiast z wiersza poleceń.
si618

3
WiX to robi. Ustaw właściwość MsiLogging. Obsługiwane tylko przez Windows Installer 4.0+.
Rob Mensching

Dziękuję bardzo „Mr. Wix”. Musisz to sprawdzić.
Terrance

17

Użyj Javascript CustomActions, ponieważ są one takie proste

Ludzie mówili, że JavaScript jest niewłaściwą rzeczą do użycia w MSI CustomActions . Podane powody: trudne do debugowania, trudne do zapewnienia niezawodności. Nie zgadzam się Debugowanie nie jest trudne, a na pewno nie trudniejsze niż C ++. Po prostu jest inaczej. Zauważyłem, że pisanie CustomActions w Javascripcie jest bardzo łatwe, o wiele łatwiejsze niż używanie C ++. O wiele szybciej. I tak samo niezawodny.

Jest tylko jedna wada: JavaScript CustomActions można wyodrębnić za pośrednictwem Orca, podczas gdy CA / C ++ CA wymagałoby inżynierii wstecznej. Jeśli uważasz, że twoja magia instalatora jest chronioną własnością intelektualną, będziesz chciał uniknąć skryptu.

Jeśli używasz skryptu, musisz zacząć od jakiejś struktury. Oto niektóre na początek.


Kod „płyty grzewczej” JavaScript dla 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
    };
}

Następnie zarejestruj niestandardową akcję za pomocą czegoś takiego:

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

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

Możesz oczywiście wstawić tyle funkcji Javascript, ile chcesz, dla wielu niestandardowych działań. Jeden przykład: użyłem Javascript, aby wykonać kwerendę WMI w IIS, aby uzyskać listę istniejących stron internetowych, na których można zainstalować filtr ISAPI. Ta lista została następnie wykorzystana do wypełnienia pola listy pokazanego później w sekwencji interfejsu użytkownika. Wszystko bardzo łatwe.

W IIS7 nie ma dostawcy WMI dla IIS, więc skorzystałem z tej shell.Run()metody, aby wywołać appcmd.exe w celu wykonania pracy. Łatwo.

Powiązane pytanie: o JavaScript CustomActions


2
+1 Uważam, że podejście DTF jest łatwe do skonfigurowania, ale javascript może być również użyteczny.
si618,

12

Peter Tate już pokazał, jak można definiować definicje ComponentGroup wielokrotnego użytku w osobnych fragmentach wix. Kilka dodatkowych sztuczek związanych z tym:

Aliasing katalogów

Fragmenty grup komponentów nie muszą wiedzieć o katalogach zdefiniowanych przez wxs głównego produktu. W swoim fragmencie grupy komponentów możesz mówić o takim folderze:

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

Następnie główny produkt może aliasować jeden ze swoich katalogów (np. „ProductInstallFolder”) w następujący sposób:

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

Wykres zależności

Elementy ComponentGroup mogą zawierać elementy potomne ComponentGroupRef. Jest to świetne, jeśli masz dużą pulę komponentów wielokrotnego użytku ze złożonym wykresem zależności między nimi. Po prostu ustawiłeś ComponentGroup we własnym fragmencie dla każdego komponentu i zadeklarowałeś takie zależności:

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

Jeśli teraz odwołujesz się do grupy komponentów „B” w konfiguracji, ponieważ jest to bezpośrednia zależność twojej aplikacji, automatycznie pobierze grupę komponentów „A”, nawet jeśli autor aplikacji nigdy nie zorientował się, że była to zależność „B”. „Po prostu działa”, o ile nie ma żadnych zależności cyklicznych.

Wixlib wielokrotnego użytku

Powyższa idea wykresu zależności działa najlepiej, jeśli skompilujesz komponenty typu big-pool-o-wielokrotnego użytku w wixlib wielokrotnego użytku z lit.exe. Podczas tworzenia konfiguracji aplikacji możesz odwoływać się do tego wixlib podobnie jak plik wixobj. Linker candle.exe automatycznie eliminuje wszelkie fragmenty, które nie są „wciągane” przez pliki wxs głównego produktu.


12

Dziwię się, że nikt nie wspomniał o użyciu T4 do generowania pliku WXS podczas kompilacji. Dowiedziałem się o tym dzięki Henry Lee @ New Age Solutions .

Zasadniczo tworzysz niestandardowe zadanie MSBuild w celu wykonania szablonu T4, który wysyła WXS tuż przed kompilacją projektu Wix. Pozwala to na (w zależności od sposobu jego implementacji) automatyczne dołączanie wszystkich danych wyjściowych zestawów podczas kompilowania innego rozwiązania (co oznacza, że ​​nie musisz już edytować pliku wxs za każdym razem, gdy dodajesz nowy zestaw).


2
+1 to naprawdę miłe, nie martwię się tak bardzo o zespoły, ale nasze projekty internetowe mogą mieć problemy ze stronami aspx i innymi artefaktami (obrazkami, css), które są dodawane do projektu, ale nie WiX.
si618,

4
Dla przyszłych użytkowników Wix 3.5 ma program narzędziowy heat.exe, który wykonuje to zbieranie automatycznie
Mrchief

@Mrchief - Nie wierzę, że Heat wybiera zespoły, które są kopiowane lokalnie - najwyraźniej jest to jednak planowane na 4.0. Odniesienie: sourceforge.net/tracker/…
Peter T. LaComb Jr.,

Ciepło nie odbiera referencyjnych zestawów.
tofutim

Jakie są dobre przykłady użycia T4 do wygenerowania pliku WXS?
tofutim

12

Użycie Heat.exe do rozbicia twarzy i spowodowania „Epic Pwnage” w boleśnie dużych instalacjach

Rozwijanie odpowiedzi Si i Roberta-P na temat ciepła.

Tłumaczenie: (Używanie ciepła, aby uniknąć ręcznego wpisywania poszczególnych plików do projektu i do automatyzacji kompilacji dla ogólnego łatwiejszego procesu.)

Szczegółowa składnia ciepła WiX 2.0

W przypadku nowszych wersji (nie wszystko różni się od starszych wersji, ale istnieją potencjalnie denerwujące zmiany składni ....) przejdź do katalogu Ciepło pochodzi z cmd.exe i po prostu wpisz ciepło, ale mam tutaj przykład, aby uzyskać pomoc w razie potrzeby z nowszymi wersjami.

Dodanie następujących elementów do zdarzenia budowania w Visual Studio 2010.
(Kliknij prawym przyciskiem myszy Projekt-> Właściwości -> Zdarzenia kompilacji-> Zdarzenia przed kompilacją)

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

-gg 

Generuje prowadnice po uruchomieniu ciepła (jak w przypadku wykonania powyższego polecenia)

-scom 

Nie chwytaj „plików COM”

-sreg 

Nie chwytaj „plików rejestru”

-sfrag 

Nie chwytaj „Fragmentów”

-srd 

Nie chwytaj „katalogu głównego”

reż

katalog wskazuje, że chcesz, aby Heat szukał w folderze

„$ (EnviromentVariable)”

Nazwa zmiennej, którą chcesz dodać do zmiennych Preprocesora we właściwościach projektu (Kliknij prawym przyciskiem myszy, Idź do właściwości) -> Kompilacja, w której jest napisane Zdefiniuj zmienne preprocesora (zakłada Visual Studio 2010)

Przykład:
EnviromentVariable = C: \ Project \ bin \ Debug;
Bez podwójnych cudzysłowów, ale zakończone średnikiem

-cg GroupVariable 

ComponentGroup, do której nastąpi odwołanie z utworzonego fragmentu do głównego pliku wxs

FragmentDir

Katalog fragmentów, w którym będzie przechowywany wyjściowy fragment wxs

FileName.wxs

Nazwa pliku

Pełny samouczek tutaj, więc cholernie pomocny

Część 1 Część 2


Istnieje inne przydatne narzędzie do nieco innych celów: Parafina ( wintellect.com/CS/blogs/jrobbins/archive/2010/03/10/4107.aspx )
ralf.w.

9

W tym obiekty COM:

heatgeneruje wszystkie najbardziej (jeśli nie wszystkie) wpisy rejestru i inną potrzebną dla nich konfigurację. Cieszyć!

W tym zarządzane obiekty COM (znane również jako obiekty .NET lub C # COM)

Użycie heatna zarządzanym obiekcie COM da ci prawie kompletny dokument wix.

Jeśli nie potrzebujesz biblioteki dostępnej w GAC (tj. Globalnie dostępnej: W większości przypadków nie potrzebujesz tego z zestawami .NET - prawdopodobnie zrobiłeś w tym momencie coś złego, jeśli nie jest to zamierzone biblioteka współdzielona), należy się upewnić, że CodeBaseklucz rejestru został zaktualizowany [#ComponentName]. Jeśli planujesz zainstalować go w GAC (np. Masz nową niesamowitą wspólną bibliotekę, z której wszyscy będą chcieli korzystać), musisz usunąć ten wpis i dodać dwa nowe atrybuty do Fileelementu: Assemblyi KeyPath. Zestaw powinien być ustawiony na „.net” i KeyPathpowinien być ustawiony na „tak”.

Jednak niektóre środowiska (zwłaszcza wszystko z pamięcią zarządzaną, takie jak języki skryptowe) również będą potrzebować dostępu do Typelib. Pamiętaj, aby uruchomić heatna swoim typelibie i dołączyć go. heatwygeneruje wszystkie potrzebne klucze rejestru. Jakie to jest świetne?


8

Instalowanie do C:\ProductName

Niektóre aplikacje muszą być zainstalowane C:\ProductNamelub coś podobnego, ale 99,9% (jeśli nie 100%) przykładów w sieci instaluje się C:\Program Files\CompanyName\ProductName.

Poniższego kodu można użyć do ustawienia TARGETDIRwłaściwości katalogu głównego C:dysku (pobranego z listy użytkowników 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>

UWAGA: Domyślnie TARGETDIR nie wskazuje C:\! Wskazuje raczej, ROOTDRIVEktóry z kolei wskazuje na rdzeń dysku z największą ilością wolnego miejsca ( patrz tutaj ) - i niekoniecznie jest to C:dysk. Może być inny dysk twardy, partycja lub dysk USB!

Następnie, gdzieś poniżej <Product ...>tagu, potrzebujesz następujących tagów katalogu, jak zwykle:

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

Czy nie byłoby łatwiej po prostu zainstalować WindowsVolume?
Wim Coenen

1
Tak, ale musisz użyć obejścia, ponieważ WindowsVolumewłaściwość nie może być używana jako Directory(kompilator podaje błąd / ostrzeżenie), jak wskazano tutaj i tutaj . Osobiście uważam, że to obejście jest mylące.
gehho,

7

Zmienne środowiskowe

Podczas kompilowania dokumentów Wxs do kodu wixobj możesz użyć zmiennych środowiskowych do ustalenia różnych informacji. Załóżmy na przykład, że chcesz zmienić, które pliki zostaną uwzględnione w projekcie. Załóżmy, że masz zmienną środowiskową o nazwie RELEASE_MODE, którą ustawiłeś tuż przed zbudowaniem MSI (albo za pomocą skryptu, albo ręcznie, to nie ma znaczenia). W źródle wix możesz zrobić coś takiego:

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

a następnie w kodzie użyj go w miejscu, aby w locie zmienić dokument wxs, np .:

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

1
Dostępne są również zmienne kompilacji, takie jak $ (Konfiguracja) i $ (Platforma). Także kilka innych na msdn.microsoft.com/en-us/library/aa302186.aspx
si618

1
@Si - kiedyś ten link nie był już aktywny. Nie mogłem znaleźć najnowszego.
Peter M,



7

Edycja okien dialogowych

Jedną z dobrych możliwości edycji okien dialogowych jest korzystanie z SharpDevelop w wersji 4.0.1.7090 (lub wyższej). Za pomocą tego narzędzia można otworzyć, wyświetlić podgląd i edytować samodzielne okno dialogowe (pliki wxs ze źródeł WiX, np. InstallDirDlg.wxs).


Wspaniale, nie wiedziałem, że SharpDevelop to obsługuje.
anton.burger

6

Ustawianie flagi enable32BitAppOnWin64 usług IIS 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

Zmodyfikuj „Gotowy do instalacji?” okno dialogowe (inaczej VerifyReadyDlg), aby zapewnić podsumowanie dokonanych wyborów.

Wygląda to tak:
alt text http://i46.tinypic.com/s4th7t.jpg

Zrób to za pomocą JavaScript CustomAction:


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

Zadeklaruj urząd certyfikacji JavaScript:

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

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

Dołącz urząd certyfikacji do przycisku. W tym przykładzie urząd certyfikacji jest uruchamiany po kliknięciu przycisku Dalej w oknie CustomizeDlg:

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

Powiązane pytanie SO: Jak ustawić w czasie wykonywania tekst do wyświetlenia w VerifyReadyDlg?


Z pewnością nie powinien to być JScript jako język skryptowy systemu Windows, a JavaScript jako język skryptowy DHTML. Być może trochę pedantyczny, ale dla niektórych osób może być nieco mylący.
caveman_dick

5

Umieść Komponenty, które mogą być załatane indywidualnie w ich własnych Fragmentach

Dotyczy to zarówno tworzenia instalatorów produktu, jak i poprawek, że jeśli dołączasz dowolny fragment do fragmentu, musisz uwzględnić wszystkie komponenty w tym fragmencie. W przypadku budowania instalatora, jeśli przegapisz jakieś odniesienia do komponentów, otrzymasz błąd połączenia z light.exe. Jeśli jednak utworzysz łatkę, jeśli do fragmentu dodasz odniesienie do jednego komponentu, wszystkie zmienione komponenty z tego fragmentu pojawią się w łatce.

lubię to:

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

zamiast tego:

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

Ponadto podczas łatania za pomocą tematu „Korzystanie z czysto WiX” z pliku pomocy WiX.chm, korzystając z tej procedury, aby wygenerować poprawkę:

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

nie wystarczy mieć wersję 1.1 produktu .wixpdb zbudowaną z komponentów w osobnych fragmentach. Pamiętaj więc o prawidłowym rozdrobnieniu produktu przed wysyłką.


5

Drukowanie umowy EULA z Wix3.0 i nowszych

1) Podczas kompilowania kodu źródłowego wix plik light.exe musi odwoływać się do pliku WixUIExtension.dll w wierszu polecenia. Użyj do tego przełącznika-wiersza poleceń.

2) Jeśli po dodaniu odwołania do pliku WixUIExtension.dll projekt nie zostanie skompilowany, jest to najprawdopodobniej z powodu kolizji identyfikatorów okien dialogowych, tj. Projekt używał tych samych identyfikatorów okien dialogowych, co niektóre standardowe okna dialogowe w pliku WixUIExtension.dll, podaj różne identyfikatory do swoich okien dialogowych. To dość powszechny problem.

3) Twoje okno dialogowe licencji musi mieć kontrolę ScrollableText o identyfikatorze „LicenseText”. Wix szuka dokładnie tej nazwy kontrolki podczas drukowania.

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

i PushButton, który odnosi się do akcji niestandardowej

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

4) Zdefiniuj CustomAction za pomocą Id = „PrintEula” w następujący sposób:

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

Uwaga: BinaryKey różni się w Wix3.0 w porównaniu do Wix2.0 i musi być dokładnie „WixUIWixca” (z rozróżnianiem wielkości liter).

Gdy użytkownik naciśnie przycisk, zostanie wyświetlony standardowy dialog Wybierz drukarkę i będzie można z niego drukować.


5
  • Wyświetlamy wersję produktu gdzieś (małą) na pierwszym ekranie GUI. Ponieważ ludzie za każdym razem popełniają błędy przy wyborze odpowiedniej wersji. (I każ nam programistom szukać wieków ..)

  • Skonfigurowaliśmy TFSBuild, aby generował również transformacje (pliki .mst) z konfiguracją dla naszych różnych środowisk. (Wiemy o wszystkich środowiskach, w których musimy wdrożyć).

Ponieważ oryginalny post na blogu autorstwa Granta Hollidaya nie działa, skopiowałem jego treść tutaj:


Zadanie MSBuild do generowania plików MSI Transform z XMLMarch 11 2008

W poprzednim poście opisałem, jak używać plików MSI Transform (* .mst) do oddzielania ustawień konfiguracji specyficznych dla środowiska od ogólnego pakietu MSI.

Chociaż zapewnia to pewien poziom elastyczności konfiguracji, istnieją dwa minusy plików transformacji:

  1. Są to format binarny
  2. Nie można „edytować” ani „wyświetlać” pliku transformacji. Musisz go zastosować lub utworzyć ponownie, aby zobaczyć, jakie zmiany zawiera.

Na szczęście możemy używać biblioteki obiektów Microsoft Windows Installer Object Library (c: windowssystem32msi.dll) do otwierania „baz danych” MSI i tworzenia plików transformacji.

Podziękowania należą się ponownie Alexowi Szewczukowi - od MSI do WiX - Część 7 - Dostosowywanie instalacji za pomocą Transforms, aby pokazać nam, jak to osiągnąć za pomocą VbScript. Zasadniczo wszystko, co zrobiłem, to przykład Alexa i za pomocą Interop.WindowsInstaller.dll zaimplementowałem zadanie MSBuild. Zadanie MSBuild

Pobierz kod źródłowy i przykładowe transforms.xml tutaj (~ 7 KB Spakowane rozwiązanie VS2008)



2
Przedefiniowaliśmy WelcomeDlgTitle w moim pliku lokalizacji - działa świetnie! <String Id = "WelcomeDlgTitle"> {\ WixUI_Font_Bigger} Witamy w [ProductName] [ProductVersion] Setup Wizard </String>
saschabeaumont

5

Przed wdrożeniem pakietu instalacyjnego zawsze kontroluję jego zawartość.

To tylko proste wywołanie w linii poleceń (zgodnie z postem Terrencesa) otwórz linię poleceń i wciśnij

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

Spowoduje to wyodrębnienie zawartości pakietu do podkatalogu „Wyodrębnij” z bieżącą ścieżką.


4

Zamiast ORCA użyj InstEd, który jest dobrym narzędziem do przeglądania tabel MSI. Ma także możliwość różnicowania dwóch pakietów według Transform -> Porównaj z ...

Dodatkowo dostępna jest wersja Plus z dodatkową funkcjonalnością. Ale również darmowa wersja stanowi dobrą alternatywę dla Orca.


4

Rejestrowanie zestawów .NET dla COM Interop ze zgodnością x86 / x64

Uwaga: Ten fragment jest zasadniczo taki sam jak REGASM Assembly.dll / codebase

W tym przykładzie dzieje się kilka rzeczy, więc oto kod, a wyjaśnię go później ...

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

Jeśli zastanawiasz się, tak naprawdę jest to dla sterownika teleskopu ASCOM .

Po pierwsze, skorzystałem z porady powyżej i stworzyłem niektóre zmienne platformy w osobnym pliku, możesz zobaczyć te rozproszone przez XML.

Część if-then-else u góry dotyczy zgodności x86 vs x64. Mój zestaw celuje w „Dowolny procesor”, więc w systemie x64 muszę go zarejestrować dwukrotnie, raz w rejestrze 64-bitowym i raz w Wow6432Nodeobszarach 32-bitowych . Jeśli-to-else mnie do tego skonfiguruje, wartości są foreachpóźniej używane w pętli. W ten sposób muszę tylko raz utworzyć klucze rejestru (zasada DRY).

Element pliku określa faktyczną bibliotekę DLL instalowaną i rejestrowaną:

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

Nic rewolucyjnego, ale zauważ Assembly=".net"- ten atrybut sam w sobie spowodowałby, że asembler zostałby umieszczony w GAC, co NIE jest tym, czego chciałem. Użycie tego AssemblyApplicationatrybutu, aby wskazywać na siebie, jest po prostu sposobem na powstrzymanie Wix przed umieszczeniem pliku w GAC. Teraz, gdy Wix wie, że jest to zestaw .net, pozwala mi używać pewnych zmiennych wiążących w moim pliku XML, takich jak, !(bind.assemblyFullname.filDriverAssembly)aby uzyskać pełną nazwę zestawu.


3

Ustaw DISABLEADVTSHORTCUTSwłaściwość, aby wymusić, aby wszystkie reklamowane skróty w instalatorze stały się zwykłymi skrótami i nie musisz dołączać fałszywego klucza reg, który będzie używany jako ścieżka.

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

Myślę, że wymagany jest Instalator Windows 4.0 lub nowszy .


2

To ładna struktura, ale w oparciu o moje doświadczenie zastanawiam się, jak radzisz sobie z tymi warunkami:

A. Wszystkie instalacje wydają się lądować w tym samym miejscu docelowym. Jeśli użytkownik musi zainstalować wszystkie 3 wersje jednocześnie, proces na to pozwoli. Czy potrafią jednoznacznie stwierdzić, którą wersję każdego pliku wykonywalnego uruchamiają?

B. Jak postępujesz z nowymi plikami, które istnieją w TESTIE i / lub SZKOLENIU, ale jeszcze nie w LIVE?


Cześć Blaine, A. Nie, nie mają. InstallName znajduje się w Config.wxi, który jest jedynym plikiem, do którego svn: externals nie odwołuje się. Jest to więc unikalne dla każdej instalacji, tj. Dla każdego produktu. Dlatego też modyfikujemy Przewodniki dla każdej wersji. B. GOTO A. :) Są to oddzielne pliki MSI z własnym kodem aktualizacji.
si618

1
Nawiasem mówiąc, rozumiem, dlaczego odpowiedziałeś na moje pytanie pytaniem, ale gdy zdobędziesz wystarczającą liczbę punktów rep, przenieś swoje pytanie do odpowiedzi na komentarze, w przeciwnym razie wątek będzie trudny do naśladowania.
si618

2

Oto sposób, aby pomóc dużym projektom internetowym zweryfikować, czy liczba wdrożonych plików odpowiada liczbie plików wbudowanych w MSI (lub moduł scalający). Właśnie uruchomiłem niestandardowe zadanie MSBuild na naszej głównej aplikacji (wciąż w fazie rozwoju) i wykryło sporo brakujących plików, głównie obrazów, ale kilka plików javascript przeszło do!

Takie podejście (zaglądanie do tabeli plików MSI poprzez podpięcie się do celu AfterBuild projektu WiX) może działać dla innych typów aplikacji, w których masz dostęp do pełnej listy oczekiwanych plików.


2

Przeprowadzanie wymuszonej ponownej instalacji, gdy instalacja nie pozwala na odinstalowanie lub ponowną instalację i nie przywraca.

Skrypt VBscript używany do zastępowania instalacji, która nie odinstalowuje się z jakiegokolwiek powodu.

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)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.