SelfInstaller, czyli samoinstalująca się usługa

Jakiś czas temu opisałem jak utowrzyć instalator dla uslugi Windows w środowisku Visual Studio. Jest to sposób dość skomplikowany i czasochłonny. Z drugiej strony daje duże możliwości konfiguracyjne i rozszerzające. Nie o tym jednak. W niniejszym artykule opisuję jak zainstalować usługę Windows wyłącznie za pomoca kodu.

W tym celu wykorzystam API do narzędzia Installutil.exe, które jest dostępne poprzez klasę ManagedInstallerClass, znajdującej się w System.Configuration.Install. Moja implementacja instalatora wygląda następująco:

[RunInstaller(true)]
public class SelfInstaller : Installer
{
	private static readonly string _exePath = Assembly.GetExecutingAssembly().Location;
	private static readonly string[] _installArgs =
	{
		"/name=TestServiceToDoNothing",
		"/LogFile=SelfInstaller.log", 
		"/ShowCallStack=true", 
		_exePath
	};
	private static readonly string[] _uninstallArgs = 
		new[] { "/u" }.Concat(_installArgs).ToArray();

	public SelfInstaller()
	{
		var processInstaller = new ServiceProcessInstaller
		{
			Account = ServiceAccount.LocalService
		};

		var serviceInstaller = new ServiceInstaller
		{
			ServiceName = "TestServiceToDoNothing",
			DisplayName = "Test service",
			Description = "Test service description",
			StartType = ServiceStartMode.Manual
		};

		Installers.AddRange(new Installer[] { processInstaller, serviceInstaller });
	}

	public static bool InstallMe()
	{
		try
		{
			ManagedInstallerClass.InstallHelper(_installArgs);
		}
		catch
		{
			return true;
		}
		return true;
	}

	public static bool UninstallMe()
	{
		try
		{
			ManagedInstallerClass.InstallHelper(_uninstallArgs);
		}
		catch
		{
			return false;
		}
		return true;
	}
}

W pierwszych liniach deklaruję argumenty startowe dla narzędzia Installutil.exe. Następnie w konstruktorze definiuję ServiceProcessInstaller oraz ServiceInstaller (identyczne komponenty dodawane są podczas tworzenia instalatora poprzez Visual Studio). Tutaj warto nadmienić, iż nazwa usługi (ServiceName) jest kluczem do spojenia wszystkich elementów i poprawnej instalacji. Na koniec dwie oczywiste metody wywołujące API.

Przykładowe wykorzystanie powyższej klasy może wyglądać następująco:

static void Main(string[] args)
{
	if (args != null && args.Length > 0)
	{
		bool install = false;
		bool uninstall = false;

		foreach (string arg in args)
		{
			if (arg.Length > 1 && (arg[0] == '-' || arg[0] == '/'))
			{
				string argCommand = arg.Substring(1, arg.Length - 1).ToLower();

				switch (argCommand)
				{
					case "install":
					case "i":
						install = true;
						break;
					case "uninstall":
					case "u":
						uninstall = true;
						break;
				}
			}
		}

		if (install)
		{
			SelfInstaller.InstallMe();
		}
		else if (uninstall)
		{
			SelfInstaller.UninstallMe();
		}
	}
	else
	{
		ServiceBase[] servicesToRun =
		{
			new MainService(),
			// another service
		};

		ServiceBase.Run(servicesToRun);
	}
}

Po uruchomieniu (z prawami administratora) skompilowanego pliku exe z argumentem odpowiedzialnym za instalację, usługa pojawi się w Usługach lokalnych, a w logu powinna odłożyć się podobna do poniższej adnotacja.

Running a transacted installation.

Beginning the Install phase of the installation.
See the contents of the log file for the c:\Small projects\ServiceSelfInstaller\ServiceSelfInstaller\bin\Debug\ServiceSelfInstaller.exe assembly's progress.
The file is located at SelfInstaller.log.
Installing assembly 'c:\Small projects\ServiceSelfInstaller\ServiceSelfInstaller\bin\Debug\ServiceSelfInstaller.exe'.
Affected parameters are:
   showcallstack = true
   logtoconsole = 
   logfile = SelfInstaller.log
   assemblypath = c:\Small projects\ServiceSelfInstaller\ServiceSelfInstaller\bin\Debug\ServiceSelfInstaller.exe
   name = TestServiceToDoNothing
Installing service TestServiceToDoNothing...
Service TestServiceToDoNothing has been successfully installed.
Creating EventLog source TestServiceToDoNothing in log Application...

The Install phase completed successfully, and the Commit phase is beginning.
See the contents of the log file for the c:\Small projects\ServiceSelfInstaller\ServiceSelfInstaller\bin\Debug\ServiceSelfInstaller.exe assembly's progress.
The file is located at SelfInstaller.log.
Committing assembly 'c:\Small projects\ServiceSelfInstaller\ServiceSelfInstaller\bin\Debug\ServiceSelfInstaller.exe'.
Affected parameters are:
   showcallstack = true
   logtoconsole = 
   logfile = SelfInstaller.log
   assemblypath = c:\Small projects\ServiceSelfInstaller\ServiceSelfInstaller\bin\Debug\ServiceSelfInstaller.exe
   name = TestServiceToDoNothing

The Commit phase completed successfully.

The transacted install has completed.

Services

Właściwości usługi

Natomiast po deinstalacji adnotacja w logu wygląda jak poniżej oraz oczywiście usługa znika z listy usług.

The uninstall is beginning.
See the contents of the log file for the c:\Small projects\ServiceSelfInstaller\ServiceSelfInstaller\bin\Debug\ServiceSelfInstaller.exe assembly's progress.
The file is located at SelfInstaller.log.
Uninstalling assembly 'c:\Small projects\ServiceSelfInstaller\ServiceSelfInstaller\bin\Debug\ServiceSelfInstaller.exe'.
Affected parameters are:
   showcallstack = true
   logtoconsole = 
   logfile = SelfInstaller.log
   assemblypath = c:\Small projects\ServiceSelfInstaller\ServiceSelfInstaller\bin\Debug\ServiceSelfInstaller.exe
   name = TestServiceToDoNothing
Removing EventLog source TestServiceToDoNothing.
Service TestServiceToDoNothing is being removed from the system...
Service TestServiceToDoNothing was successfully removed from the system.

The uninstall has completed.

Kilka usług w jednym pliku exe

Istnieje możliwość uruchamiania kilku usług za pomocą jednego pliku exe (najlepszym przykładem są usługi systemu Windows, patrz svchost.exe). W tym celu należy zainstalować je pod różną nazwą (przypominam, iż spoiwem jest nazwa usługi).
Odnosząc się do mojej powyższej implementacji, wystarczyłoby zdefiniować kolejny ServiceInstaller z inną nazwą usługi i to wszystko.
Uruchamiając jedną z usług, system startuje ten sam plik exe, jednakże po nazwie usługi rozpoznaje, którą konkretnie usługę ma wystartować.

Podbij ↑

One thought on “SelfInstaller, czyli samoinstalująca się usługa

Skomentuj Tomasz Slota Anuluj pisanie odpowiedzi

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *