Artykuł

flickr.com/photos/dbreg2007/4376127476 flickr.com/photos/dbreg2007/4376127476
lis 15 2013
0

WiX Toolset - tworzymy własny instalator - cz. 1

Programując w domu w C#, przez kilka ostatnich lat korzystałem głównie z Visual Studio 2010, którego to pozyskałem jeszcze w studenckich latach. Minęło już więc trochę czasu i stwierdziłem, że najwyższa pora na zmianę. Postanowiłem że skuszę się na najnowszy zestaw edycji typu Express.

Pierwsze spostrzeżenie było takie, że sporo się tu zmieniło w interfejsie od czasu wersji 2010. Oczywiście widziałem wcześniej screeny z dwóch najnowszych edycji oraz krótko nawet je testowałem, ale w praktyce nie jest to, to samo co konkretne programowanie przy określonym projekcie. Interfejs VS to kluczowy element, który zmienił się na przestrzeni tych ostatnich lat, ale oczywiście nie jest to jedyna modyfikacja. Doszło sporo nowych funkcjonalności zarówno w samym IDE jak i kolejnych wersjach .Net Frameworku. Pojawiło się również kilka zmian, które są ważne, choć prawdopodobnie nie zobaczycie ich przy pierwszych 5 minutach pracy. Jedna z tego typu korekt, zabolała mnie szczególnie. Chodzi mi o usunięcie wsparcia dla projektów instalatorów vdproj.

Pytam więc - jak żyć drogi Microsofcie? I w ten usłyszałem głos, zaprzyjaźnić się z Wixem...

WiX Toolset - poznajmy się

Po wycofaniu przez Microsoft w Visual Studio 2012 projektów instalatora vdproj, to właśnie WiX Toolset (przez resztę tekstu będę go nazywał w skrócie WiX) stał się rekomendowanym przez giganta z Redmond rozwiązaniem wspomagającym tworzenie instalatorów. Microsoft sam wykorzystuje WiXa na co dzień i przykładów potwierdzających tą tezę nie trzeba daleko szukać. Wystarczy tylko spojrzeć na instalator najpopularniejszego na świecie pakietu biurowego. Ten z wersji 2007 skonstruowany był właśnie przy pomocy WiXowych XMLi.

Skoro wspomniałem już wyżej o XMLach, to przyszła najwyższa pora by powiedzieć o tym jak to wszystko działa. Całość projektu w tym przypadku opiera się właśnie o tego rodzaju plik, w którym znajdują się zapisy mówiące co, gdzie i jak ma być zainstalowane na komputerze użytkownika. Plik konfiguracyjny oraz wszystkie pliki instalowane na komputerze użytkownika, są kompilowane oraz linkowane przez specjalne narzędzia dostarczone razem z Toolsetem.

W wyniku tego procesu dostajemy plik msi, który wykorzystuje instalator Windows. Sumarycznie uzyskujemy więc coś bardziej na kształt bazy danych, w której znajdziemy zapisy mówiące o tym jak wykorzystać instalator Windows do zainstalowania stworzonej przez nas aplikacji.

Pobieranie oraz instalacja

Najnowszą wersję WiXa można pobrać oczywiście z oficjalnej strony projektu. W tym miejscu niestety pojawia się kilka niedomówień, ponieważ na liście wydań można znaleźć zarówno wersję 3.x oraz 4.x. Z tego co widać na stronie, to oficjalnie wspierana jest ta pierwsza. Nie wiem czy wersja 4.x to bardziej melodia przyszłości?

Obecnie lepiej jest korzystać z linii 3.x, ponieważ posiada ona pełną dokumentację. Zasadnicza różnica, którą można zauważyć pomiędzy obiema liniami to zmieniona przestrzeń nazw, którą określamy w pliku XML definiującym proces instalacji. W niniejszym tekście będę operować na wersji z linii 3.x.

Wix Toolset potrafi się również zintegrować z Visual Studio. Oczywiście w tym przypadku konieczne jest posiadanie wersji innej niż Express, która potrafi wspierać narzędzia zewnętrzne. W moim przypadku został więc czysty XML;-)

Do pracy z pakietem konieczne jest zainstalowanie .Net Frameworka. Nie jest on jednak wymagany na stacjach roboczych użytkowników końcowych, którzy będą korzystać z naszych plików msi. Oczywiście to czego wymaga instalowana przez nas docelowo aplikacja, to już zupełnie inna bajka;-)

Kompilacja oraz linkowanie

Zanim uzyskamy docelowy plik msi, będziemy musieli się trochę napracować. Oprócz stworzenia głównego pliku WXS (plik o strukturze XMLa), będziemy musieli jeszcze przeprowadzić proces kompilacji oraz linkowania. Oczywiście najdłużej będzie trwać przygotowanie pliku zawierającego opis instalacji, ale o tym później.

Kompilacje oraz linkowanie wykonujemy za pomocą narzędzi (odpowiednio) candle oraz light. Oba znajdziemy w podkatalogu bin, który z kolei znajduje się w katalogu głównym całej paczki. W tej samej lokalizacji znajdziemy również podstawowe rozszerzenia (biblioteki DLL), które możemy wykorzystać przy pracy nad projektem instalatora (warto zatem rozważyć dodanie tej lokalizacji do zmiennej środowiskowej path, dzięki czemu uzyskamy szybki dostęp do narzędzi WiXa z dowolnego miejsca na komputerze). Temat samych rozszerzeń jest odrobinę bardziej złożony, dlatego wrócimy do niego dopiero prawdopodobnie w kolejnej części tekstu.

Spójrzmy teraz na prosty przykład. Zakładając że nasz opis instalacji znajduje się w pliku Setup.wxs, wywołanie dla kompilatora wygląda następująco:

candle Setup.wxs

Prosto - prawda? Oczywiście ten schemat zadziała, gdy dodaliśmy katalog bin do systemowej ścieżki path, lub gdy wszystkie pliki WiXa oraz naszego projektu znajdują się w jednej lokalizacji. Nie muszę chyba mówić, że drugie rozwiązanie jest bardziej niż drętwe;-)?

Jeśli nie chcecie, lub nie możecie dodać WiXa do zmiennej path, to zawsze możecie podać ścieżki bezwzględne do wszystkich elementów. Oczywiście przy pierwszych projektach instalatorów istnieje wysokie prawdopodobieństwo, że projekt będziecie budować więcej niż kilka-kilkanaście razy, dlatego w takim przypadku stworzenie skryptu bat/cmd, jest bardziej niż zalecane;-)

Wróćmy jednak do samego procesu tworzenia. Po pomyślnej kompilacji uzyskamy plik z rozszerzeniem *.wixobj. Domyślne reguły są takie, że ma on taką samą nazwę jak plik *.wxs, więc w naszym przypadku powinniśmy się spodziewać pliku Setup.wixobj. Nowy plik potrzebny będzie oczywiście w wywołaniu linkera:

light Setup.wixobj -cultures:pl-PL -out TestSetup.msi

W przypadku tego wywołania pojawiły się dwa dodatkowe parametry. Po pierwsze - przy linkowaniu możemy wskazać ustawienia regionalne. Jeśli nie chcemy domyślnego języka angielskiego, warto pochylić się bliżej nad tym zagadnieniem (różne dostępne wartości znajdziecie np. tutaj). W tym przypadku wstawiłem oczywiście nasze rodzime ustawienie. Ostatni parametr determinuję ścieżkę/nazwę dla pliku wyjściowego. Gdybyśmy nie podali tego parametru, to wyjściowy plik nazywałby się po prostu Setup.msi i trafiłby do tego samego katalogu, w którym znajdował się linkowany element.

Jeśli wszystko pójdzie w porządku i oba narzędzia nie rzucą żadnego błędu, to w wyniku ich działania powinniście otrzymać gotowy plik msi. Ale to oczywiście za chwilę, jak tylko spreparujemy nasz pierwszy projekt;-)

Pierwszy projekt

Nasz pierwszy projekt będzie bardzo skromny, bo zawierać będzie zaledwie około 40 liniowy XML;-) Za jego pomocą będziemy w stanie:

  • Wrzucić plik wykonywalny aplikacji, do wskazanego w projekcie folderu w Program Files
  • Dodać skrót do aplikacji na pulpicie oraz w menu start
  • Dodać klucz do rejestru
  • Odinstalować aplikację w razie potrzeby;-)

Niestety nasz instalator nie będzie user-friendly i nie pozwoli użytkownikom wdać się w jakiekolwiek interakcje. No dobra - skłamałem... Jak będziecie mieć szczęście to przez jakaś sekundę, powinniście zobaczyć ekran mniej, lub bardziej podobny do tego widocznego poniżej na screenie.

Obiektem naszych manewrów będzie moja aplikacja Multime. Powoli zaczynam rozwijać ją dalej, a jednym z pierwszych kroków jest przejście właśnie na instalator WiXa.

W dzisiejszym odcinku kursu zainstalujemy tylko plik wykonywalny. W ten sposób aplikacji nie uda się uruchomić poprawnie, ale w sposób oczekiwany powinien za to zadziałać nasz instalator. Wszystkie niezbędne pliki (w tym pliki wynikowe) możecie pobrać stąd.

Plik Setup.wxs będziemy budować kawałek po kawałku, a kolejne elementy jego listingu będę dla uproszczenia widoku justował do lewej strony (poprawnie zbudowany XML można upiększyć kilkoma kliknięciami choćby w Notepadzie++).

Na początek rozpoczynamy plik i definiujemy podstawowe dane produktu:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
	<Product Name="Multime" Id="D819C0F2-9DC4-4007-BDC1-801E4DC06656" UpgradeCode="68265506-3FF2-486D-99E5-A5C861C04422" 
		Language="1045" Codepage="1250" Version="1.0.0" Manufacturer="Jerzy Piechowiak">

W tym momencie warto zwrócić uwagę na kilka rzeczy:

  • W węźle Product definiujemy podstawowe dane produktu:
    • Id - unikalny id produktu/programu (stały w czasie)
    • UpgradeCode - unikalny kod wykorzystywany przy aktualizacji oprogramowania (stały w czasie)
    • Language & Codepage - kod języka oraz numery strony kodowej używanej w instalatorze. W tym przypadku podałem ustawienia dla języka polskiego. Dostępne wartości, które można w tym miejscu ustawić, znajdziecie tutaj

Pozostałe atrybuty wydają mi się być na tyle jasne, że pominąłem ich opis - podobnie będę czynił w dalszej części tekstu. Jak pewnie zauważyliście na listingu, w kodzie pojawiają się GUIDy. GUIDów będzie docelowo znacznie więcej i dla własnych instalacji najlepiej wygenerować unikalne kody. Na szybko można to zrobić np. tutaj.

W kolejnym kroku tworzymy paczkę. Paczka za każdym razem musi posiadać unikalny id, więc dla uproszczenia tylko w tym jednym miejscu można podać *, którą WiX automatycznie zastąpi unikalnym guidem.

<Package Id="*" Keywords="Instalator" Description="Instalator Multime" 
	Comments="Multime - aplikacja do masowej zmiany nazwy plików oraz katalogów" Manufacturer="Jerzy Piechowiak" 
	InstallerVersion="100" Languages="1045" Compressed="yes" SummaryCodepage="1250" />

Wszelkie atrybuty opisowe możecie tutaj zmienić według własnego upodobania. Ponownie pojawiają się tutaj atrybuty związane z ustawieniami kulturowymi, dodatkowo pojawia się również flaga Compressed, która determinuje czy paczka ma być spakowana, czy też nie.

W kolejnym kroku określamy Media. Ten element miał swego czasu duże znaczenie, w momencie gdy mieliśmy np. instalatory operujące na kilku płytach. Kojarzycie te wszystkie instalatory, gdzie na kolejnych nośnikach znajdowały się wielkie pliki cab? No właśnie, oto tu właśnie chodzi, czyli o określenie składowych - części instalatora. W naszym przypadku tworzymy prosty, malutki instalator, w którym plik cab wcielimy do wnętrza pliku msi:

<Media Id="1" Cabinet="Multime.cab" EmbedCab="yes" />

W kolejnym kroku przygotujemy strukturę katalogów:

<Directory Id="TARGETDIR" Name="SourceDir">
	<Directory Id="ProgramFilesFolder" Name="PFiles">
		<Directory Id="JerzyPiechowiak" Name="Jerzy Piechowiak">
			<Directory Id="INSTALLDIR" Name="Multime">

Na samym początku, zawsze powinien znajdować się katalog o id TARGETDIR o predefiniowanej nazwie SourceDir. Jest to taki swego rodzaju początek wszystkiego w WiXie. Później tworzymy już normalną strukturę, zaczynają od katalogu Program Files. Tu kolejny niuans, ponieważ w tym przypadku również wykorzystujemy ustaloną wcześniej wartość id ProgramFilesFolder. Wartość ta odnosi się do zmiennej systemowej wskazującej na lokalizację folderu Program Files na komputerze użytkownika. Jest to ważne, ponieważ w różnych wersjach językowych katalog ten może się różnie nazywać, a w architekturze 64 bitowej mamy nawet dwa foldery tego typu w systemie. Podobne identyfikatory użyjemy dla pulpitu (DesktopFolder) oraz menu start (ProgramMenuFolder).

Id INSTALLDIR dotyczy już z kolei głównego katalogu instalacji i będziemy się do niego odnosić kilka razy później. W przypadku katalogów w których podajemy nasze unikalne ID, możemy podać również własne wartości pól Name, które posłużą jako rzeczywiste nazwy folderów w tworzonej przez nas strukturze.

Po zdefiniowaniu struktury katalogów możemy przystąpić do stworzenia pierwszego komponentu, który zgodnie z tym co napisałem wcześniej zawierać będzie tylko jeden plik. Oczywiście w ramach ćwiczeń możecie dodać więcej własnych plików;-)

			<Component Id="MainExecutable" Guid="B35DD4D4-2918-4A65-BA4E-7C8B420C5BC9">
				<File Id="MultimeEXE" Name="Multime.exe" Source="Multime.exe" KeyPath="yes">
					<Shortcut Id="StarMenuMultime" Directory="ProgramMenuDir" Name="Multime" 
						WorkingDirectory="INSTALLDIR" Icon="MultimeIco.exe" IconIndex="0" Advertise="yes" />
					<Shortcut Id="DesktopMultime" Directory="DesktopFolder" Name="Multime" 
						WorkingDirectory="INSTALLDIR" Icon="MultimeIco.exe" IconIndex="0" Advertise="yes" />
				</File>
			</Component>
		</Directory>
	</Directory>
</Directory>

Warto pochylić się chwilę nad powyższym fragmentem kodu. Po pierwsze określamy nazwę docelową pliku (atrybut Name, ścieżkę do pliku względem skryptu instalacyjnego (Source) oraz opcjonalnie definiujemy atrybut KeyPath. Atrybut ten musi zawsze wystąpić gdzieś wewnątrz komponentu i wskazywać określony obiekt. Na podstawie tego wskazania, instalator Windows jest w stanie określić czy dany komponent jest zainstalowany, czy też nie. W tym przypadku instalator będzie szukać pliku Multime.exe.

Wewnątrz węzła File tworzymy skróty, które umieścimy na pulpicie oraz w menu start. W ich konstrukcji odwołujemy się do ikony, której szczegóły określimy w dalszej części listingu. Na temat atrybutu advertised, poczytacie więcej tutaj.

W kolejnym kroku tworzymy katalogi odpowiedzialne za menu start oraz pulpit, do których za pomocą ID odwoływaliśmy się już w poprzednim komponencie:

	<Directory Id="ProgramMenuFolder" Name="Programs">
		<Directory Id="ProgramMenuDir" Name="Multime">
			<Component Id="ProgramMenuDir" Guid="16FE7C9C-1489-436B-87CF-9B71186187A3">
				<RemoveFolder Id="ProgramMenuDir" On="uninstall" />
				<RegistryKey Root="HKCU" Key="Software\[Manufacturer]\[ProductName]">
					<RegistryValue Type="string" Value="" KeyPath="yes" />
				</RegistryKey>
			</Component>
		</Directory>
	</Directory>
	<Directory Id="DesktopFolder" Name="Desktop" />
</Directory>

Warto teraz zwrócić uwagę na kilka nowych elementów:

  • W linii 4 wykorzystujemy konstrukcję RemoveFolder, która usuwa folder z menu start w przypadku akcji odinstalowania (atrybut On, wartość Uninstall)
  • W liniach 5-7 tworzymy węzeł RegistryKey, który odpowiedzialny jest za dodanie określonych wartości do rejestru. Dodajemy pustą wartość (6), dzięki której instalator wykryje obecność tego komponentu w systemie - mamy tutaj wspominany wcześniej atrybut KeyPath

Przy okazji definicji komponentu, warto powiedzieć sobie o kilku rzeczach:

  • Komponentów w jednym projekcie może być bardzo dużo, WiX jest w stanie wszystko to przetworzyć niezależnie od ilości
  • Komponenty są ważne, ponieważ później dzięki nim można tworzyć niestandardowe rodzaje instalacji, w których użytkownik wybiera tylko niezbędne mu komponenty
  • Każdy komponent musi posiadać unikalny GUID

Nieubłaganie zbliżamy się do końca listingu, więc przyszła najwyższa pora aby dodać ficzery. Każdy z węzłów typu Feature określa grupę komponentów, które mają zostać zainstalowane. W naszym przypadku zastosujemy jeden węzeł tego rodzaju, dlatego umieścimy w nim wszystkie utworzone komponenty, które zawsze będą się instalować. W drugiej części tutorialu powiem Wam nieco więcej na ten temat.

<Feature Id="Complete" Level="1">
	<ComponentRef Id="MainExecutable" />
	<ComponentRef Id="ProgramMenuDir" />
</Feature>

Zwieńczeniem listingu jest ikona programu, o której wspominałem Wam wcześniej. Ponieważ plik wykonywalny jest mały, wskazujemy tutaj ponownie na niego. Nie jest to do końca zalecane podejście, ponieważ prowadzi do zdublowania określonego zasobu w pliku instalatora. Często można się jednak z nim spotkać właśnie w sytuacji gdy główny plik jest niewielkich rozmiarów.

<Icon Id="MultimeIco.exe" SourceFile="Multime.exe" />
	</Product>
</Wix>

To już wszystko co chciałem przedstawić w pierwszej części tekstu o WiXie. Mam nadzieję, że nie zrobiłem żadnych błędów merytorycznych. Niestety dokumentacja toolsetu nie jest zbyt pomocna i wiele ważnych kwestii nie jest tam należycie wyjaśnionych, dlatego pomocy trzeba szukać w wielu innych źródłach.

Tak jak wspomniałem wcześniej, pliki użyte w projekcie możecie pobrać z działu download.

Aktualizacja - zmiana wersji toolsetu

Ponieważ po premierze tekstu pojawiła się stabilna wersja 3.8, zaktualizowałem wpis oraz przedstawiony w tekście kod by korzystał właśnie z tego rozwiązania. W nowszych wersjach beta z linii 4.x, pojawiły się (przynajmniej u mnie) pewne problemy z instalatorem.

Tekst powstał dzięki:

  • Świetnemu tutorialowi WiXa, na którym sam się uczyłem (i dalej to robię) i wzorowałem
  • Oficjalnej stronie projektu, której dokumentacje lepiej przeszukiwać za pomocą Google;-)
  • Google
  • Eksperymentom własnym

Data ostatniej modyfikacji: 08.01.2014, 22:09.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus