如何用.NET建立Windows服務

咱們將研究如何建立一個做爲Windows服務的應用程序。內容包含什麼是Windows服務,如何建立、安裝和調試它們。會用到System.ServiceProcess.ServiceBase命名空間的類。
什麼是Windows服務? 

   Windows服務應用程序是一種須要長期運行的應用程序,它對於服務器環境特別適合。它沒有用戶界面,而且也不會產生任何可視輸出。任何用戶消息都會被寫進Windows事件日誌。計算機啓動時,服務會自動開始運行。它們不要用戶必定登陸才運行,它們能在包括這個系統內的任何用戶環境下運行。經過服務控制管理器,Windows服務是可控的,能夠終止、暫停及當須要時啓動。

   Windows 服務,之前的NT服務,都是被做爲Windows NT操做系統的一部分引進來的。它們在Windows 9x及Windows Me下沒有。你須要使用NT級別的操做系統來運行Windows服務,諸如:Windows NT、Windows 2000 Professional或Windows 2000 Server。舉例而言,以Windows服務形式的產品有:Microsoft Exchange、SQL Server,還有別的如設置計算機時鐘的Windows Time服務。


建立一個Windows服務 
  咱們即將建立的這個服務除了演示什麼也不作。服務被啓動時會把一個條目信息登記到一個數據庫當中來指明這個服務已經啓動了。在服務運行期間,它會在指定的時間間隔內按期建立一個數據庫項目記錄。服務中止時會建立最後一條數據庫記錄。這個服務會自動向Windows應用程序日誌當中登記下它成功啓動或中止時的記錄。

  Visual Studio .NET可以使建立一個Windows服務變成至關簡單的一件事情。啓動咱們的演示服務程序的說明概述以下。

1. 新建一個項目
2. 從一個可用的項目模板列表當中選擇Windows服務
3. 設計器會以設計模式打開
4. 從工具箱的組件表當中拖動一個Timer對象到這個設計表面上 (注意: 要確保是從組件列表而不是從Windows窗體列表當中使用Timer) 
5. 設置Timer屬性,Enabled屬性爲False,Interval屬性30000毫秒
6. 切換到代碼視圖頁(按F7或在視圖菜單當中選擇代碼),而後爲這個服務填加功能


Windows服務的構成 
  在你類後面所包含的代碼裏,你會注意到你所建立的Windows服務擴充了System.ServiceProcess.Service類。全部以.NET方式創建的Windows服務必須擴充這個類。它會要求你的服務重載下面的方法,Visual Studio默認時包括了這些方法。

• Dispose – 清除任何受控和不受控資源(managed and unmanaged resources)
• OnStart – 控制服務啓動
• OnStop – 控制服務中止

數據庫表腳本樣例  在這個例子中使用的數據庫表是使用下面的T-SQL腳本建立的。我選擇SQL Server數據庫。你能夠很容易修改這個例子讓它在Access或任何你所選擇的別的數據庫下運行。 

CREATE TABLE [dbo].[MyServiceLog] (
[in_LogId] [int] IDENTITY (1, 1) NOT NULL,
[vc_Status] [nvarchar] (40) 
COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[dt_Created] [datetime] NOT NULL
) ON [PRIMARY]

 

 
 
吧友202.127.125.*
Windows服務樣例 
  下面就是我命名爲MyService的Windows服務的全部源代碼。大多數源代碼是由Visual Studio自動生成的。

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.ServiceProcess;

namespace CodeGuru.MyWindowsService
{
public class MyService : System.ServiceProcess.ServiceBase
{
private System.Timers.Timer timer1;
/// <remarks> 
/// Required designer variable.
/// </remarks>
private System.ComponentModel.Container components = null;

public MyService()
{
// This call is required by the Windows.Forms 
// Component Designer.
InitializeComponent();
}

// The main entry point for the process
static void Main()
{
System.ServiceProcess.ServiceBase[] ServicesToRun;


ServicesToRun = new System.ServiceProcess.ServiceBase[] 
{ new MyService() };

System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}

/// <summary> 
/// Required method for Designer support - do not modify 
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.timer1 = new System.Timers.Timer();
((System.ComponentModel.ISupportInitialize)
(this.timer1)).BeginInit();
// 
// timer1
// 
this.timer1.Interval = 30000;
this.timer1.Elapsed += 
new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
// 
// MyService
// 
this.ServiceName = "My Sample Service";
((System.ComponentModel.ISupportInitialize)
(this.timer1)).EndInit();

}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null) 
{
components.Dispose();
}
}
base.Dispose( disposing );
}

/// <summary>
/// Set things in motion so your service can do its work.
/// </summary>
protected override void OnStart(string[] args)
{
this.timer1.Enabled = true;
this.LogMessage("Service Started");
}

/// <summary>
/// Stop this service.
/// </summary>
protected override void OnStop()
{
this.timer1.Enabled = false;
this.LogMessage("Service Stopped");
}

/*
* Respond to the Elapsed event of the timer control
*/
private void timer1_Elapsed(object sender, 
System.Timers.ElapsedEventArgs e)
{
this.LogMessage("Service Running");
}

/*
* Log specified message to database
*/
private void LogMessage(string Message)
{
SqlConnection connection = null;
SqlCommand command = null;
try
{
connection = new SqlConnection( 
"Server=localhost;Database=SampleDatabase;Integrated 
Security=false;User Id=sa;Password=;");
command = new SqlCommand(
"INSERT INTO MyServiceLog (vc_Status, dt_Created) 
VALUES ('" + Message + "',getdate())", connection);
connection.Open();
int numrows = command.ExecuteNonQuery();
}
catch( Exception ex )
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
finally
{
command.Dispose();
connection.Dispose();
}
}
}
}
回覆
  • 2樓
  • 2006-02-08 15:31
 
 
吧友202.127.125.*
安裝Windows服務 
  Windows服務不一樣於普通Windows應用程序。不可能簡簡單單地經過運行一個EXE就啓動Windows服務了。安裝一個Windows服務應該經過使用.NET Framework提供的InstallUtil.exe來完成,或者經過諸如一個Microsoft Installer (MSI)這樣的文件部署項目完成。


添加服務安裝程序 
  建立一個Windows服務,僅用InstallUtil程序去安裝這個服務是不夠的。你必須還要把一個服務安裝程序添加到你的Windows服務當中,這樣便於InstallUtil或是任何別的安裝程序知道應用你服務的是怎樣的配置設置。

1. 將這個服務程序切換到設計視圖
2. 右擊設計視圖選擇「添加安裝程序」
3. 切換到剛被添加的ProjectInstaller的設計視圖
4. 設置serviceInstaller1組件的屬性: 
1) ServiceName = My Sample Service
2) StartType = Automatic
5. 設置serviceProcessInstaller1組件的屬性 
1) Account = LocalSystem
6. 生成解決方案

  在完成上面的幾個步驟以後,會自動由Visual Studio產生下面的源代碼,它包含於ProjectInstaller.cs這個源文件內。

using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;

namespace CodeGuru.MyWindowsService
{
/// <summary>
/// Summary description for ProjectInstaller.
/// </summary>
[RunInstaller(true)]
public class ProjectInstaller : 
System.Configuration.Install.Installer
{
private System.ServiceProcess.ServiceProcessInstaller 
serviceProcessInstaller1;
private System.ServiceProcess.ServiceInstaller serviceInstaller1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public ProjectInstaller()
{
// This call is required by the Designer.
InitializeComponent();

// TODO: Add any initialization after the InitComponent call
}

#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new 
System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new 
System.ServiceProcess.ServiceInstaller();
// 
// serviceProcessInstaller1
// 
this.serviceProcessInstaller1.Account = 
System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
// 
// serviceInstaller1
// 
this.serviceInstaller1.ServiceName = "My Sample Service";
this.serviceInstaller1.StartType = 
System.ServiceProcess.ServiceStartMode.Automatic;
// 
// ProjectInstaller
// 
this.Installers.AddRange(new 
System.Configuration.Install.Installer[] 
{this.serviceProcessInstaller1, this.serviceInstaller1});
}
#endregion
}
}
回覆
  • 3樓
  • 2006-02-08 15:32
 
 
吧友202.127.125.*
用InstallUtil安裝Windows服務 
  如今這個服務已經生成,你須要把它安裝好才能使用。下面操做會指導你安裝你的新服務。

1. 打開Visual Studio .NET命令提示
2. 改變路徑到你項目所在的bin\Debug文件夾位置(若是你以Release模式編譯則在bin\Release文件夾)
3. 執行命令「InstallUtil.exe MyWindowsService.exe」註冊這個服務,使它創建一個合適的註冊項。
4. 右擊桌面上「個人電腦」,選擇「管理」就能夠打計算機管理控制檯
5. 在「服務和應用程序」裏面的「服務」部分裏,你能夠發現你的Windows服務已經包含在服務列表當中了
6. 右擊你的服務選擇啓動就能夠啓動你的服務了

  在每次須要修改Windows服務時,這就會要求你卸載和從新安裝這個服務。不過要注意在卸載這個服務前,最好確保服務管理控制檯已經關閉,這會是一個很好的習慣。若是沒有這樣操做的話,你可能在卸載和重安裝Windows服務時會遇到麻煩。僅卸載服務的話,能夠執行相的InstallUtil命令用於註銷服務,不過要在後面加一個/u命令開關。


調試Windows服務 
  從另外的角度度看,調試Windows服務毫不同於一個普通的應用程序。調試Windows服務要求的步驟更多。服務不能象你對普通應用程序作的那樣,只要簡單地在開發環境下執行就能夠調試了。服務必須首先被安裝和啓動,這一點在前面部分咱們已經作到了。爲了便於跟蹤調試代碼,一旦服務被啓動,你就要用Visual Studio把運行的進程附加進來(attach)。記住,對你的Windows服務作的任何修改都要對這個服務進行卸載和重安裝。


附加正在運行的Windows服務 
  爲了調試程序,有些附加Windows服務的操做說明。這些操做假定你已經安裝了這個Windows服務而且它正在運行。

1. 用Visual Studio裝載這個項目 
2. 點擊「調試」菜單
3. 點擊「進程」菜單
4. 確保 顯示系統進程 被選
5. 在 可用進程 列表中,把進程定位於你的可執行文件名稱上點擊選中它
6. 點擊 附加 按鈕
7. 點擊 肯定
8. 點擊 關閉
9. 在timer1_Elapsed方法裏設置一個斷點,而後等它執行


總結 
  如今你應該對Windows服務是什麼,以及如何建立、安裝和調試它們有一個粗略的認識了。Windows服務的額處的功能你能夠自行研究。這些功能包括暫停(OnPause)和恢復(OnContinue)的能力。暫停和恢復的能力在默認狀況下沒有被啓用,要經過Windows服務屬性來設置。
回覆
  • 4樓
  • 2006-02-08 15:33
 
 
 
再看一個例子:

Windows服務是獨立於登陸用戶而工做的Windows應用程序,它一般在計算機啓動時開始執行,且經常連續執行,直到計算機關閉爲止。像Exchange Server,IIS和殺毒軟件等都使用這種方式,這樣就能夠獨立於某一用戶並且能夠在任何用戶登陸前來運行,同時也能夠服務於全部的進程,從而以一種服務的形式存在。 

  正由於Windows服務有着這麼多的特性,所以,當須要一些特殊功能的時候就能夠考慮使用Windows服務來解決問題。好比下面咱們要作的這個例子。對於咱們這些程序設計人員,計算機是在一塊兒工做時間最長的夥伴,天天都會對着它的屏幕八個小時以上,還不包括下班後在家打遊戲的時間,所以,保護眼睛是最重要的了。問題的原由來源於本人週六去眼科對激光手術的複查,大夫一再向我強調眼睛的自我調節能力,就是說只要你能保證你每隔一個小時左右就閉眼休息或向遠處眺望,離開電腦屏幕,那麼已經治好的近視就不會反彈。本人雖是自律性比較強的人,但在計算機屏幕面前就再也不如此了,每每幾個小時也不擡頭一次,爲了眼睛的健康,我決定把這個艱鉅的任務交由計算機來完成,讓它在一小時左右自動提醒我休息五分鐘。如此一來,豈不是不再用顧慮這件事了。 

  功能雖然簡單,但要寫個程序放在啓動組裏天天自動運行也不是一個好的辦法,正巧之前也沒作過Windows服務,不如索性來試一試,同進也看看.NET爲咱們提供了多麼先進的功能吧,因而決定,就用C#來作一個提醒我保護眼睛的Windows服務,取名就叫CareEye吧。 

  運行Visual Studio.NET 2003,創建一個C#的Windows服務項目,在CareEye.cs的設計視圖提示能夠把須要的控件和組件拖動到這上面,假如想要作系統日誌的話固然就能夠把EventLog組件拖過來了,不過這個程序好像不須要這些東西,仍是算了吧。那麼計時要不要採用計時器控件呢?想了一下,這個控件雖然好用,但太經常使用了,本着學習新知識的原則,最恰當的恐怕就是線程了,並且在之後作其餘Windows服務的時候線程確定是必需的,因此仍是用線程好,這樣我只要在線程中完成對時間的監測,把線程的啓動和中止交給服務的啓動和中止,呵,很方便啊。 

  再來看CareEye.cs的源程序,一大堆沒見過的東西,不過仔細分析一下也就沒什麼了。CareEye類派生於ServiceBase類,所以繼承了基本服務類的特性,顯然Main()方法會由SCM(服務控制管理程序)調用,在這個方法中Run一個新的CareEye實例,這樣就運行了一個Windows服務,OnStart()和OnStop()明顯是用於啓動和中止服務的響應函數了。 

  注意在Main()方法中有一個ServiceBase[]的數組,它是爲那些一個服務進程包含多個服務準備的,對於這個程序來講,它只有一個CareEye服務,所以徹底能夠把這個數組刪除,而只是使用System.ServiceProcess.ServiceBase.Run(new CareEye());一句就夠了。 

  接下來爲了使用線程,須要引入System.Threading命名空間,爲了使用對話框,還須要引入System.Windows.Forms命名空間,這是爲了未來提示用戶時顯示對話框而準備的。 

  下面爲類CareEye添加一個成員字段private Thread MainThread;同時在構造函數中對其進行初始化: 

MainThread=new Thread(new ThreadStart(ThreadFunc)); 
MainThread.Priority=ThreadPriority.Lowest; 

  這裏把線程的優先級設到最低,這樣不會耗用過多的系統性能。這個線程對象使用ThreadFunc做爲線程函數,所以將這個線程函數補充完整: 

public static void ThreadFunc() 

 int LastHour=DateTime.Now.Hour; 
 while (true) 
 { 
  System.Threading.Thread.Sleep(60000); 
  if (DateTime.Now.Hour-1==LastHour) 
  { 
   MessageBox.Show("爲了愛護您的眼睛,請您暫時休息5分鐘並向遠處眺望!","警告",MessageBoxButtons.OK,MessageBoxIcon.Warning,MessageBoxDefaultButton.Button1, 
MessageBoxOptions.DefaultDesktopOnly); 
   LastHour=DateTime.Now.Hour; 
  } 
 } 


  餘下的代碼就簡單了,只要在OnStart中啓動線程,在OnStop中中止線程就好了。 

  以上的服務程序雖然很簡單,線程的處理上也不很恰當,也違背了不少服務程序的原則好比界面顯示等,但對於本人的需求而言是足夠了,所以就如此製做了。若是你有須要,固然能夠把對話框改成其餘的提醒方式如響鈴等,線程也可使用內核對象同時使用更好的處理方法。 

  Windows服務就作完了,餘下的就是要測試了,但發現這個EXE沒法運行,它會提示你該EXE須要使用安裝程序來安裝服務,看來不可能寫一個程序就算是Windows服務了,還要把它註冊到Windows才行。 

  接下來,右擊CareEye.cs的設計視圖,添加安裝程序,(VS.NET想得就是挺周到的),這下又出來一批代碼,不過好在不用改代碼了,只要把Account的帳戶類型設成LocalSystem,把StartType設成手動啓動就好了,這裏用手動是爲了方便調試,之後能夠改爲自動類型。 

  編譯完後,仍是沒法運行,此處還須要一步,就是運行installutil來安裝這個服務,其安裝和卸載的用法爲: 

installutil CareEye.exe 
installutil /u CareEye.exe 

  安裝完後能過系統的服務管理器你就能夠看到你的服務了,只要點擊啓動就能夠把它啓動,把時間向前改一個小時它就會提醒你須要休息了,呵呵,夠簡單了吧。 

  若是你想製做成安裝包分發給本身的朋友,只須要再添加個部署項目就好了,不過爲了完成自注冊,要在自定義操做編輯器中的安裝階段添加一個自定義的安裝操做,把InstallerClass屬性設成TRUE便可。 

  餘下的事情,就是本身動手試試吧,這回不用擔憂用眼超時了!
回覆
  • 5樓
  • 2006-02-08 18:50
 
 
 
如下是careeye.cs的源程序:  using System;  using System.Collections;  using System.ComponentModel;  using System.Data;  using System.Diagnostics;  using System.ServiceProcess;  using System.Threading;  using System.Windows.Forms;  namespace CareEye  {   public class CareEye : System.ServiceProcess.ServiceBase   {    private Thread MainThread;    /// <summary>    /// 必需的設計器變量。    /// </summary>    private System.ComponentModel.Container components = null;    public CareEye()    {     // 該調用是 Windows.Forms 組件設計器所必需的。     InitializeComponent();     // TODO: 在 InitComponent 調用後添加任何初始化     MainThread=new Thread(new ThreadStart(ThreadFunc));     MainThread.Priority=ThreadPriority.Lowest;    }    // 進程的主入口點    static void Main()    {     //System.ServiceProcess.ServiceBase[] ServicesToRun;     // 同一進程中能夠運行多個用戶服務。若要將     //另外一個服務添加到此進程,請更改下行     // 以建立另外一個服務對象。例如,     //     // ServicesToRun = New System.ServiceProcess.ServiceBase[] {new CareEye(), new MySecondUserService()};     //     //ServicesToRun = new System.ServiceProcess.ServiceBase[] { new CareEye() };     System.ServiceProcess.ServiceBase.Run(new CareEye());    }    /// <summary>    /// 設計器支持所需的方法 - 不要使用代碼編輯器    /// 修改此方法的內容。    /// </summary>    private void InitializeComponent()    {     //     // CareEye     //     this.ServiceName = "CareEye";    }    /// <summary>    /// 清理全部正在使用的資源。    /// </summary>    protected override void Dispose( bool disposing )    {     if( disposing )     {      if (components != null)      {       components.Dispose();      }     }     base.Dispose( disposing );    }    /// <summary>    /// 設置具體的操做,以便服務能夠執行它的工做。    /// </summary>    protected override void OnStart(string[] args)    {     // TODO: 在此處添加代碼以啓動服務。     MainThread.Start();    }    /// <summary>    /// 中止此服務。    /// </summary>    protected override void OnStop()    {     // TODO: 在此處添加代碼以執行中止服務所需的關閉操做。     MainThread.Abort();    }    public static void ThreadFunc()    {     int LastHour=DateTime.Now.Hour;     while (true)     {      System.Threading.Thread.Sleep(60000);      if (DateTime.Now.Hour-1==LastHour)      {       MessageBox.Show("爲了愛護您的眼睛,請您暫時休息5分鐘並向遠處眺望!","警告",MessageBoxButtons.OK,MessageBoxIcon.Warning,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);  LastHour=DateTime.Now.Hour;      }     }    }   }  }
相關文章
相關標籤/搜索