all of the bits and pieces
Archive for the ‘WiX’ Category
W Windows Vista(Installer 4.5) i Windows 7(installer 5.0) jest całkiem ciekawa funkcjonalność – buforowanie instalatorów msi. Rzecz całkiem przydatna, gdy niespecjalnie lubimy komunikaty w stylu “Nie można znaleźć pliku (ulubiony soft).msi”.
Read the rest of this entry »

Merge modules to plik z rozszerzniem .msm, który możemy dołączyć do naszego pliku .msi. Zaletą jest to, że na wyjściu dostajemy jeden plik Windows Installera, którego zawartość jest połączona(stąd nazwa merge module) z modułem.
Read the rest of this entry »
Jak już pewnie wiecie wersja 3.0 Windows Installer Xml jako stabilna. Oznacza to, że nie będzie już żadnych rewolucyjnych zmian w drzewie 3.0 poza poprawkami krytycznych błędów. Numer builda to 3.0.5419.0. Więcej można przeczytać na blogu Roba.
Wersja 3.5
Teraz rozpoczęły się pracę nad wersją 3.5 WiX, głównym dodatkiem będzie Burn, czyli bootstrapper(chainer) dla msi. Z tego co pisał Rob na swoim blogu wynika, że będzie można zupełnie ukryć windows installera pod swoim własnym wyglądem. Będzie można też prosto instalować pakiety zależne, a nawet je ściągnąć z sieci. Będzie wreszcie elegancki sposób na zastąpienie własnych bootstrapperów – tak jak to opisałem w jednym z poprzednich wpisów. Więcej o burn można znowu poczytać na blogu Roba.
Jako mały test zainstalowałem już wersję 3.5, działa zupełnie poprawanie, oglądałem też sposób generowania bootstrappera za pomocą manifestów xmlowych i spróbuje go użyć jak tylko wyjdzie jakaś w miarę stabilna wersja burna. Przy okazji stworzyłem instalator dla BlipFace (wpfowego klienta dla blipa), jest do znalezienia na githubie. Tak na marginesie to było moje pierwsze użycie gita muszę przyznać że filozofia odrobinę inna niż w svn, ale udało mi się szybko dostosować.
O tym, że można wykryć czy .NET albo dowolny inny pakiet jest zainstalowany pisałem w jednym z poprzednich wpisów. Pisałem również, że z poziomu MSI nie można odpalać instalacji innych pakietów.
Dzisiaj pokaże jak szybko stworzyć bootstrappera do plików MSI. W tym celu będę używał bootstrappera znanego z ClickOnce.
Pierwszym krokiem jest wyedytowanie pliku .wixproj i dodanie następującego kawałka kodu:
<ItemGroup>
<BootstrapperFile Include="Microsoft.Net.Framework.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5</ProductName>
<Install>true</Install>
</BootstrapperFile>
</ItemGroup>
<Target Name="AfterBuild">
<GenerateBootstrapper
ApplicationFile="WixProject3.msi"
ApplicationName="WixProject3"
BootstrapperItems="@(BootstrapperFile)"
OutputPath=".\bin\Debug"
ComponentsLocation="Relative"
Culture="en"
Path="C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper"
/>
</Target>
I to właściwie wszystko! Po uruchomieniu budowania projektu WiX w folderze bin\Debug znajdziemy setup.exe, który po uruchomieniu sprawdzi i ewentualnie zainstaluje .NET 3.5, a później odpali plik WixProject3.msi.
Zawartość folderu bin\Debug wygląda mniejwięcej tak:
zwróćcie uwagę, że wszystkie pakiety od .neta też wylądowały w tym folderze. Stało się tak ponieważ użyłem ComponentsLocation=”Relative”, ale można również ustawić żeby pobierał się z sieci (więcej o GenerateBootstrapperTask).
Teraz całość (oprócz .wixpdb) możemy zapakować, najlepiej jakiś selfextractorem ( ja lubie iexpress ) i ustawić wywołanie setup.exe po rozpakowaniu. Trzeba tylko uważać na rozmiar, gdy jeszcze dorzucimy crystala i sql może być już całkiem spory.
Przedstawione przezemnie rozwiązanie ma pewne niedogodności:
- boostrapper clickonce nie ma polskiej lokalizacji, jednak stosunkowo prosto można to załatwić tłumacząc odpowiednie pliki xml
- w przypadku jakiś niestandardowych (albo własnych) zależności będziemy musieli je dodać, tak żeby były widoczne w deploymencie clickonce we właściwościach projektu. Na szczęście to też można załatwić w miarę szybko.
Miłej zabawy!
PS. Zastanawiam się, czy by nie opowiedzieć o WiX w ramach Speaker Idola na MTS 2009 – jak myślicie warto ? czy temat zainteresuje publikę ? Na pewno warto zwrócić na niego uwagę szerszej publiczności, bo przez sam Microsoft jest traktowany po macoszemu, pewnie dlatego że jest open source
Miła dla mnie wiadomość na początek czerwca – w numerze 06\2009 Software Developer’s Journal ukazał się artykuł o WiX mojego autorstwa:
Przy okazji małe sprostowanie w momencie pisania artykułu WiX miał być częścią VS 2010 później to się zmieniło.
Jeśli chodzi o zawartość artykułu – to jeśli czytasz regularnie mojego bloga nie będzie w nim dla Ciebie żadnych niespodzianek.
Czasami przy instalacji trzeba stwierdzić czy zainstalowano już jakiś inny MSI. Może to być .NET, serwer SQL, czy może jakiś zupełnie inny produkt.
.NET
Z samym .NETem sprawa jest dosyć prosta. Wystarczy, że użyjemy biblioteki WixNetFxExtension.dll, która realizuje potrzebną nam funkcjonalność:
<PropertyRef Id="NETFRAMEWORK35"/>
<Condition Message=".NET Framework w wersji 3.5 musi być zainstalowany ([NETFRAMEWORK35])">
NETFRAMEWORK35
</Condition>
Użycie PropertyRef jest konieczne, aby zastała zaimportowana odpowiednia wartości Property z WixNetFxExtension.dll. Dalej już jest prosto definiujemy element Condtion w InnerText wpisujemy warunek, który musi być ustawiony na TRUE, aby mogła rozpocząć się instalacja – całość ląduje w tabeli LaunchCondition. Własność NETFRAMEWORK35 jest ustawiona na #1 jeśli jest zainstalowany .NET Framework 3.5 . W NetFxExtension jest ustawianych bardzo dużo własności, które mogą nas poinformować o wersji SP, a nawet czy zainstalowano Client Profile. Pełny opis w c:\program files\Windows Installer Xml v3\doc\Wix.chm – hasło “WixNetfxExtension”.
Inne pakiety
Żeby wykryć inne zainstalowane pakiety można użyć np.: RegistrySearch:
<Property Id="NETFRAMEWORK20">
<RegistrySearch Id="NetFramework20" Root="HKLM" Key="Software\Microsoft\NET Framework Setup\NDP\v2.0.50727" Name="Install" Type="raw" />
</Property>
Co ustawi, własność NETFRAMEWORK20 na wynik wyszukiwania wartości klucza w rejestrze – tak naprawdę taką metodą posługuje się NetFxExtension.
Innną metodą jest wyszukiwanie konkretnego zainstalowanego komponentu za pomocą ComponentSearch:
<Property Id="VS2005PROJECTAGGREGATOR2">
<ComponentSearch Id="VS2005ProjectAggregator2Search" Guid="B0BB80E0-5CCC-474E-A75E-05DC1AE073BC" />
</Property>
Musimy w takiej sytuacji znać Guid komponentu.
Inne możliwości to:
- IniFileSearch – pozwala szukać pliku .ini lub konkretnej jego zawartości, to jest trochę przestarzała metoda, ponieważ tych plików już tak często się nie używa
- DirectorySearch – wyszukiwanie konkretnego folderu
- FileSearch – wyszukiwanie konkretnego pliku
Instalacja zależnych pakietów
W samym pakiecie MSI nie możemy wywoływać instalacji innych pakietów MSI, jest to zabronione. Możemy do naszego instalatora dołączyć tzw. Merge Module, czyli plik z rozszerzeniem .msm, ale takie pliki rzadko są dostarczane przez producentów.
Jedyną skuteczną metodą na zainstalowanie np. .NETa przed instalacją naszej aplikacji jest użycie tzw. MSI chainera, który instaluje pliki .MSI jeden po drugim. Można też posłużyć się czymś bardziej zaawansowanym – bootstrapperem od clickonce. Temat chainera i bootstrappera to sprawa na osobny wpis, a właściwie całą serię.
Nie wiem czy zauważyliście, ale przy domyślnie stworzony instalatorach w WiX okno postępu instalacji nigdy nie wyświetla dokładnie jakie pliki, klucze rejestru są zakładane i kopiowane, wyświetla się tylko dosyć ogólna informacja “Kopiowanie nowych plików”:
Na przykład instalator od resharpera wyświetla bardzo sczegółowe info o tym co się dzieje:
Żeby dodać podobny efekt, musimy zmodyfikować interfejs użytkownika dodać nową kontrolkę i zasubskrybować jej zawartość do zdarzenia ActionData:
<Control Id="ActionData" Type="Text" X="59" Y="165" Width="273" Height="41" Transparent="yes" TabSkip="no">
<Subscribe Event="ActionData" Attribute="Text" />
</Control>
Niby prosta modyfikacja, ale wymaga albo zbudowania WiXa od nowa(chociaż UIExtension) albo dodania całego UI do swojego projektu instalatora – co ma niewątpliwe zalety na przyszłość, będzie można trochę poszaleć i np stworzyć coś podobnego do resharpera. BTW instalator resharpera jest dobrym miejscem do podejrzenia jak zrobić parę ciekawych rzeczy w wix – więc orca w dłoń i do roboty.
Bartek zadał pytanie:
Od pewnego czasu szukam narzędzia do robienia instalek (lepszego niż z VS) testowałem już advanced installer i instalshield teraz znalazlem twoj opis WIX ale żedne z tych narzędzi nie integruje się z VS2008 express. Moje pytanie "Czy jest na to jakiś sposób" czy też muszę przejść na wyższą pułkę?
Wtyczka Votive, która dodaje nowy typ projektu – WixProj i pozwala na uruchomienie Candle i Light z poziomu Visual Studio niestety nie działa w wersji Express. Wersja ta ma wogóle wyłączoną możliwość instalacji dodatków( poza chlubnym wyjątkiem jakim jest TestDriven.net). Brak Votive jednak nie uniemożliwia użycia Wixa.
Wtyczka votive uruchamia aplikacje Candle i Light. Możemy zrobić to samo posługując się “Post build event” we właściwościach projektu:
A plik buildmsi.bat może mieć postać zbliżoną do:
"%wix%bin\candle.exe" -out obj\Debug\Product.wixobj -arch x86 -ext "%wix%bin\WixUIExtension.dll" Product.wxs
"%wix%bin\Light.exe" -cultures:pl-PL -ext "%wix%WixUIExtension.dll" -out bin\Debug\WixProject2.msi -pdbout bin\Debug\WixProject2.wixpdb obj\Debug\Product.wixobj
W zależności od tego w jaki sposób zbudowaliśmy Product.wxs do candle może trzeba będzie podać dodakowe komendy -Dnazwaparametru=wartosc. Zmienna %Wix% jest ustawiana przez instalator podczas konfigurowania Wixa.
W poprzednich częściach o WiX:
Jakiś czas temu Szymon Kobalczyk i Marcin Książek pytali o możliwość automatycznego tworzenia skryptów WxS w oparciu o stworzone przez projekt. Taki mechanizm byłby szczególnie przydatny dla projektów, w których często zmieniają się pliki, które muszą być zainstalowane np: dla aplikacji webowych. Gdy zajrzałem do jednego ze starych projektów stwierdziłem, że używałem do tego dosyć rozbudowanego skryptu Wsh.
W wix3 istnieje całkiem przydatny program narzędziowy, który pozwala właśnie na stworzenie pliku wxs. Aplikacja nazwya się heat. Przy wywoływaniu heat-a ważny jest tzw. harvester, czy zbieracz plików. Przykładowymi harvesterami jest directory, który zbiera wszystkie pliki z folderu oraz project, który tworzy wxs na podstawie wyniku projektu z VS.
Przykładowe wywołanie może wyglądać tak:
heat.exe project myproj.csproj -sfrag -srd -gg -suid -pog:Binaries -pog:Content -out Files.wxs
Omówie po kolei wszystkie parametry:
- project – określa typ harvestera, następna jest nazwa pliku projektu
- sfrag – powoduje, że nie są generowane osobne fragmenty dla każdego pliku
- gg – generuje odrazu guidy dla componentów
- suid – powoduje, że pola Id dla komponentów i plików nie są unikalnymi guidami, co jest całkiem przydatne w sytuacji gdy chcielibyśmy w innym miejscu odwołać się do plików
- pog:Binaries – określa, które grupy wyjścia mają być uwzględnione w pliku, w przykładzie wybrałem Binaria, parametr pog można powtarzać
- out Files.wxs – plik, do którego będzie generowanie wyjście
Plik Files.wxs będzie miał taką postać:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="c2csi.Binaries">
<Component Id="c2csi.Binaries.c2csi.exe" Guid="{0CCD4E1F-0AD8-40F3-B741-7A11E3E70D99}">
<File Id="c2csi.Binaries.c2csi.exe" Source="$(var.c2csi.TargetDir)\c2csi.exe" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="c2csi.Binaries">
<ComponentRef Id="c2csi.Binaries.c2csi.exe" />
</ComponentGroup>
</Fragment>
<Fragment>
<DirectoryRef Id="c2csi.Content">
<Component Id="c2csi.Content.XMLFile1.xml" Guid="{41E641F5-C0CA-470B-A7F2-73EF37B49A5F}">
<File Id="c2csi.Content.XMLFile1.xml" Source="$(var.c2csi.ProjectDir)\XMLFile1.xml" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="c2csi.Content">
<ComponentRef Id="c2csi.Content.XMLFile1.xml" />
</ComponentGroup>
</Fragment>
</Wix>
Dodatkowo do tego główny skrypt:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="00225fb3-42b1-4404-a595-f9a1c9ad5d16" Name="WixProject2" Language="1045" Version="1.0.0.0" Manufacturer="WixProject2" UpgradeCode="4dd7c3bd-1d88-4d2b-8da9-fd39b11b2653" Codepage="1250">
<Package InstallerVersion="200" Compressed="yes" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="c2csi.Binaries" Name="WixProject2">
</Directory>
</Directory>
</Directory>
<Feature Id="ProductFeature" Title="WixProject2" Level="1">
<ComponentGroupRef Id="c2csi.Binaries"/>
</Feature>
</Product>
</Wix>
Trzymanie plików w osobnym skrypcie WxS jest dobrą praktyką, natomiast z automatycznym generowaniem należy być ostrożnym może to spowodować nieprzewidziane problemy.
Uruchamianie heat warto wrzucić do before build action do projektu wix.
I was trying to write a post on generating wxs scripts with heat. But i think I found a bug. Below more info – maybe someone can verify.
I’ve got a following directory contents:
And I run heat with following parameters:
heat dir . -dr D_INSTALLLOCATION -sfrag -srd -gg -suid -cg test -out test.wxs
I get following wxs:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<ComponentGroup Id="test">
<ComponentRef Id="" />
<ComponentRef Id="" />
<ComponentRef Id="" />
<ComponentRef Id="" />
</ComponentGroup>
</Fragment>
<Fragment>
<DirectoryRef Id="D_INSTALLLOCATION">
<Component Id="c2csi.exe" Guid="{43B03251-B0EE-4273-8E3C-DE54EAD0A263}">
<File Id="c2csi.exe" KeyPath="yes" Source="SourceDir\c2csi.exe" />
</Component>
<Component Id="c2csi.pdb" Guid="{5B925509-EB62-461F-BEB8-FC4C8C99C290}">
<File Id="c2csi.pdb" KeyPath="yes" Source="SourceDir\c2csi.pdb" />
</Component>
<Component Id="c2csi.vshost.exe" Guid="{048CE65A-FCD9-4A05-B482-45F326F40199}">
<File Id="c2csi.vshost.exe" KeyPath="yes" Source="SourceDir\c2csi.vshost.exe" />
</Component>
<Component Id="test.wxs" Guid="{90927E13-B1AD-48D2-AADA-0C227CD2DC36}">
<File Id="test.wxs" KeyPath="yes" Source="SourceDir\test.wxs" />
</Component>
</DirectoryRef>
</Fragment>
</Wix>
Notice the ComponentGroup – it has empty Ids in ComponentRef.
Why would I want to generate non unique File Ids and put them in ComponentGroup?
The answer is: I would put ComponentGroupRef in Feature and use references to File element to create for example a shortcut in start menu.
Now when I removed –suid when calling heat it generated wxs with unique component and file ids which are impossible to reference in the main script.
Is it a bug or should I take a completly different approach?
UPDATE: when used project harvester with the same paramaters .wxs was ok:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="c2csi.Binaries">
<Component Id="c2csi.Binaries.c2csi.exe" Guid="{22D0EAD2-B33C-4D1D-B5F2-FA046CF79649}">
<File Id="c2csi.Binaries.c2csi.exe" Source="$(var.c2csi.TargetDir)\c2csi.exe" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="c2csi.Binaries">
<ComponentRef Id="c2csi.Binaries.c2csi.exe" />
</ComponentGroup>
</Fragment>
</Wix>