用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 )