Artykuły

A A A
Drukuj Ekportuj do PDF
Opublikowane: 2004.03.08 1:33 | Jakub Gutkowski | Aktualizacja: 2010.01.21 2:04

Tworzenie usług (Windows Services) w VB.NET

Na przykładowej małej usłudze

Implementacja usługi

Na samym początku należy uruchomić Visual Studio .NET i stworzyć nowy projekt (Ctrl+Shift+N) VB.NET Windows Service o podanej nazwie (w tym przypadku np.: UslugaMonitorujacaPliki), jak to zobrazowuje rysunek poniżej.

Rys. 1 Tworzenie nowego projektu usługi windows w VB.NET

Po kliknięciu na przycisk OK VS.NET tworzy szablon usługi wraz z dwoma stworzonymi procedurami Sub:

  • OnStart()

  • OnStop()

Zauważcie, iż utworzona automatycznie klasa Service1 usługi dziedziczy po klasie System.ServiceProcess.ServiceBase dostępnej w .NET Framework.

Imports System.ServiceProcess
Public Class Service1
     Inherits System.ServiceProcess.ServiceBase
     Protected Overrides Sub OnStart(ByVal args() As String)
          Add code here to start your service. This method should set things
          ´ in motion so your service can do its work.
     End Sub
     Protected Overrides Sub OnStop()
          ´ Add code here to perform any tear-down necessary to stop your service.
     End Sub
End Class

Kod 1 Procedury OnStart() i OnStop()

Jak widać na kodzie zamieszczonym wyżej, procedura OnStart() pobiera jeden parametr wejściowy args(), który za pomocą tablicy znaków umożliwia użytkownikowi na przekazanie parametrów startu do usługi. Dodatkowo do dwóch procedur utworzonych za pomocą VS.NET istnieje możliwość dodania jeszcze pięciu procedur dostępnych poprzez klasę ServiceBase:

  • OnContinue()

  • OnCustomCommand()

  • OnPause()

  • OnPowerEvent()

  • OnShutdown()

W artykule tym, zostaną wykorzystane tylko dwie funkcje OnStart() i OnStop() niezbędne by wytworzyć usługę. Pozwoli ona monitorować określone typy dokumentów (np.: dokumenty Word, tekstowe czy też HTML) znajdujące się na określonej ścieżce na dysku twardym. Dzięki tej opcji, użytkownik będzie mógł łatwo przejrzeć informacje o tym, co się ostatnio działo w jego katalogu, na plikach, które go interesują.

Przy tworzeniu usługi zostanie wykorzystana klasy FileSystemWatcher, dostarczona z .NET Framework.

Na początku należy wytworzyć kod, który będzie ustawiał parametry działania usługi. Tym celu przed deklaracja klasy Service1 należy umieścić import przestrzeni nazw .NET Framework System.IO, a po deklaracji klasy, deklarację zmiennej globalnej odpowiedzialnej za zapis do pliku. Następną czynnością jest oprogramowanie procedury OnStart(), która wykorzysta zaimportowaną przestrzeń nazw oraz zmienną globalną. Kod poniżej pokazuje jedną z możliwych implementacji procedury OnStart():

     Imports System.IO
     ´ ...
     Dim sr As StreamWriter
     Protected Overrides Sub OnStart(ByVal args() As String)
          ´ Tworzy nowy Service obserwujacy pliki i ustawia jego wlasciwosci
          Dim fswFilesWatcher As New FileSystemWatcher
          Dim strErr As String
          Try
               ´ parametr startu
               fswFilesWatcher.Path = args(0).ToString
               fswFilesWatcher.IncludeSubdirectories = args(1).ToString
               fswFilesWatcher.NotifyFilter = Convert.ToInt16(args(2))
               fswFilesWatcher.Filter = args(3).ToString
          Catch e As Exception
          ´ Co zrobic wrazie bledu
          End Try
          ´ Event Handlers
          AddHandler fswFilesWatcher.Changed, AddressOf OnFileUpdate
          AddHandler fswFilesWatcher.Created, AddressOf OnFileChanged
          AddHandler fswFilesWatcher.Deleted, AddressOf OnFileChanged
          AddHandler fswFilesWatcher.Renamed, AddressOf OnFileRenamed
          Me.AutoLog = False
          Const FILE_NAME = "c:\MonitorLog.log"
          If Not File.Exists(FILE_NAME) Then
               sr = File.CreateText(FILE_NAME)
          Else
               sr = New StreamWriter(FILE_NAME, True)
          End If
          sr.WriteLine("-------------START-------------")
          sr.WriteLine("MonitorowaniePlikow started At " & Date.Now)
          sr.WriteLine("-------------------------------")
          fswFilesWatcher.EnableRaisingEvents = True
     End Sub

Kod 2 Procedura OnStart()

Na początku został utworzony obiekt FileSystemWatcher a następnie jego atrybutom zostały przypisane parametry (przekazane do zmiennej wejściowej args()) inicjalizujące usługę. Atrybutom, którym zostały przypisane wartości parametru args() są dość intuicyjne (ze względu na nazwę parametru):

  • Path – ścieżka do katalogu, który ma być monitorowany.

  • IncludeSubdirectories – czy podkatalogi też mają być monitorowane.

  • NotifyFilter – ustawienia typu aktywności plików/katalogów, które mają być monitorowane. Do aktywności zalicza się:

  • LastAccess – data i godzina ostatniego otworzenia pliku

  • LastWrite – data i godzina ostatniego zapisania pliku

  • FileName – data i godzina zmiany nazwy pliku

  • DirectoryName – data i godzina zmiany nazwy katalogu

  • Filter – określa, jakiego typu pliki mają być monitorowane np.: *.txt oznacza, że pliki tekstowe mają być monitorowane.

Następnie należy określić handlery zdarzeń, które przechwycą zdarzenia (modyfikacja, utworzenia, usunięcie, zmiana nazwy) na plikach. Poniższy kod ilustruje obsłużenie takich zdarzeń:

     Public Sub OnFileChanged(ByVal objSource As Object, ByVal e As FileSystemEventArgs)
          sr.WriteLine(Date.Now & " : " & e.ChangeType.ToString & " : File : " & e.FullPath)
     End Sub
     Public Sub OnFileUpdate(ByVal objSource As Object, ByVal e As FileSystemEventArgs)
          sr.WriteLine(Date.Now & " : Updated : File : " & e.FullPath)
     End Sub
     Public Sub OnFileRenamed(ByVal objSource As Object, ByVal e As RenamedEventArgs)
          sr.WriteLine(Date.Now & " : Renamed : File : " & e.OldFullPath & " to " & e.FullPath)
     End Sub

Kod 3 Kod do przechwytywania zdarzeń

Standardowo, każda usługa zapisuje swoją aktywność (uruchomienie, zatrzymanie, za pauzowanie) w logu aplikacji, dostępnym pod podglądem zdarzeń. By wyłączyć wpisy do logu aplikacji wystarczy ustawić parametr AutoLog na False w procedurze OnStart():

   Me.AutoLog = False

Kod 4 Wyłączenie wpisów do logu aplikacji

Dodatkowo przy uruchamianiu usługi, należy określić plik, do którego będą zapisywane informacje o działaniach na plikach. Jeżeli taki plik nie istnieje to należy go utworzyć, w przeciwnym wypadku otworzyć i zacząć dopisywać informacje na jego końcu.

Const FILE_NAME = "c:\files.log"
If Not File.Exists(FILE_NAME) Then
     sr = File.CreateText(FILE_NAME)
Else
     sr = New StreamWriter(FILE_NAME, True)
End If
sr.WriteLine("-------------START-------------")
sr.WriteLine("MonitorowaniePlikow started At " & Date.Now)
sr.WriteLine"-------------------------------")

Kod 5 Utworzenie nowego pliku lub otworzenie już istniejącego

Na końcu należy ustawić parametr EnableRaisingEvents (który określa czy FileSystemWatcher ma monitorować pliki czy nie) na True:

fswFilesWatcher.EnableRaisingEvents = True

Kod 6 Włączenie monitorowania plików

Kod procedury OnStart() włączał i ustawiał zapisywanie monitorowania plików, teraz pora na ustawienie wyłączenia monitorowania plików. W tym celu do procedury OnStop() należy dodać następujący kod (jedyna, ważne linijka to sr.Colse(), zamykająca działanie na pliku, reszta może być dowolnie dobierana w zależności od wymagań):

     Protected Overrides Sub OnStop()
          ´ Zatrzymanie serwisu
          sr.WriteLine("-------------------------------")
          sr.WriteLine("MonitorowaniePlikow stopped At " & Date.Now)
          sr.WriteLine("--------------END--------------")
          sr.Close()
     End Sub

Kod 7 Wyłączenie usługi monitorowania plików

Instalowanie usługi

Teraz, gdy kod niezbędny do uruchomienia i zatrzymania usługi został już wytworzony, należy utworzyć kod, który zainstaluje ją w systemie, gdyż przetestowanie jej za pomocą debugingu (F5) czy po prostu otworzeniu utworzonego pliku wykonywalnego *.exe jest niewykonalne. Zanim jednak doda się instalację, należy ustawić nazwę usługi w parametrze ServiceName tak jak to pokazuje rysunek poniżej:

Rys. 2 Ustawienia parametru ServiceName

Następnie w tym samym oknie należy kliknąć Add Installer tak jak to pokazano na rysunku 3.

Rys. 3 Dodanie instalatora usługi

Add Installer doda nowy plik do projektu z dwoma nowymi instancjami klas kolejno ServiceProcessInstaller i ServiceInstaller, które ułatwią konfigurację instalacji i odinstalowania usługi. Obie instancje należy skonfigurować tak jak jest to pokazane na rysunku 4.

Rys. 4 Konfiguracja instancji klas ServiceProcessInstaller i ServiceInstaller

Parametr Account z wartością LocalSystem, używa konta odgrywającego rolę użytkownika, na lokalnym komputerze, bez przywilejów jego plusem jest także umożliwienie anonimowego dostępu do dowolnego zdalnego serwera. Jeżeli w tym polu zostanie podana wartość User, system po instalacji usługi poprosi o podanie użytkownika i hasła do konta, z którego usługa będzie korzystała.

Parametr StartType z standardową wartością Manual mówi usłudze o nie uruchamianiu się dopóki użytkownik tego nie zechce.

Uwaga: Należy się upewnić czy parametr ServiceName (jak pokazano na rysunku 2) ma taką samą nazwę jak parametr ServiceName dla instancji klasy ServiceInstaller (jak pokazano na rysunku 4).

Teraz, gdy wszystkie parametry instalacji zostały ustawione pora zbudować projekt tak jak to pokazano na rysunku 5.

Rys. 5 Budowanie naszego projektu

Posiadając plik wykonywalny, powstały w wyniku zbudowania usługi, trzeba go zainstalować w systemie za pomocą programu InstallUtil dostępnego wraz z .NET Framework. Za pomocą linii poleceń (Start | Uruchom | cmd) należy przejść do katalogu bin aplikacji monitorowania plików i wprowadzić następującą linijkę:

C:\Documents and Settings\Administrator\My Documents\Visual Studio Projects\Uslu
gaMonitorujacaPliki\bin>InstallUtil UslugaMonitorujacaPliki.exe

Kod 8 Instalacja usługi z cmd

Uwaga: Jeżeli konsola nie rozpozna programu InstallUtil to zawsze można ten program uruchomić z konsoli Visual Studio .NET Command Prompt (Visual Studio .NET 2003 Command Prompt dla VS.NET 2003) dostępnej w menu Visual Studio .NET Tools.

Po instalacji usługi użytkownik może się upewnić czy aby na pewno została ona zainstalowana w systemie. Może to sprawdzić za pomocą przystawki mmc Usługi dostępnej w narzędziach administratora lub poprzez mmc w Start | Uruchom.

Rys. 6 Usługi

Usługa może zostać uruchomiona za pomocą kliknięcia na niej prawym przyciskiem myszy i wybrawszy opcję Start. Jednakże usługa nie zadziała, zostanie od razu po uruchomieniu zatrzymana. Dzieje się tak, gdyż wymaga ona podania parametrów wejściowych, które można określić w przystawce mmc Usługi, jednak sposób ich wprowadzania wcale nie jest przyjazny użytkownikowi.

Uruchomienie usługi za pomocą kontrolera usługi

Używanie przystawki Usługi do uruchamiania i zatrzymywania wytworzonej wcześniej usługi nie jest przyjazne dla użytkownika, w szczególności przez atrybuty, które muszą zostać do niej przekazane. W celu ułatwienia przekazywania parametrów do usługi należy wytworzyć prostą aplikację windowsową, która umożliwi kontrolowanie wytworzonej usługi. Okno główne aplikacji kontrolującej usługę może wyglądać tak jak pokazano na rysunku 7.

Rys. 7 Główne okno aplikacji

Pierwsza zakładka będzie kontrolowała usługę a druga wszystkie pozostałe usługi dostępne w systemie. By tworzona aplikacja miała dostęp do usług należy na główne okno aplikacji dodać komponent ServiceController, tak jak pokazano to na rysunku 8.

Rys. 8 Dodanie komponentu ServiceController

Komponent ServiceController umożliwia na manipulację (taką jak zatrzymanie, uruchomienie, za pauzowanie) usługami. Wymaga on ustawienia parametrów:

  • MachineName – nazwa komputera

  • ServiceName – nazwa usługi

Zakładka pierwsza umożliwia skonfigurowanie parametrów uruchomienia wytworzonej wcześniej usługi. Po kliknięciu na przycisk Start, ustawione parametry są pakowane do tablicy ciągu znaków i przekazywane do usługi za pomocą ServiceController:

     Private Sub cmdStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdStart.Click
          If scServiceController.Status = ServiceProcess.ServiceControllerStatus.Running Then Return
          Dim args(3) As String
          args(0) = txtPath.Text
          args(1) = chkIncludeSubDir.Checked
          If chkLastAccess.Checked Then args(2) = args(2) Or NotifyFilters.LastAccess
          If chkLastWrite.Checked Then args(2) = args(2) Or NotifyFilters.LastWrite
          If chkFileNameChange.Checked Then args(2) = args(2) Or NotifyFilters.FileName
          If chkDirectoryNamechange.Checked Then args(2) = args(2) Or NotifyFilters.DirectoryName
          args(3) = cboFileType.Text
          scServiceController.Start(args)
          Displaystatus("MonitorowaniePlikow")
     End Sub

Kod 9 Pakowanie zaznaczonych parametrów i przesłanie ich do usługi

By za pauzować usługę, należy dodać kod odpowiedzialny za zdarzenia Click przycisku Pause. Kod powinien wyglądać następująco:

     Private Sub cmdPause_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdPause.Click
          If scServiceController.CanPauseAndContinue Then
          scServiceController.Pause()
          Displaystatus("MonitorowaniePlikow")
          Else
          MsgBox("Service cannot be paused.")
          End If
     End Sub

Kod 10 Pauzowanie usługi

Zwróćcie uwagę na sprawdzenie czy usługa może być za pauzowana za pomocą parametru CanPauseAndContinue. Z powodu, niezaimplementowania procedury OnPause() w usłudze, nie sprawdzenie czy może ona być za pauzowana spowodowałoby błąd. Podobnie, by zatrzymać usługę, należy upewnić się czy usługa nie została już wcześniej zatrzymana:

     Private Sub cmdStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdStop.Click
          If scServiceController.Status <> ServiceProcess.ServiceControllerStatus.Stopped Then
          scServiceController.Stop()
          Displaystatus("MonitorowaniePlikow")
          End If
     End Sub

Kod 11 Zatrzymanie usługi

Nie został tutaj zaimplementowany error handling, gdyż usługa może być w fazie przejściowej, przez co error handling nie byłby w 100% pewny – wręcz na pewno zadziałałby przeciwnie do zamierzeń programisty.

Przycisk Show Status, wyświetla status usługi

     Private Sub cmdShowStatus_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdShowStatus.Click
          Displaystatus("MonitorowaniePlikow")
     End Sub

Kod 12 Kod ukryty pod przyciskiem Show Status

Zwróćcie uwagę, iż ServiceController musi zostać odświeżony by móc wyświetlić status usługi.

Teraz, gdy zakładka pierwsza jest już oprogramowana, pora stworzyć i oprogramować zakładkę drugą, która wygląda tak jak pokazano na rysunku 9.

Rys. 9 Druga zakładka

Przycisk Get Services wyświetla wszystkie zainstalowane usługi w systemie na niżej umieszczonej kontrolce ListBox.

     Private Sub cmdGetServices_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdGetServices.Click
          lstServices.Items.Clear()
          Dim controller As New System.ServiceProcess.ServiceController
          Dim services() As System.ServiceProcess.ServiceController
          Dim i As Integer
          services = controller.GetServices()
          For i = 0 To services.Length - 1
               lstServices.Items.Add(services(i).DisplayName)
          Next
     End Sub

Kod 13 Pobranie listy zainstalowanych usług w systemie

Metoda GetServices() klasy ServiceController zwraca listę wszystkich zainstalowanych usług w systemie za pomocą tablicy elementów, która potem zostaje przypisana do kontrolki ListBox. By uruchomić, zatrzymać lub za pauzować dowolną usługę w systemie, wystarczy przekazać jej nazwę do odpowiedniej metody (Start(), Stop(), Pause()) klasy ServiceController.

     Private Sub cmdStart2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdStart2.Click
          Dim controller As New System.ServiceProcess.ServiceController(lstServices.SelectedItem)
          controller.Start()
          Displaystatus(lstServices.SelectedItem)
     End Sub
     Private Sub cmdPause2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdPause2.Click
          Dim controller As New System.ServiceProcess.ServiceController(lstServices.SelectedItem)
          If controller.CanPauseAndContinue Then
               controller.Pause()
               Displaystatus(lstServices.SelectedItem)
          Else
               MsgBox("Service cannot be paused")
          End If
     End Sub
     Private Sub cmdStop2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdStop2.Click
          Dim controller As New System.ServiceProcess.ServiceController(lstServices.SelectedItem)
          If controller.Status <> ServiceProcess.ServiceControllerStatus.Stopped Then
               controller.Stop()
               Displaystatus(lstServices.SelectedItem)
          End If
     End Sub

Kod 14 Kod uruchamiający, zatrzymujący i pauzujący wybraną usługę

To już wszystko, teraz wystarczy uruchomić aplikację i ją przetestować. Dla testu można ustawić monitorowanie wszystkich plików (wraz z podkatalogami) w katalogu c:\Test (jeżeli katalog nie istnieje, utworzyć go) a następnie uruchomić usługę za pomocą przycisku Start. Teraz w monitorowanym katalogu należy utworzyć nowy plik tekstowy a następnie go usunąć i zatrzymać usługę monitorowania plików przyciskiem Stop. Utworzony plik logu aplikacji na dysku c:\MonitorLog.log powinien wyglądać mniej więcej tak:

-------------START-------------
MonitorowaniePlikow started At 2004-01-31 22:48:13
-------------------------------
2004-01-31 22:48:19 : Created : File : C:\Test\new text document.txt
2004-01-31 22:48:21 : Deleted : File : C:\Test\new text document.txt
-------------------------------
MonitorowaniePlikow stopped At 2004-01-31 22:48:25
--------------END--------------

Kod 15 Zrodlo pliku monitorowania

W kodzie dołączonym do artykułu kontroler usługi monitorowania plików zawiera dodatkowo opcje ukrywania aplikacji w zasobniku systemowym (system tray), dzięki czemu może ona pozostać uruchomiona przez cały czas działania systemu i nie przeszkadza w pracy użytkownikowi.

Załączniki:


Podobne artykuły

Komentarze 7 Masz uwagi do tej strony? Napisz

DRECH 2010.01.21 1:10
0 oceń pozytywnie   oceń negatywnie 0
avatar
 
Do recenzenta: Zwracając komuś uwagę na błędy warto samemu ich unikać... Tak poważnie to artykuł jest bardzo przeciętny. Więcej opisów kreatorów niż samych usług. Brakuje mi również kilku słów na temat zarządzania usługami przez SCM. No i trochę za dużo o tematach pobocznych (FileSystemWatcher - przecież ostatnio były 2 artykuły na ten temat).
skorczyk348 2010.01.21 1:10
0 oceń pozytywnie   oceń negatywnie 0
avatar
 
Bo mi się podoba komentarz recenzenta, który sam robi literówki :)
testa1435 2010.01.21 1:10
0 oceń pozytywnie   oceń negatywnie 0
avatar
 
:-(
User 79341 2010.01.21 1:10
0 oceń pozytywnie   oceń negatywnie 0
avatar
 
za duzo nie potrzebnych zrzutow z vstudio; bledy jezykowe (czy nikt inny nie czyta tego przed opublikowaniem?); brak konkretow (po co, dlaczego), samo rozwiazanie jakiegos problemu autora
User 108709 2010.01.21 1:10
0 oceń pozytywnie   oceń negatywnie 0
avatar
 
Brak wstępu, planu, artykuł nie na temat...
a.agaciak3727 2010.01.21 1:10
0 oceń pozytywnie   oceń negatywnie 0
avatar
 
do poprzednich komentatorów zwłaszcza moderatora - komentując w ten sposób cudzą pracę nie przyczynisz sie do wzrostu ilości chętnych do pisania artykułów i serwis umrze, a szkoda. Jesli macie konstruktywne uwagi napiszczie rozszerzenie artykułu - czy tak nie lepiej?
kamis19823788 2010.01.21 1:10
0 oceń pozytywnie   oceń negatywnie 0
avatar
 
Podobało sie: Jasno, konkternie, przystępnie Nie podobało się: Błędy, błędy - może to nie konkurs ortografi (całe szczęście :))ale ktoś rzeczywiście powinien to wcześniej sprawdzić. PS. komentarz ":-(" to chyba jakaś dziecinada! takie rzeczy powinny być usówane.

Dodaj komentarz

avatar

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

Autor Jakub Gutkowski
avatar
 

Załóż konto
CodeGuru to miejsce dla każdego programisty. Przez lata portal rozwijany był siłami społeczności i to właśnie społeczność programistów jest tutaj najważniejsza. CG od wielu lat gromadzi wokół siebie coraz większą grupę pasjonatów. Warto być jej częścią!

Dowiedz się więcej o CodeGuru

Geek Club - Windows Phone

 

MetroOne

Idź na górę strony