BETA
Aby się zalogować, najpiew wybierz portal.
Aby się zarejestrować, najpiew wybierz portal.
Podaj słowa kluczowe
Słowa kluczowe muszą mieć co najmniej 3 sąsiadujące znaki alfanumeryczne
Pole zawiera niedozwolone znaki

Baza wiedzy











Windows Installer - Instalatory, Setupy...

30-12-2006 23:27 | mnova
Niniejszy artykul poswiecony jest tematowi wdrozenia aplikacji lub komponentow w srodowisku Windows XP, Windows 2000 oraz Windows NT, przy wykorzystaniu narzedzia IDE jakim jest Visual Studio 2005 lub 2003 firmy Microsoft. Kazdy program, aplikacja lub serwis, z wyjatkiem tych typu Stand-Alone, wymaga zazwyczaj zainstalowania w systemie, czyli utworzenia odpowiednich katalogow, skopiowania niezbednych plikow, dokonania zapisow w rejestrach, a niekiedy skorzystania z zewnetrznych systemow (np. baz

 Windows Installer - Instalatory, Setupy...

 

 

1      Wstęp

2      Windows Installer

3      Tworzenie pakietu MSI

3.1       Przygotowanie aplikacji

3.2       Oprogramowanie interfejsu Windows Installer API

3.3       Testowanie instalatora za pomocą InstallUtil.exe

3.4       Przygotowanie Setup and Deployment Project

3.4.1        Edytor File System

3.4.2        Edytor Registry

3.4.3        Edytor User Interface

3.4.4        Edytor Custom Actions

3.4.5        Edytor Lunch Conditions

3.5       Edycja pakietu MSI za pomocą narzędzia Orca.exe

4      Setup Project i Unmanaged Code

5      Podsumowanie

Bibliografia

 

  

1      Wstęp

Niniejszy artykuł poświęcony jest tematowi wdrożenia aplikacji lub komponentów w środowisku Windows XP, Windows 2000 oraz Windows NT, przy wykorzystaniu narzędzia IDE jakim jest Visual Studio 2005 lub 2003 firmy Microsoft. Każdy program, aplikacja lub serwis, z wyjątkiem tych typu Stand-Alone, wymaga zazwyczaj zainstalowania w systemie, czyli utworzenia odpowiednich katalogów, skopiowania niezbędnych plików, dokonania zapisów w rejestrach, a niekiedy skorzystania z zewnętrznych systemów (np. bazy uprawnień, systemu katalogowego, itp.). Za wykorzystaniem instalatora aplikacji przemawiają również jego możliwości sprawdzania wymaganych komponentów systemowych (np. platforma .NET w wersji 2.0), obsługi nieudanych prób instalowania oprogramowania i powrót do stanu systemu sprzed instalacji, a także zautomatyzowanie i elegancja (vs np. uruchamianie skryptowe). Dodatkowo większość klientów, firm, które zlecają przygotowanie oprogramowania, domaga się dostarczenia gotowej „paczki instalacyjnej” (w miarę możliwości), dzięki czemu zainstalowanie aplikacji nie wymaga wielkiej wprawy ani wiedzy informatycznej. Ponadto, jeśli jest to pakiet MSI, możliwe jest uruchomienie go przez administratora w wersji „cichej” (ang. silent) bez wiedzy pozostałych użytkowników.

 

2      Windows Installer

Windows Installer jest głównym elementem całego mechanizmu odpowiedzialnego za kontrolę instalacji na platformach Windows. Jest to serwis uruchamiany automatycznie przez SCM (Service Control Manager) podczas startu systemu, zarządzający nowo instalowanymi lub już zainstalowanymi programami za pomocą publicznego interfejsu Windows Installer API. Interfejs jest wystawiany przez pakiety instalacyjne MSI, a jego najważniejszymi elementami są funkcje: Install, Commit, Rollback oraz Uninstall, których definicje są zaszyte w kodzie programu. Oznacza to, że to właśnie autor aplikacji, jest odpowiedzialny za oprogramowanie interfejsu dla instalatora, zgodnie ze specyfiką jej działania. Ma to głęboki sens logiczny, gdyż autor aplikacji wie najlepiej, jakie są wymogi systemowe i komponentowe aplikacji, oraz jak powinna wyglądać jej instalacja na docelowej maszynie.

 

windows_installer

Rys. 1 Serwis Windows Installer

 

 

Do najważniejszych cech Windows Installera można zaliczyć:

- cechę data-driven: program dostarczany jest jako pakiet, zawierający niezbędne dane oraz instrukcje konieczne do przeprowadzenia procesu instalacji; Windows Installer nie wymaga załączania dodatkowych poleceń, skryptów jak i gdzie zainstalować komponent, a jedynie wskazania na komponent, który ma zostać zainstalowany (przełom w stosunku do starszej wersji Setup API);

- kontrolę dostępu do zasobów współdzielonych: informuje o korzystaniu przez inne aplikacje z zasobów, które są niezbędne do przeprowadzenia procesu instalacji i o konieczności ich zamknięcia;

- kontrola istnienia niezbędnych z punktu widzenia instalowanej aplikacji, danych komponentów w systemie;

- przechowywanie wszystkich informacji o instalowanej aplikacji;

- cofnięcie zmian procesu instalacyjnego i powrót do stanu sprzed instalacji, jeśli próba instalowania nie powiedzie się lub użytkownik w międzyczasie z niej zrezygnuje;

- samonaprawianie aplikacji i możliwość doinstalowania zasobów, które zostały np. omyłkowo usunięte z systemu;

- prostota w korzystaniu z Windows Installer API od strony programistycznej;

- funkcjonalność oparta na pakietach instalacyjnych MSI, o budowie strukturalnej bazy danych;

- udostępnienie interfejsu z linii komend, który pozwala na bardziej wyrafinowaną instalację oprogramowania (np. w trybie „cichym”): polecenie msiexe [params];

 

Wymienione cechy Windows Installera to tylko jedne z wielu. Więcej informacji na ten temat można znaleźć w [1] i [2].

 

3      Tworzenie pakietu MSI

Windows Installer opiera swoje zadanie instalowania, odinstalowywania aplikacji w systemie głównie na pakietach instalacyjnych MSI. W takiej postaci dostarczane są skompilowane programy do Windows Installera, w celu przygotowania środowiska do ich użycia na docelowej maszynie i ich zainstalowania. Pakiety MSI mają budowę strukturalną przypominająca bazę SQL’ową typową dla OLE Structured Storage Files. Baza danych MSI zawiera około 80 tabel, których kolumny są połączone dość skomplikowanymi referencjami. Ich budowa jest bardzo złożona i dlatego do budowania pakietów MSI i ustawiania ich atrybutów wewnętrznych wykorzystuje się specjalne narzędzia z MSI Windows Installer SDK. Jest on dostępny w wersji 1.1 na stronie Microsoft w dziale downloads.

Z punktu widzenia programistycznego znaczną część pracy wykonuje za programistę IDE Visual Studio 2003/2005, który buduje pakiet MSI z domyślnymi wartościami kolumn w bazach MSI. Niemniej jednak, aby w pełni wykorzystać możliwości Windows Installera podczas instalowania oprogramowania, wykorzystuje się bardzo przydatne narzędzie, jakim jest Orca.exe. Jest ona jednym z wielu pożytecznych aplikacji dostępnych w MSI SDK.

Orca nie robi nic innego poza edycją i tworzeniem plików MSI. Edytuje tabelową strukturę pakietu i dostarcza prostego i czytelnego w obsłudze interfejsu graficznego do ustawiania jego parametrów funkcjonalnych. Atrybuty pakietu reprezentowane są przez wpisy (rekordy) w odpowiednich tabelach MSI. Orca.exe umożliwia tworzenie struktur MSI od zera, nie mniej jednak nie posiada wbudowanych mechanizmów walidacji wprowadzanych danych. Dlatego w tym celu zazwyczaj nie jest wykorzystywana. Służy głównie jako narzędzie do modyfikowania istniejących pakietów MSI.

 

orca

Rys. 2 Narzędzie Orca (z pakietu MSI SDK). Okno dla dodawania nowego wiersza w tabeli MsiAssembly

 

 

Żeby wytłumaczyć proces tworzenia i modyfikowania pakietów MSI w środowisku Visual Studio .NET 2003/2005 przedstawię podstawowe etapy budowania instalatorów:

1. Przygotowanie aplikacji lub komponentu bibliotecznego (.dll) jako oddzielnego projektu za pomocą IDE Visual Studio

2. Oprogramowanie interfejsu Windows Installer API

3. Testowanie instalatora za pomocą InstallUtil.exe

4. Przygotowanie Setup and Deployment Project - ustawienie odpowiednich atrybutów (tych, które z poziomu Visual Studio .NET są konfigurowalne, i które z punktu widzenia funkcjonalności aplikacji są ważne)

5. Edycja pakietu MSI za pomocą narzędzia Orca.exe w celu wzbogacenia funkcjonalności.

 

Przedstawione powyżej punkty stanowią bazę w przygotowywaniu pakietu instalatora. W dalszej części artykułu opiszę dokładnie każdy z tych punktów na przykładzie prostego serwisu napisanego w C#, którego zadaniem będzie zapisywanie do systemowego Event Loga informacji o statusie serwisu, gdy ten zostanie wystartowany lub zatrzymany. Przykład z serwisem systemowym wydał mi się najbardziej odpowiedni, gdyż stworzenie instalatora dla aplikacji Windows Forms lub innego komponentu, np. Assembly jest znacznie mniej skomplikowane i informacje tu zawarte w zupełności do tego wystarczą. Dodatkowo omówię, w jaki sposób można uruchomić / zainstalować komponent, który został napisany w unmaneged code (kod natywny C++ Win 32), gdyż, o dziwo, jest na ten temat bardzo mało informacji, a proces jest dużo bardziej skomplikowany i godny uwagi.

 

3.1           Przygotowanie aplikacji

Przykładowy serwis, na podstawie, którego zostanie przedstawiony proces tworzenia instalatora, nie ma zaszytej w sobie głębokiej logiki. Podczas startowania i zatrzymywania z konsoli SCM zapisuje do Event Loga informację o pomyślnym przebiegu operacji.

W środowisku .NET Framewroku i z wykorzystaniem IDE Visual Studio 2005, proces tworzenia serwisu jest mocno wspierany przez środowisko programistyczne, przez co życie programisty jest ułatwione. Aby stworzyć wykorzystany w artykule serwis należy wybrać Windows Service jako typ projektu, a następnie przeciążyć metody OnStart i OnStop.

Każdy serwis reprezentowany jest przez klasę ServiceBase, z której musi dziedziczyć. Programista tworzy jedynie konstruktor klasy, gdzie inicjalizuje wszystkie zasoby, z których serwis korzysta, a następnie nadpisuje te funkcje abstrakcyjne, które są niezbędne do spełnienia założeń funkcjonalnych.

W środowisku Visual Studio w bardzo wygodny sposób korzysta się z obiektu EventLog z przestrzeni System.Diagnostics, którego zadaniem jest zapisywanie informacji do systemowego Event Viewer’a. Alternatywą może być raportowanie do pliku, ale nie jest to zbyt „ładne” rozwiązanie.

 

Listing 1. Kod prostego serwisu logującego swój status do SCM

 

public partial class SimpleService : ServiceBase

{

private System.Diagnostics.EventLog eventLog1;

public Service1()

      {

            //ustalenie nazwy serwisu

            this.ServiceName = "Simple Service";

 

            //inicjalizacja obiektu

this.eventLog1 = new System.Diagnostics.EventLog();

//stworszenie Event Logu dedykowanego dla serwisu

            if (!System.Diagnostics.EventLog.SourceExists("SimpleServiceSource"))

            {

                System.Diagnostics.EventLog.CreateEventSource(

                    "SimpleServiceSource", "SimpleServiceLog");

            }

//przypisanie obiektowi EventLogu źródła i logu, do którego ma //zapisywac

            eventLog1.Source = "SimpleServiceSource";

            eventLog1.Log = "SimpleServiceLog";

        }

 

         //funkcja woływana podczas startowania serwisu

        protected override void OnStart(string[] args)

        {

            eventLog1.WriteEntry("SimpleService: Starting.....on date "+DateTime.Now.ToString());

 

        }

        //funkcja wołana podczas zatrzymywania serwisu

        protected override void OnStop()

        {

            eventLog1.WriteEntry("SimpleService: Stopping.....on date " + DateTime.Now.ToString());

        }

       

}

 

Wywołanie serwisu odbywa się w klasie Program, w jej statycznej metodzie static Main za pomocą funkcji z interfejsu ServiceBase:

 

Listing 2.

static class Program

{

static void Main()

      {

ServiceBase[] ServicesToRun;

 

ServicesToRun = new ServiceBase[] {new SimpleService ()};

 

ServiceBase.Run(ServicesToRun);

       }

}

 

Kod serwisu jest gotowy. Niemniej jednak, aby można było przetestować poprawność jego działania i przeanalizować funkcjonalność, konieczne jest dodanie klasy System.Configuration.Install.Installer, której zadaniem będzie inferencja z Windows Installer’em systemu. Instalowanie serwisu jest szczególnym przypadkiem ogólnego postępowania w tworzeniu instalatora. Wymaga, bowiem zdefiniowania dwóch kolejnych obiektów klas: ServiceInstaller i ServiceProcessInstaller. Ale poza tym, postępowanie jest podręcznikowe.

 

3.2           Oprogramowanie interfejsu Windows Installer API

Interfejs programistyczny Windows Installera jest napisany w C++ Win32. Jednak dzięki użyciu języka C#, który jest zarządzany (ang. Managed) programista korzysta z przyjaznych w użyciu wywołań funkcji i nie musi wiedzieć, co to jest DWORD lub msiHandle. To, czym musi się zainteresować to klasa System.Configuration.Install.Installer. Dzięki niej możliwe jest zdefiniowanie funkcji-łączników pomiędzy kodem a pakietem MSI, w taki sposób, aby można było wywołać odpowiednią funkcję / akcję w każdej z czterech faz instalatora: Install, Unistall, Commit oraz Rollback. Najważniejsze metody klasy Insatller, odpowiadające fazom instalatora, zostaną następnie zmapowane z odpowiednimi wpisami w tablicy MIS o nazwie CustomAction. Czynność „mapowania” wykonywana jest automatycznie podczas budowania projektu Setup and Deployment Project w IDE Visual Studio .NET. Jednak na tym etapie rozważań, skupić się należy na implementacji klasy Installer’a.

Używanie funkcjonalności Installer’a odbywa się za pomocą dowolnej publicznej klasy, która spełnia następujące warunki:

- dziedziczy z klasy System.Configuration.Install.Installer

- przeciąża metody Install, Uninstall, Commit i Rollback Instalera

- klasa posiada atrybut RunInstallerAttribute ustawiony na true

- klasa musi znajdować się w tym samym obiekcie Assembly, w którym znajduje się aplikacja do zainstalowania (należy ją dodać jako plik .cs (jeśli to c#) do projektu aplikacji)

- klasa instalatora jest elementem Managed Code, dlatego można jej używać wyłącznie w środowisku .NET Framework

O czym należy zawsze pamiętać to to, że podczas procesu instalowania i odinstalowywania aplikacji wykorzystywane są różne instancje obiektu klasy Installer. Dlatego nie można przechowywać informacji w atrybutach tego obiektu. Do tego celu służy specjalny obiekt IDictionary, który jest parametrem wywołania każdej z funkcji Install,Uninstall, Rollback i Commit. Obiekt jest permanentny i przechowuje swój stan.

Magazyn IDictionary ma postać tablicy typu <object,object>, a użyć go można np. do przechowywania wartości kluczy rejestru lub plików, sprzed instalacji (tych, na których operuje aplikacja) tak, aby można było przywrócić stan systemu sprzed instalacji po usunięciu aplikacji.

Poniższy kod pokazuje wykorzystanie interfejsu Installer’a do stworzenia własnego instalatora. Dodatkowo wykorzystany został obiekt EventLog w celu monitorowania jego zachowania. Dla ułatwienia log, jest ten sam dla instalatora i serwisu SimpleService. Aby zobrazować wykorzystanie obiektu IDictionary, przechowywana jest wartość ProductName klucza HKEY_LOCAL_MACHINE\software\MY_KEY, która jest przywracana po odinstlowaniu aplikacji.

 

Listing 3. Kod instalatora

using Microsoft.Win32; //czytanie i pisanie do rejestru

[RunInstaller(true)]

public partial class ProjectInstaller : Installer

{

private System.Diagnostics.EventLog eventLog1;

 

public ProjectInstaller()

{

      SimpleServiceInit(); //patrz Listing 4.

      //inicjalizacja obiektu

this.eventLog1 = new System.Diagnostics.EventLog();

//stworszenie Event Logu dedykowanego dla serwisu

     if (!System.Diagnostics.EventLog.SourceExists("SimpleServiceSource"))

     {

System.Diagnostics.EventLog.CreateEventSource(

"SimpleServiceSource", "SimpleServiceLog");

}

//przypisanie obiektowi EventLogu źródła i logu, do którego ma

//zapisywac

eventLog1.Source = "SimpleServiceSource";

eventLog1.Log = "SimpleServiceLog";

}

//funkcja wywolywana podczas instalowania aplikacji

public override void Install(System.Collections.IDictionary stateSaver)

{

base.Install(stateSaver);

RegistryKey regkey;

try

{

regkey = Registry.LocalMachine.OpenSubKey(@"Software\MY_KEY");

 

string val = (string)regkey.GetValue("ProductName");

stateSaver.Add((string)"ProductName", (string)val);

//modyfikacja wartosci klucza

//....

}

catch (Exception e)

{

      //obsluga wyjatkow

}

eventLog1.WriteEntry("SimpleService Installer...Install");

}

//funkcja wywolywana podczas usuwania apklikacji z systemu

public override void Uninstall(System.Collections.IDictionary savedState)

{

base.Uninstall(savedState);

RegistryKey regkey;

try

{

string old_productName = (string)savedState["ProductName"];

                

regkey = Registry.LocalMachine.OpenSubKey(@"Software\MY_KEY");

regkey.SetValue("ProductName", old_productName);

Registry.LocalMachine.Flush();

 

}

catch (Exception e)

{

//obsluga wyjatku

}

eventLog1.WriteEntry("SimpleService Installer...Uninstall");

}

//funkcja wywolywana jesli process instalacji sie powiedzie - Install

public override void Commit(System.Collections.IDictionary savedState)

base.Commit(savedState);

eventLog1.WriteEntry("SimpleService Installer...Commit");

}

//funkcja wywolywana jesli wystapi blad podczas instalacji, lub my go zasygnalizujemy

 

public override void Rollback(System.Collections.IDictionary savedState)

{

base.Rollback(savedState);

eventLog1.WriteEntry("SimpleService Installer...Rollback");

}

 

Jak już wcześniej wspomniałam instalator serwisu wymaga dodefiniowania dwóch obiektów klas ServiceProcessInstaller oraz ServiceInstaller (W przypadku innych aplikacji kawałek kodu z Listing 4 należy pominąć). Pozwalają one na określenie np. z prawami, jakiego konta serwis będzie się wykonywał oraz tryb serwisu (Automatic,Manual). W rzeczywistości serwis nie powinien wykonywać się w kontekście konta systemowego, a jedynie takiego, z minimalnymi (ale wystarczającymi) przywilejami.

 

Listing 4. Kod funkcji inicjalizującej obiekty niezbędne do instalowania serwisu

 

private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;

private System.ServiceProcess.ServiceInstaller serviceInstaller1;

 

private void SimpleServiceInit()

{

//inicjalizacja obiektów niezbędnych do wystartowania serwisu

this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();

this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();

//ustawienie konta, na ktrym serwis ma sie wykonywac

this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem

this.serviceProcessInstaller1.Password = null;

this.serviceProcessInstaller1.Username = null;

//typ startowania serwisu – automatyczny: startuje wraz z systemem

this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;

 

this.Installers.AddRange(new System.Configuration.Install.Installer[] {

this.serviceProcessInstaller1, this.serviceInstaller1 });

}

 

Więcej informacji o klasach System.ServiceProcess.ServiceProcessInstaller, System.ServiceProcess.ServiceInstaller i System.Configuration.Install.Installer można znaleźć w [5], [6], [7].

 

3.3           Testowanie instalatora za pomocą InstallUtil.exe

Po napisaniu kodu odpowiedzialnego za zainstalowanie serwisu, należy przetestować poprawność działania zarówno instalatora jak i serwisu. W tym celu wykorzystane zostanie narzędzie InstallUtil.exe.

InstallUtil.exe znajduje się w każdym pakiecie .NET Framework katalogu C:\WINDOWS\Microsoft.NET\Framework\vXXX. Wywoływane jest z linii komend następujący sposób:

1. C:\InstallUtilexe /i SimpleService.exe – instaluje aplikację w systemie

2. C:\InstallUtilexe /u SimpleService.exe – usuwa aplikację z systemu.

Podczas instalowania / odinstalowywania aplikacji, narzędzie nie robi nic innego jak wołanie metod Install, Uninstall, Commit oraz Rollabck interfejsu klasy System.Configuration.Install.Installer. Jak wcześniej wspomniałam, klasa Installer’a musi znajdować się w tym samym pliku Assembly co aplikacja.

InstallUtil.exe jest bardzo przydatnym narzędziem. Niestety posiada swoje ograniczenia, takie jak brak możliwości instalowania aplikacji napisanych w kodzie natywnym (np. C++ Win32). Ograniczenie to wynika bezpośrednio z faktu, że kod natywny nie korzysta z platformy .NET Framework, z którą to właśnie dystrybuowane jest narzędzie InstallUtil.exe.

Wydawać by się mogło, że jest to małe albo żadne ograniczenie. Jednak podczas pisania kodu Unmanaged, stanowi duży problem. W dalszej części artykułu postaram się opisać jak obejść to ograniczenie, i w jaki sposób wdrażać aplikacje w kodzie natywnym w systemie Windows bez udziału .NET Framework.

Po pomyślnym zainstalowaniu serwisu SimpleService w logu systemowym (Event Viewer) w węźle SimpleServiceLog pojawią się dwa wpisy:

SimpleService Installer...Install

SimpleService Installer...Commit (lub SimpleService Installer…Rollback, jeśli wystąpiły błedy)

Po pomyślnym odinstalowaniu, natomiast:

SimpleService Installer...Uninstall.

 

Jeżeli serwis został zainstalowany bezbłędnie wówczas w Computer Managemend -> Services and Applications -> Services pojawi się następujący wpis z serwisem SimpleService, który należy ręcznie wystartować:

service_pending

Rys. 3 Zainstalowany i wystartowany z SCM serwis SimpleService

 

W logu SimpleServiceLog, można zauważyć raport wygenerowany podczas wołania funkcji OnStart dla serwisu:

SimpleService: Starting.....on 2006-12-29 14:15:31.

Po zastopowaniu serwisu pojawi się analogiczny wpis dla funkcji OnStop:

SimpleService: Stopping.....on 2006-12-29 14:19:3.7

 

InstallUtil.exe jest narzędziem, które może być użyte nie tylko do instalowania serwisu, lecz dowolnej aplikacji, np. Windows Forms, która posiada dedykowaną klasę Installer’a. Więcej informacji o narzędziu można znaleźć w [8].

InstallUtil.exe nie jest wykorzystywany podczas komercyjnych wdrożeń, chociażby dlatego, że nie pozwala na wykonanie instalacji w trybie Silent. W pełni profesjonalna aplikacja powinna być dostarczana do odbiorcy końcowego w postaci paczki instalacyjnej MSI. Do tego celu słuzy Setup and Deployment Project w IDE Visual Studio .NET.

 

 

3.4           Przygotowanie Setup and Deployment Project

Visual Studio .NET 2003/2005 dostarcza gotowych wzorców dla projektów wdrożeniowych. Najciekawsze z nich to CAB, Merge Module oraz Setup Project. Z punktu widzenia instalacji aplikacji niezależnej (także serwisu) najważniejszy wydaje się Setup Project i ten typ projektu postaram się omówić w tym artykule.

 

 

setup_project

Rys. 4 Typy projektów Setup and Deployment

 

Pierwszym krokiem w budowaniu projektu instalacyjnego Setup jest jego dodanie do komponentu Solution, w którym znajduje się budowana aplikacja, a następnie ustawienie Project Output, tak aby wskazywał na projekt SimpleService. Zostało to pokazane na Rysunku 5.

setup_project2

Rys. 5 Dodanie Primary Output wskazującego na SimpleService.exe

 

Gdy Primary Output został ustawiony, projekt Setup będzie wiedział, gdzie poszukiwać klasy Instalatora (w jakim pliku Assembly) i jednocześnie doda niezbędne biblioteki, od których aplikacja zależy. W tym przypadku w Detected Dependencies znalazł się wpis o Microsoft .NET Framework.

Drugim etapem jest odpowiednie skonfigurowanie projektu Setup, do którego zalicza się:

   1. File System: ustalenie hierarchii folderów dla instalera, skopiowanie zasobów aplikacji

   2. Registry: ustalenie kluczy w rejestrach, których potrzebuje aplikacja

   3. User Interface: zdefiniowanie interfejsu użytkownika dla Setup’u

   4. Custom Actions: zdefiniowanie akcji, które mają zostać wykonane podczas fazy Install, Uninstall, Rollback i Commit

   5. Lunch Conditions: zdefiniowanie warunków, które muszą być spełnione, zanim instalator przystąpi do instalowania apliakcji

 

3.4.1      Edytor File System

Edytor File System pozwala na dodanie do projektu Setup katalogów, plików, obiektów Project Output (czyli Assembly projektów, których dotyczy instalacja) i wyspecyfikowanie, gdzie maja być one zainstalowane na docelowej maszynie.

Domyślnie Seup posiada trzy foldery: Application Folder, User’s Desktop, User’s Programs Menu. Pierwszy z nich reprezentuje katalog instalacyjny, w którym zostanie zainstalowana aplikacja. Zazwyczaj ścieżkę do katalogu podaje się podczas instalowania aplikacji za pomocą MSI chyba, że jest to „ciche” instalowanie, gdzie parametry przekazywane są w inny sposób.

Application Folder pozwala na przechowywanie plików różnego rodzaju, m.in. Assembly, plików graficznych oraz tekstowych, z których korzysta aplikacja, i które mają zostać zainstalowane w systemie. Jest katalogiem instalacyjnym, do którego ścieżkę specyfikuje się podczas uruchamiania pliku MSI (własność ApplicationFolder). Podczas dodawania Project Output do projektu Setup, do katalogu Application Folder został dodany Assembly – plik SimpleService.exe.

User’s Desktop wskazuje na pulpit użytkownika. Dzięki niemu możliwe jest np. stworzenie skrótu do aplikacji, którą się instaluje, lub umieszczenie tam pliku typu  Readme. Przykładowo, aby stworzyć skrót do pliku wykonywalnego Primary output from SimpleService, należy wybrać ten zasób z Application Folder a następnie zaznaczyć Create shortcut. Następnie wystarczy nazwać go wedle potrzeb, np. Simple Service i umieścić w katalogu User’s Programs. Resztą zajmie się mechanizm Setup and Deployment.

User’s Programs Menu wskazuje na katalog programów dostępny poprzez Start -> All Programs.

Programista może stworzyć dodatkowe podfoldery w istniejących folderach domyślnych lub foldery głównego poziomu.

Korzystanie z funkcjonalności File System jest bardzo proste i intuicyjne. Dlatego nie będę pisała o niej zbyt wiele. Więcej informacji można znaleźć w [9].

 

file_system

Rys.6 Edytor File System

 

3.4.2      Edytor Registry

Edytor registry pozwala na ustawianie kluczy i i czwartości w rejestrach systemowych zgodnie z rysunkiem Rys. 6. Przydatną cechą edytora jest możliwość korzystania z powiązanymi z projektem Setup jego właściwościami, takimi jak [ProductName], [Manufacturer]. Podczas instalacji zmienne są zamieniane prawdziwymi wartościami. Podczas korzystania z edytora powinno zachować się uwagę i ustawiać tylko te klucze i ich wartości, które są potrzebne z punktu widzenia funkcjonalności aplikacji.

 

registry

Rys. 7 Edytor Registry

 

Korzystanie z edytora Registry jest proste i nie zasługuje na dłuższą uwagę. Więcej informacji na ten temat można znaleźć w [9].

3.4.3      Edytor User Interface

Podczas tworzenia projektu Setup, budowany jest domyślny interfejs graficzny dla użytkownika, który przeprowadzi proces instalacji aplikacji. Zawiera formatki Welcome, Installation Folder (do podania katalogu instalacyjnego), Confirm Installation (do zatwierdzenia instalacji), Progress oraz Finished. Zdarza się jednak, że ta funkcjonalność nie wystarcza. Wówczas można zdefiniować dodatkowe okna dialogowe, które pojawią się po uruchomieniu pliku MSI. Okna dialogowe posiadają pola typu TextBox, Checkbox oraz RadioButton.

Żeby pokazać sposób korzystania z funkcjonalności dodatkowych okien dialogowych, zaimplementuję parametr Parameter1 typu boolowskiego, którego wartość będzie ustalana za pomocą CheckBox’a od strony GUI użytkownika. Następnie po stronie kodu instalatora będzie można wykonać akcję zależnie od wartości parametru.

Do tego celu wykorzystane zostanie okno dialogowe typu Checkboxes (A), które zawiera 4 kontrolki Checkbox. Interesuje nas tylko jedna (bo jeden jest parametr Parameter1), dlatego dla pozostałych ustawiona zostanie wartość CheckboxVisible = false. Dla kontrolki checkbox1, parametry Label i Property przyjmą wartość jak na rysunku 8. Kolejność pojawienia się Checkboxes (A) ma znaczenie i musi być przed oknem dialogowym Istallation Folder.

parameter1

Rys. 8 Definiowanie parametru Parameter1 w oknie Checkboxes (A)

 

Po uruchomieniu projektu MSI, pojawi się następujące okienko:

checkboxesA

Rys. 9 Ustawianie wartości parametru Paramter1 z poziomu GUI w Setup’ie

 

Parametr Parameter1 jest atrybutem MSI, który jest przechowywany w jednej z tabel wewnętrznych, z których pakiet się składa.

Należy jednak zauważyć, że jest on bezużyteczny, jeśli jego wartość nie jest nigdzie wykorzystywana. Dlatego parametr zostanie przekazany do klasy Installer z Assembly aplikacji i na podstawie jego wartości zostanie wykonana jakaś akcja.

Parametry ustawiane w MSI są dostępne programistycznie w klasie Installer’a poprzez globalną strukturę Context i jej atrybut Parameters.

Aby ustalić wartość parametru Parameter1 wystarczy dodać następujący kawałek kodu w dowolnej metodzie klasy Installer projektu SimpleService:

 

Listing 5. Dostęp do parametrów ustawianych w MSI

string param1 = (String)Context.Parameters["My_Paramaeter1"];

//My_Parameter1: klucz podany w CustomActionData

if (param1 == null || param1 == "" || param1 == "0" )

{

//perform action 1

}

else if (param1 == "1")

{

//perform action 2

}

 

Należy jednak pamiętać, że aby dany parametr z pakietu MSI był dostępny w kodzie Installera (w jednej z jego czterech głównych metodach), niezbędne jest jawne wyspecyfikowanie tego parametru w Custom Action w strukturze CustomActionData. Będzie to opisane w dalszej części artykułu (pkt 5 Edytor Custom Actions).

 

Okazuje się, że istnieje jeszcze cały szereg parametrów, które projekt Setup domyślnie w sobie zawiera, takich jak np. Manufacturer, ProductName. Dostęp do nich odbywa się analogicznie z tym małym wyjątkiem, że nie trzeba ich specyfikować w CustomActionData.

Niekiedy potrzebne jest zapamiętanie parametru w sposób trwały. Posłużyć do tego może obiekt IDictionary state-saver, który dostępny jest jako parametr wywołania funkcji Install, Commit, Rollback i Unistall interfejsu Installer’a. Była o tym mowa wcześniej.

 

3.4.4      Edytor Custom Actions

Edytor pozwala na definiowanie akcji / zadań, które mają zostać wykonane, kiedy instalacja aplikacji, będzie w jednym z czterech stanów: Install, Commit, Rollback i Uninstall. Rolę zadań Custom Actions mogą spełniać jedynie pliki .exe, .dll oraz skrypty .vb. W przypadku SimpleService, jako Custom Action został wybrany plik wykonywalny: SimpleService.exe, który w edytorze pojawia się pod nazwą Primary output from SimpleService. Zawiera on klasę Installer’a, dlatego w jego atrybutach w każdej z akcji Custom Action należy wybrać InstallerClass = true (Zostało to pokazane na rysunku 10).

Obiekty Custom Actions odpowiadają fazom, w jakich może znajdować się instalacja. Jeżeli pliki assembly dodane do Custom Action zawierają zdefiniowaną klasę Installera, MSI wywoła odpowiednią metodę Installera – Commit, Rollback, Install albo Uninstall. Dzieje się to automatycznie.

Każdy obiekt Custom Action może zawierać po kilka plików .exe, .dll lub .vb. Przykładowo, oprócz Primary otput, można stworzyć projekt Custom Library (.dll) o nazwie Run_WWWMicrosoft, który będzie posiadał w funkcji Main() wywołanie System.Diagnostics.Process.Start("www.microsoft.com"). Jeżeli doda się ten skompilowany komponent .dll do Custom Actions -> Install, wówczas po zainstalowaniu aplikacji zostanie uruchomiony Internet Explorer (lub inna domyślna przeglądarka) z podanym w funkcji Main() adresem www. Ponieważ Run_WWWMicrosoft.dll nie posiada zaszytej w sobie klasy Installera, należy ustawić jego atrybut InstallerClass = false. Kolejność wykonywania plików .exe, .dll i .vb jest kolejnością w strukturze drzewiastej edytora.

 

Uwaga!

Każde z czterech typów zadań Custom Actions posiada atrybut CustomActionData, który odgrywa kluczową role w przesyłaniu wartości atrybutów z projektu Setup do klasy Installera. Struktura CustomActionData jest definiowana per typ Custom Action i jest dostępna w odpowiadającej jej funkcji Installer’a. Oznacza to, że jeśli chcemy przesłać do funkcji Install parametr Parameter1, musi być on zdefiniowany w CustomActionData dla Cutom Action typu Install. Format przesyłanych kluczy-wartości to /klucz=wartość. Takie pary muszą być oddzielone pojedynczym znakiem spacji. Przedstawia to rysunku 10.

 

custom_actions

Rys. 10 Edytor Custom Actions

 

3.4.5      Edytor Lunch Conditions

Edytor pozwala na zdefiniowanie warunków, które muszą być spełnione, aby instalacja się powiodła (a nawet rozpoczęła). Warunki mogą dotyczyć wersji zainstalowanego systemu operacyjnego, rozmiaru pamięci RAM, wpisów w rejestrze, a także istnienia danych plików.

Podczas dodawania do projektu Setup Primary output’u wskazującego na Assembly SimpleService.exe, została automatycznie zlokalizowana i dodana zależność od platformy .NET Framework 2.0 (nie można jej usunąć).

Definiowanie warunków jest bardzo proste. Należy jednak zapoznać się ze składnią tych warunków i z nazwami parametrów, których można używać w każdym z rodzajów LunchConditions. Przykładowe atrybuty używane podczas definiowania warunków to PhisicalMemory, version9x, ServicePackLevel. Więcej informacji na ten temat można znaleźć w [9].

Jeśli wszystkie atrybuty projektu Setup zostały ustawione, wystarczy projekt zbudować w wersji release i bezpiecznie wdrażać.

 

3.5           Edycja pakietu MSI za pomocą narzędzia Orca.exe

Wydawać by się mogło, że projekt wdrożeniowy, przysłowiowa „paczka”, jest gotowy. I byłaby to prawda, gdyby wymogi biznesowe poprzestały na tym, co zostało powiedziane. Załóżmy jednak, że wymogi się rozszerzyły o prosty warunek restartu systemu i o możliwość uruchomienia „instalki” MSI w trybie cichym. Okazuje się, że Visual Studio .NET ma ograniczenia – nie można poprzez IDE ustawić wszystkich atrybutów kolumn w tabelach MSI, na których nam zależy. W takim wypadku pozostaje użyć narzędzia Orca.exe.

Narzędzie jest dostępne jako jedno z wielu w pakiecie Microsoft Platform Software Development Kit - MsiIsntall.SDK, który można bezpłatnie pobrać z działu Downloads na stronie Microsoft.

 

Wymóg 1 – Restart systemu po pomyślnej instalacji

Wymóg restartu systemu po pomyślnej instalacji ma za zadanie zobrazowanie ręcznego ustawiania wartości w kolumnach tabel pakietu MSI. Poniższe postępowanie może być przeprowadzone dla dowolnego innego parametru.

Jedną z najciekawszych tabel MSI od strony programistycznej jest tabela Property, która zawiera dwie kolumny: Property i Value. Podczas uruchamiania instalera tabela jest czytana i na jej podstawie określane jest zachowanie instalatora MSI.

W celu zmuszenia instalatora MSI do restartu systemu należy do tabeli Property dodać nowy wpis REBOOT z wartością  Force. Obrazuje to rysunek 11.

 

orca_reboot

Rys. 11 Ustawianie parametru REBOOT w Orca.exe

 

Informacje na temat tabeli Property znajdują się w [10].

 

Wymóg 2 – Instalacja w trybie cichym

Instalacja w trybie cichym ma za zadanie zainstalowanie oprogramowania bez notyfikacji użytkowników maszyny. Zazwyczaj wykonywana jest globalnie przez administratora systemu. Do tego celu służy narzędzie msiexec.exe, które wywoływane jest z linii komend.

Instalacja w trybie cichym odbywa się bez uruchamiania trybu graficznego – czytane są bezpośrednio tablice pakietu MSI i wykonywana jest odpowiednia akcja przez Installer’a (funkcje Install i Commit). Oznacza to niestety, że nie ma możliwości wyspecyfikowania parametru wejściowego Parameter1, na którym opiera się funkcjonalność Installer’a, za pomocą GUI i checkbox’a. Oznacza to konieczność zmodyfikowania tabel MSI za pomocą narzędzia Orca.exe.

Okazuje się, że operacja ta jest bardzo prosta. Analogicznie jak w przypadku parametru REBOOT=Force, tak i tutaj obróbce zostaje poddana tabela Property. Należy dodać kolejny wiersz w tabeli z wartościami Parameter1 = 1, lub Parameter1 = 0, w zależności od potrzeb. Nic więcej nie należy robić, gdyż struktura CustomActionData została tak zdefiniowana (/My_Parameter1=[Parameter1] ), aby w kluczu My_Parameter1 przekazać wartość Parameter1 do kodu Installer’a serwisu. Za pomocą narzędzia Orca.exe można znaleźć tą zależność:

orca_custom_actions

Rys. 12 Orca.exe – Tabela CustomAction

 

Instalowanie ciche, w najprostszym przypadku, odbywa się poprzez wywołanie z linii komend następującego polecenia:

C:\msiexec /i sciezka_do_msi\SimpleServiceSetup.msi /q

 

Wynik instalacji można zaobserwować w Event Logu systemowym.

 

4        Setup Project i Unmanaged Code

Z punktu widzenia najważniejszych tematów związanych z instalowaniem i wdrożeniem oprogramowania w środowisku Windows z platformą .NET, wszystko zostało powiedziane. Pozostaje jedynie jedna wątpliwość – a co jeśli program, który chcemy zainstalować jest kodem natywnym, np. C++ Win32? I tutaj pojawiają się małe komplikacje.

Pierwsza z nich to brak możliwości używania InstallUtil.exe do celów testowych, gdyż funkcjonalność narzędzia opiera się na platformie .NET. Druga to fakt, że klasa System.Configuration.Install.Installer, która stanowi podstawę w oprogramowaniu instalatora, jest klasą Managed Code! A zatem do projektu Unmanaged Code nie może zostać dodana. Oczywiście Visual Studio .NET jest na tyle sprytne, że na taką operację pozwoli i zasugeruje przekonwertowanie projektu z Unmanaged Code do postaci Managed Code. Ale nie o to tutaj chodzi.

Rozwiązaniem dla tego problemu jest skorzystanie z Win32 API, a dokładniej z biblioteki msi.lib oraz nagłówków msi.h i msiquery.h w kodzie programu natywnego. Biblioteka msi.lib nie jest standardową biblioteką, lecz instalowana jest razem z Microsoft Platform Software Development Kit. Należy ją skopiować do C:\WINDOWS\system32, tak aby była widoczna razem z pozostałymi bibliotekami systemowymi. Dołączenie biblioteki w kodzie odbywa się za pomocą dyrektyw preprocesora:

 

Listing 6. Korzystanie z biblioteki msi.lib

#pragma once

#pragma comment(lib, "msi.lib")

#include <msi.h>

#include <msiquery.h>

 

Kolejnym krokiem jest odpowiednie oprogramowanie funkcji dla Custom Actions, np. MyInstall i MyUninstall, w aplikacji C++, która ma być zainstalowana za pomocą projektu Setup. Załóżmy przykładowo, że będzie to prosta biblioteka dll o nazwie ProstaBibilotekaWin32.dll. Należy zatem „zmusić ją”, aby wystawiła publiczny interfejs z metodami MyInstall i MyUninstall, tak aby pakiet MSI mógł z nich skorzystać. Wystawienie interfejsu odbywa się w pliku definicji ProstaBibilotekaWin32.def, gdzie umieszczone zostają polecenia eksportu metod. Dyrektywa EXPORT jest dedykowaną dyrektywą dla definiowanych programistycznie w C++ zadań typu CustomActions:

 

Listing 7. Jawne specyfikowanie metod CustomActions, które będą widoczne z poziomu MSI

LIBRARY     " ProstaBibilotekaWin32"

EXPORTS

      MyInstall

      MyUninstall

 

Teraz wystarczy tylko oprogramować metody MyInstall i MyUninstall. Każda funkcja, która ma spełniać zadanie Custom Action musi mieć prototyp postaci:

 

Listing 8. Prototyp funkcji Custom Action

UINT __stdcall Nazwa_funkjci_eksportowanej(MSIHANDLE hInstall)

{

      //kod

}

 

Dostęp do parametrów tablicy pakietu MSI odbywa się poprzez metodę MsiGetProperty, bezpośrednio z Windows Installer API.

 

Listing 9. Wydobywanie wartości atrybutu My_Parameter1

//...   

DWORD sizeValueBuffer = 255;

TCHAR valueBuffer[BUF_SIZE] = {0};

 

UINT  uiStat = MsiGetPropertyW(hInstall, _T("My_Parameter1"), valueBuffer, &sizeValueBuffer);

//w valueBuffer znajduje się wartość parametru My_Parameter1

//...

 

Ostatnim etapem tworzenia komunikacji pomiędzy pakietem MSI a kodem natywnym, jest wyspecyfikowanie w projekcie Setup, które z wyeksportowanych w pliku definicji funkcji Custom Actions mają zostać wykonane podczas określonych akcji. Po dodaniu Primary Otput from ProstaBibilotekaWin32, należy wyedytować jego właściwości i ustawić parametr EntryPoint = MyInstall (dla Custom Action typu Install) lub EntryPoint = MyUninstall (Custom Action typu Uninstall). Dodatkowo konieczne jest ustawienie parametru InstallerClass = false, gdyż biblioteka ProstaBibilotekaWin32, nie posiada w sobie implementacji klasy Installer’a.

win32_msi

Rys. 13 Ustawienie parametrów w MSI dla .dll w kodzie nie zarządzanym

 

Dodatkowe informacje dotyczące tego problemu można znaleźć w [11].

 

5        Podsumowanie

Artykuł miał na celu zaznajomienie czytelnika z tematem Windows Installer i tworzenia paczek wdrożeniowych. Temat publikowania stron ASP .NET nie został tutaj opisany, gdyż kilka miesięcy temu pojawił się na CodeGuru wyczerpujący tekst na ten temat. Zawarte tutaj rozważania można wykorzystać podczas tworzenia „instalek” dla programów Windows Forms, Windows Services, projektów Win32, Console Applicaition, a także projektów bibliotecznych (.dll).

Mam nadzieje, że poruszony temat został w pełni wyjaśniony, a to, co nie zostało omówione, lub zostało, ale w zbyt małym stopniu, na pewno może zostać zgłębione na podstawie załączonej bibliografii.

 

Bibliografia

 

[1] Windows Installer: Benefits and Implementation

http://www.microsoft.com/technet/prodtechnol/windows2000serv/maintain/featusability/winmsi.mspx

[2] Windows Installer

http://en.wikipedia.org/wiki/Microsoft_Installer

[3] Creating a Windows Service Application

http://msdn2.microsoft.com/en-us/library/zt39148a(VS.80).aspx

[4] Creating Custom Action

http://msdn2.microsoft.com/en-us/library/d9k65z2d(VS.80).aspx

[5] Service Installer

http://msdn2.microsoft.com/en-us/library/system.serviceprocess.serviceinstaller.aspx

[6] Service Process Installer

http://msdn2.microsoft.com/en-us/library/system.serviceprocess.serviceprocessinstaller.aspx

[7] Installer

http://msdn2.microsoft.com/en-us/library/system.configuration.install.installer.aspx

[8] InstallUtil.exe

http://msdn.microsoft.com/library/en-us/cptools/html/cpconInstallerUtilityInstallutilexe.asp?frame=true

[9] Setup and Deployment Project Ref

http://msdn2.microsoft.com/en-us/library/ms235317(vs.80).aspx

http://blogs.msdn.com/astebner/articles/574618.aspx

http://msdn2.microsoft.com/en-us/library/aa289503(VS.71).aspx

[10] MSI Property Table

http://msdn2.microsoft.com/en-us/library/aa370889.aspx

[11] Installer Database ref

http://msdn2.microsoft.com/en-us/library/aa369398.aspx

 

 

 

 

 

Podobne artykuły

Komentarze 0

pkt.

Zaloguj się lub Zarejestruj się aby wykonać tę czynność.