1. 程式人生 > >除錯windows service的OnStart事件及除錯service的一些方法彙總

除錯windows service的OnStart事件及除錯service的一些方法彙總

雖然 MSDN 的 HOW TO:偵錯 Windows 服務應用程式 檔案有教如何除錯,但是這樣的除錯有個大缺點,就是有時後程式碼是在 OnStart 事件一開始的時候就發生問題的,有時後就會因為來不及 附加 (Attach) 程式導致無法除錯。另外,對於「安裝專案」內的「自訂動作」由於是在專案的 Installer 類別中執行的,所以要對「安裝專案」進行除錯也不太容易。我今天就來分享幾個很簡單又有效的除錯密技。

這個方式我個人是比較喜歡,因為在開發時期要加 Code 上去很容易,你只要在想要除錯的程式碼之前加上以下程式片段,就可以要求程式暫停,並讓你選用 Visual Studio 進行除錯!

System.Diagnostics.Debugger.Launch();

當執行程式到這行時,就會出現以下視窗:

Visual Studio Just-In-Time Debugger

相對的要對「安裝專案」中的自訂動作除錯,也可以使用這個技巧,方便又有效!

在啟動 Visual Studio 的 Debugger 之後,你也可以利用 Debugger.Break() 方法自訂中斷點,你可以在希望監看的程式碼片段之前加上這個方法,或是透過一些條件判斷讓 Visual Studio Debugger 執行到這行時自動暫停讓你可以看看當下的物件狀況。

方法二:在 OnStart 事件開始前先睡個 20 秒

來不及附加程式的問題要解決也很簡單,「睡」一下就好了,讓程式在睡覺的時候趕緊利用 Visual Studio 附加該服務的程式。

System.Threading.Thread.Sleep(1000 * 20);

方法三:把 Windows Service 當成 Console Application 來執行

大家應該知道 Windows Service 是沒辦法直接按下 F5 啟動程式的,所以一般的處理方式都是 Windows Service 專案搭配一個 Windows Setup 專案進行安裝,安裝後再啟動,然後再想辦法進行除錯。

但事實上,透過一點點小修改也可以讓 Windows Service 變成 Console Application 來執行,這樣一來你就可以用 F5 啟動程式並直接進入偵錯模式了!

要達成這個目標,有 2 件事必須瞭解,瞭解後就知道我在寫什麼了。

1. 由於 Windows Service 與 Console Appliation 的程式進入點都一樣是 static void Main(),預設的 Main() 內容如下:

[c#] view plain copy print?
  1. staticvoid Main()  
  2. {  
  3.     ServiceBase[] ServicesToRun;  
  4.     ServicesToRun = new ServiceBase[]   
  5.     {   
  6.         new Service1()   
  7.     };  
  8.     ServiceBase.Run(ServicesToRun);  
  9. }  
static void Main(){ ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[]  {   new Service1()  }; ServiceBase.Run(ServicesToRun);}

2. 在 Windows Service 專案中的啟動點是在 OnStart 方法,但這個方法是屬於 protected 存取層級,無法對外公開使用。

---

接下來,就要正式修改程式了,共有 3 個步驟:

1. 先修改 Service1.cs 程式 ( Windows Service 專案中的主要服務類別 )

    由於 OnStart 與 OnStop 方法被 protected 宣告保護著,無法讓使用此類別的程式直接呼叫,此時必須多寫兩個方法(method)讓使用此類別的程式可以呼叫 OnStart() 方法。

[c#] view plain copy print?
  1. publicvoid Start(string[] args)  
  2. {  
  3.     this.OnStart(args);  
  4. }  
  5. publicvoid Stop()  
  6. {  
  7.     this.OnStop();  
  8. }  
public void Start(string[] args){    this.OnStart(args);}public void Stop(){    this.OnStop();}

2. 接著修改 Program.cs 中的 Main() 方法 ( 也就是整個 Windows Service 專案的程式啟動點 )

    透過 Environment.UserInteractive 屬性即可得知該元件是執行於使用者互動模式中,若該元件是以 Windows Service 執行時,則此屬性將會回傳 false,我將原本的 Main() 程式改成以下程式碼:

[c#] view plain copy print?
  1. staticvoid Main()  
  2. {  
  3.     if (Environment.UserInteractive)  
  4.     {  
  5.         Service1 s = new Service1();  
  6.         s.Start(null);  
  7.         Console.WriteLine("服務已啟動,請按下 Enter 鍵關閉服務...");  
  8.         // 必須要透過 Console.ReadLine(); 先停止程式執行
  9.         // 因為 Windows Service 大多是利用多 Thread 或 Timer 執行長時間的工作
  10.         // 所以雖然主執行緒停止執行了,但服務中的執行緒已經在運行了!
  11.         Console.ReadLine();  
  12.         s.Stop();  
  13.         Console.WriteLine("服務已關閉");  
  14.     }  
  15.     else
  16.     {  
  17.         ServiceBase[] ServicesToRun;  
  18.         ServicesToRun = new ServiceBase[]   
  19.         {   
  20.             new Service1()   
  21.         };  
  22.         ServiceBase.Run(ServicesToRun);  
  23.     }  
  24. }  
static void Main(){ if (Environment.UserInteractive) {  Service1 s = new Service1();  s.Start(null);  Console.WriteLine("服務已啟動,請按下 Enter 鍵關閉服務...");  // 必須要透過 Console.ReadLine(); 先停止程式執行  // 因為 Windows Service 大多是利用多 Thread 或 Timer 執行長時間的工作  // 所以雖然主執行緒停止執行了,但服務中的執行緒已經在運行了!  Console.ReadLine();  s.Stop();  Console.WriteLine("服務已關閉"); } else {  ServiceBase[] ServicesToRun;  ServicesToRun = new ServiceBase[]   {    new Service1()   };  ServiceBase.Run(ServicesToRun); }}

3. 變更 Windows Service 專案的 輸出型別 ( Output Type )

    開啟 Windows Service 專案屬性(Properties)視窗,將原本的 Windows Application 切換到 Console Application即可。

Project Properties - Output Type: Console Application

---

如此一來,你就可以讓同一個元件同時可以用來當 Windows Service 的主程式,以及可以直接當成 Console 程式來執行,非常的方便。

Service啟動失敗,後提示以下錯誤資訊:

查了一下資料,應該是服務的邏輯程式碼出了問題,開啟控制面板/管理工具/事件檢視器 ->應用程式 裡發現瞭如下資訊:

雙擊錯誤資訊,即可找到服務的錯誤提示!~!~

根據錯誤的提示資訊,可檢查程式碼。