用Visual C#建立Windows服務程式
一.Windows服務介紹:
Windows服務以前被稱作NT服務,是一些運行在Windows NT、Windows 2000和Windows XP等作業系統下使用者環境以外的程式。在以前,編寫Windows服務程式需要程式員很強的C或C++功底。然而現在在Visual Studio.Net下,你可以運用C++或Visual C#或Visual Basic.Net很輕鬆的建立一個Windows服務程式。同樣,你還可以運用其他任何與CLR相容的語言來建立Windows服務程式。本文就向大家介紹如何運用Visual C#來一步一步建立一個檔案監視的Windows服務程式,然後介紹如何安裝、測試和調試該Windows服務程式。
在介紹如何建立Windows服務程式以前,我先向大家介紹一些有關Windows服務的背景知識。一個Windows服務程式是在Windows作業系統下能完成特定功能的可執行檔應用程式。Windows服務程式雖然是可執行檔,但是它不像一般的可執行檔通過雙擊就能開始運行了,它必須有特定的啟動方式。這些啟動方式包括了自動啟動和手動啟動兩種。對於自動啟動的Windows服務程式,它們在Windows啟動或是重啟之後使用者登入之前就開始執行了。只要你將相應的Windows服務程式註冊到服務控制管理員(Service Control Manager)中,並將其啟動類別設為自動啟動就行了。而對於手動啟動的Windows服務程式,你可以通過命令列工具的NET START 命令來啟動它,或是通過控制台中管理工具下的服務一項來啟動相應的Windows服務程式(見圖1)。同樣,一個Windows服務程式也不能像一般的應用程式那樣被終止。因為Windows服務程式一般是沒有使用者介面的,所以你也要通過命令列工具或是下面圖中的工具來停止它,或是在系統關閉時使得Windows服務程式自動停止。因為Windows服務程式沒有使用者介面,所以基於使用者介面的API函數對其是沒有多大的意義。為了能使一個Windows服務程式能夠正常並有效在系統內容下工作,程式員必須實現一系列的方法來完成其服務功能。Windows服務程式的應用範圍很廣,典型的Windows服務程式包含了硬體控制、應用程式監視、系統級應用、診斷、報告、Web和檔案系統服務等功能。
圖1
二.建立Windows服務程式:
在介紹如何建立Windows服務程式以前,我先向大家介紹一下.Net架構下與Windows服務相關的命名空間和其中的類庫。.Net架構大大地簡化了Windows服務程式的建立和控制過程,這要歸功於其命名空間中的功能強大的類庫。和Windows服務程式相關的命名空間涉及到以下兩個:System.ServiceProcess和System.Diagnostics。
要建立一個最基本的Windows服務程式,我們只需要運用.Net架構下的System.ServiceProcess命名空間以及其中的四個類:ServiceBase、ServiceInstaller、ServiceProcessInstaller以及ServiceController,其體繫結構可見圖2。
圖2
其中ServiceBase類定義了一些可被其子類重載的函數,通過這些重載的函數,服務控制管理員就可以控制該Windows服務程式了。這些函數包括:OnStart()、OnStop()、OnPause()以及OnContinue()等四個。而且ServiceBase類的子類還可以重載OnCustomCommand()函數來完成一些特定的操作。通過重載以上的一些函數,我們就完成了一個Windows服務程式的基本架構,這些函數的重載方法如下:
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
protected override void OnPause()
{
}
protected override void OnContinue()
{
}
ServiceBase類還為我們提供了一些屬性,而這些屬性是任何Widnows服務程式所必須的。其中的ServiceName屬性指定了Windows服務的名稱,通過該名稱系統就可以調用Windows服務了,同時其它應用程式也可以通過該名稱來調用它的服務。而CanPauseAndContinue和CanStop屬性顧名思義就是允許暫停並恢複和允許停止的意思。
要使得一個Windows服務程式能夠正常運行,我們需要像建立一般應用程式那樣為它建立一個程式的進入點。在Windows服務程式中,我們也是在Main()函數中完成這個操作的。首先我們在Main()函數中建立一個Windows服務的執行個體,該執行個體應該是ServiceBase類的某個子類的對象,然後我們調用由基類ServiceBase類定義的一個Run()方法。然而Run()方法並不就開始了Windows服務程式,我們必須通過前面提到的服務控制管理員調用特定的控制功能來完成Windows服務程式的啟動,也就是要等到該對象的OnStart()方法被調用時服務才真正開始運行。如果你想在一個Windows服務程式中同時啟動多個服務,那麼只要在Main()函數中定義多個ServiceBae類的子類的執行個體對象就可以了,方法就是建立一個ServiceBase類的數組對象,使得其中的每個對象對應於某個我們已預先定義好的服務。
{
System.ServiceProcess.ServiceBase[] MyServices;
MyServices = new System.ServiceProcess.ServiceBase[] { new Service1(), new Service2() };
System.ServiceProcess.ServiceBase.Run(MyServices);
}
static void Main()
三.添加檔案監視服務:
瞭解了Windows服務的基本體繫結構和建立方法後,我們就可以試著往服務中添加一些實際的功能了。下面我將向大家介紹一個能監視本地檔案系統的檔案監視服務-FileMonitorService。該服務能根據預先設定的本地目錄路徑監視其中的檔案包括子檔案夾中的任何變化:檔案建立、檔案刪除、檔案改名、檔案修改。同時,該服務還為每種變化建立了一個相對應的計數器,計數器的作用就是反映該種變化的頻度。
首先,我們開啟Visual Studio.Net,建立一個Visual C#的Windows服務的項目,3所示:
圖3
在重載Windows服務的OnStart()函數之前,我們先給其類添加一些計數器對象,這些計數器分別對應了檔案的建立、刪除、改名以及修改等變化。一旦指定目錄中的檔案發生以上的某種變化,與其相對應的計數器就會自動加1。所有的這些計數器都是定義為PerformanceCounter類型的變數的,該類是包含在System.Diagnostics命名空間中的。
private System.Diagnostics.PerformanceCounter fileCreateCounter;
private System.Diagnostics.PerformanceCounter fileDeleteCounter;
private System.Diagnostics.PerformanceCounter fileRenameCounter;
private System.Diagnostics.PerformanceCounter fileChangeCounter;
之後我們便在類的InitializeComponent()方法中建立以上定義的各個計數器對象並確定其相關屬性。同時我們將該Windows服務的名稱設定為“FileMonitorService”,設定其即是允許暫停並恢複的又是允許停止的。
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.fileChangeCounter = new System.Diagnostics.PerformanceCounter();
this.fileDeleteCounter = new System.Diagnostics.PerformanceCounter();
this.fileRenameCounter = new System.Diagnostics.PerformanceCounter();
this.fileCreateCounter = new System.Diagnostics.PerformanceCounter();
fileChangeCounter.CategoryName = "File Monitor Service";
fileDeleteCounter.CategoryName = "File Monitor Service";
fileRenameCounter.CategoryName = "File Monitor Service";
fileCreateCounter.CategoryName = "File Monitor Service";
fileChangeCounter.CounterName = "Files Changed";
fileDeleteCounter.CounterName = "Files Deleted";
fileRenameCounter.CounterName = "Files Renamed";
fileCreateCounter.CounterName = "Files Created";
this.ServiceName = "FileMonitorService";
this.CanPauseAndContinue = true;
this.CanStop = true;
servicePaused = false;
}
接著就是重載OnStart()函數和OnStop()函數,OnStart()函數完成了一些必要的初始化工作。在.Net架構下,檔案的監視功能可以由FileSystemWatcher類來完成,該類是包含在System.IO命名空間下的。該Windows服務所要完成的功能包括了監視檔案的建立、刪除、改名和修改等變化,而FileSystemWatcher類包含所有了對應於這些變化的處理函數。
protected override void OnStart(string[] args)
{
FileSystemWatcher curWatcher = new FileSystemWatcher();
curWatcher.BeginInit();
curWatcher.IncludeSubdirectories = true;
curWatcher.Path =
System.Configuration.ConfigurationSettings.AppSettings
["FileMonitorDirectory"];
curWatcher.Changed += new FileSystemEventHandler(OnFileChanged);
curWatcher.Created += new FileSystemEventHandler(OnFileCreated);
curWatcher.Deleted += new FileSystemEventHandler(OnFileDeleted);
curWatcher.Renamed += new RenamedEventHandler(OnFileRenamed);
curWatcher.EnableRaisingEvents = true;
curWatcher.EndInit();
}
注意其中被監視的目錄是存放在一個應用程式設定檔中的,該檔案是一個XML類型的檔案。這種做法的好處就是我們不必重新編譯並發布該Windows服務而只要直接修改其設定檔就可以達到更改所要監視的目錄的功能了。
當該Windows服務啟動後,一旦被監視的目錄中的檔案發生某種變化,與其相對應的計數器的值便會相應的增加,方法很簡單,只要調用計數器對象的IncrementBy()即可。
private void OnFileChanged(Object source, FileSystemEventArgs e)
{
if( servicePaused == false )
{
fileChangeCounter.IncrementBy(1);
}
}
private void OnFileRenamed(Object source, RenamedEventArgs e)
{
if( servicePaused == false )
{
fileRenameCounter.IncrementBy(1);
}
}
private void OnFileCreated(Object source, FileSystemEventArgs e)
{
if( servicePaused == false )
{
fileCreateCounter.IncrementBy(1);
}
}
private void OnFileDeleted(Object source, FileSystemEventArgs e)
{
if( servicePaused == false )
{
fileDeleteCounter.IncrementBy(1);
}
}
OnStop()函數即是停止Windows服務的,在該Windows服務中,服務一旦停止,所有的計數器的值都應歸零,但是計數器並不提供一個Reset()方法,所以我們只好將計數器中的值減去當前值來達到這個目的。
protected override void OnStop()
{
if( fileChangeCounter.RawValue != 0 )
{
fileChangeCounter.IncrementBy(-fileChangeCounter.RawValue);
}
if( fileDeleteCounter.RawValue != 0 )
{
fileDeleteCounter.IncrementBy(-fileDeleteCounter.RawValue);
}
if( fileRenameCounter.RawValue != 0 )
{
fileRenameCounter.IncrementBy(-fileRenameCounter.RawValue);
}
if( fileCreateCounter.RawValue != 0 )
{
fileCreateCounter.IncrementBy(-fileCreateCounter.RawValue);
}
}
同時,因為我們的Windows服務是允許暫停並恢複的,所以我們還得重載OnPause()函數和OnContinue()函數,方法很簡單,只要設定前面定義的布爾值servicePaused即可。
protected override void OnPause()
{
servicePaused = true;
}
protected override void OnContinue()
{
servicePaused = false;
}
這樣,該Windows服務的主體部分已經完成了,不過它並不有用,我們還必須為其添加安裝檔案。安裝檔案為Windows服務的正確安裝做好了工作,它包括了一個Windows服務的安裝類,該類是重System.Configuration.Install.Installer繼承過來的。安裝類中包括了Windows服務運行所需的帳號資訊,使用者名稱、密碼資訊以及Windows服務的名稱,啟動方式等資訊。
[RunInstaller(true)]
public class Installer1 : System.Configuration.Install.Installer
{
/// <summary>
/// 必需的設計器變數。
/// </summary>
private System.ComponentModel.Container components = null;
private System.ServiceProcess.ServiceProcessInstaller spInstaller;
private System.ServiceProcess.ServiceInstaller sInstaller;
public Installer1()
{
// 該調用是設計器所必需的。
InitializeComponent();
// TODO: 在 InitComponent 調用後添加任何初始化
}
#region Component Designer generated code
/// <summary>
/// 設計器支援所需的方法 - 不要使用代碼編輯器修改
/// 此方法的內容。
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
// 建立ServiceProcessInstaller對象和ServiceInstaller對象
this.spInstaller =
new System.ServiceProcess.ServiceProcessInstaller();
this.sInstaller = new System.ServiceProcess.ServiceInstaller();
// 設定ServiceProcessInstaller對象的帳號、使用者名稱和密碼等資訊
this.spInstaller.Account =
System.ServiceProcess.ServiceAccount.LocalSystem;
this.spInstaller.Username = null;
this.spInstaller.Password = null;
// 設定服務名稱
this.sInstaller.ServiceName = "FileMonitorService";
// 設定服務的啟動方式
this.sInstaller.StartType =
System.ServiceProcess.ServiceStartMode.Automatic;
this.Installers.AddRange(
new System.Configuration.Install.Installer[]
{this.spInstaller, this.sInstaller });
}
#endregion
}
同樣,因為該Windows服務中運用到了計數器對象,我們也要為其添加相應的安裝檔案,安裝檔案的內容和作用與前面的類似。限於篇幅,這裡就不給出相應的代碼了,有興趣的讀者可以參考文後附帶的原始碼檔案。
到此為止,整個Windows服務已經構建完畢,不過Windows服務程式和一般的應用程式不同,它不能直接調試運行。如果你直接在IDE下試圖調試運行之,就會報出4所示提示。
圖4
根據其中提示,我們知道安裝Windows服務需要用到一個名為InstallUtil.exe的命令列工具。而運用該工具安裝Windows服務的方法是非常簡單的,安裝該Windows服務的命令如下:
installutil FileMonitorService.exe
而要卸載該Windows服務,你只要輸入如下的命令即可:
installutil /u FileMonitorService.exe
Windows服務安裝成功後,它便會出現在服務控制管理員中,5所示。
圖5
這樣,該檔案監視的Windows服務就完成了,一旦我們對被監視的目錄中的檔案進行操作,相應的計數器就會運作,起到監視檔案變化的作用。不過這個功能對於一般的使用者而言沒多大意義,然而你可以在此基礎上添加新的功能,比如構建一個背景檔案處理系統,一旦被監視的目錄中的檔案發生某種變化,Windows服務便對其進行特定的操作,而終端使用者就不必去關心幕後處理程式是如何?的了。
四.總結:
本文向大家介紹了Windows服務的一些基本概念和構建一般的Windows服務所需的方法,同時還向大家展示了一個具有檔案監視功能的Windows服務程式。通過本文,讀者應該能體會到構建Windows服務並不是想象中的那麼複雜,這主要還得歸功於.Net架構為我們所作的大量努力。同時,希望大家能在本文給出的執行個體的基礎上構建更加完善和更加強大的Windows服務程式。最後希望本文對大家能有不少協助。
(註:原始碼檔案為Source.rar )