Windows98/NT服務

  每個操作系統都需要有在後臺執行任務的方法,無論是誰正在使用這部機器,這些任務都可以繼續運行,後臺任務可以處理各種重要的服務,包括系統的或者用戶的。例如,一個信使服務可以監控網絡,並且在接收到另一臺機子的信息時,可以顯示一個對話框。一個發送和接收傳真的應用需要在啓動的時候運行,並且不斷地監控負責傳真的modem,看有沒有傳真進來。一個家庭的或者辦公室的安全程序,用來控制一件檢測設備時,它需要不時地查詢傳感器,並且在適當的時候響應它。所有這些任務都需要CPU時間來執行它們,不過由於它們需要的CPU時間很少,因此可以放在後臺而不影響用戶使用系統。

  在MS-DOS中,後臺的任務是通過TSR(Terminate and Stay Resident)程序來處理的。這些程序經由autoexec.bat文件開始。在UNIX中,後臺任務是通過Daemons來處理的。在每次啓動UNIX的過程中,你都可以看到操作系統啓動一些任務,例如定時的程序(Cron)和Finger的daemons,然後纔可以讓首個用戶登錄。在Windows NT中,後臺的任務被稱爲服務。服務可在每次NT啓動的時候運行,並且不管是誰登陸,都會一直運行下去。

  Windows NT的服務都是通過一般的可執行程序實現的,不同的是,它遵循內部的一個特定協議來設計,以便它們能夠與服務控制管理器(SCM,Service Control Manager)進行正確的交互。在這篇文章中,你將學習到如何在Windows NT中創建和安裝簡單的Win32服務。一旦你懂得了這個簡單的服務,你要建立自己的服務也不難了,因爲所有的服務,不論是如何地複雜,都必須包含有同樣基本的SCM接口代碼。只要符合SCM的要求,其實爲服務設計的可執行文件和一般的程序並沒有多少的區別。

  無論是對於編程者或者系統管理員,瞭解NT的服務如何工作都是很重要的。編程者就不必說了,因爲他們要創建自己的服務,而對於系統管理員,也是同樣重要的。因爲後臺的任務可以是很危險的。MS-DOS和Macintosh系統都是一個病毒的溫牀,因爲它們在安全性方面先天不足,它們都可以允許任何人或者程序在任何時間創建後臺的任務。Windows NT和UNIX系統是較安全的,因爲只有系統管理員纔可以爲系統增加後臺的任務,不過,如果系統管理員加入了一個破壞性的後臺程序,就它就可以爲所欲爲了。因此係統管理員要了解Windows NT服務的技巧和權限設置,就可以避免加入有潛在危險的後臺任務。

  基本的概念

  服務有兩種不同的形式。驅動器服務使用驅動器協議,讓NT可以與特定的硬件進行通信。另一個是Win32服務,通過一般的Win32 API來實現後臺任務。這篇文章的重點是談Win32服務,因爲它們更爲常見,而且創建起來也很容易。任何的NT編程者通過使用一般的NT SDK(或者Visual C++),並且可以用管理員的身份訪問一臺NT機器,都可以實現和安裝自己的Win32服務。如果你想創建一些在Windows NT啓動時就運行的程序,並且要求它會在系統中一直運行,你就要使用Win32的服務。

  在NT中,服務通過控制面板進行管理。在控制面板中,你會發現有一個服務的圖標,打開它你會看到所有Win32服務的清單。在那裏你可以開始、停止、暫停和繼續某個服務。你按下其中的啓動按鈕後,就會出現一個對話框,你可以修改啓動操作以及服務使用的默認帳號。一個服務可以在系統啓動的時候自動運行,也可以被完全禁止。或者設置爲手動執行。在手動的時候,用戶還可以設置啓動的參數。要對服務中的項目作修改的話,你需要以一個管理員或者超級用戶的身份登錄。

  Windows NT自帶有一些預裝的任務,用來處理諸如網絡信使服務的操作或者使用“at”命令定時執行的操作,以及分佈的RPC命名。在你創建自己的服務時,你必須執行一個獨立的安裝步驟,以將服務的信息插入到服務管理工具的列表中,這些信息包括有新服務的名字、執行文件的名字和啓動的類型等,都會寫入到註冊表中,這樣在機器下次啓動的時候,SCM就會得到新服務的相關信息。


  創建一個新的服務

  執行服務的程序也是一個EXE文件,不過它必須符合某些特定的規範,以便可以與SCM進行正確的交互。微軟很細緻地設計了函數調用的流程,你必須遵循這些流程,否則你的服務就不能工作。具體的規定如下所列。你可以在Win32編程者的參考指南中找到以下涉及的函數,這些資料在SDK的Win32在線幫助或者Visual C++都有:

   .服務的代碼必須要有一個一般的main或者WinMain函數。這個函數應該會馬上調用StartServiceCrtlDispatcher函數。通過調用這個函數,你可以讓SCM得到ServiceMain函數的指針,這樣在SCM要啓動該服務時,就可以調用它

   .在SCM要啓動服務的時候,就會調用ServiceMain函數。例如,如果管理員在服務管理器中按下啓動的按鈕,SCM就會在一個獨立的線程中執行ServiceMain函數。ServiceMain應該調用RegisterServiceCtrlHandler函數,這樣可以註冊一個Handler函數,以便SCM對服務進行控制。Handler函數的名字可以是任意的,不過它會在Handler下的文檔中列出來。RegisterServiceCtrlHandler函數會返回一個句柄,在服務需要發送狀態信息給SCM時,可以通過該句柄進行。

   .ServiceMain函數也必須啓動做該服務實際工作的線程。在服務停止前,ServiceMain函數是不應該有返回的。當它返回的時候,服務已經停止了。

   .Handler函數包含了一個switch語句,用來分析由SCM傳送過來的請求。默認的情況,SCM可以發送以下任何的的控制常數:

     SERVICE_CONTROL_STOP - 要服務停止


     SERVICE_CONTROL_PAUSE - 要服務暫停

     SERVICE_CONTROL_CONTINUE - 要服務繼續

     SERVICE_CONTROL_INTERROGATE - 要服務馬上報告它的狀態

     SERVICE_CONTROL_SHUTDOWN - 告訴服務即將關機

  也可以創建自定義的常數(值在128到255之間),並且通過SCM發送給服務。

  如果你創建的EXE包括有以上提到的main、ServiceMain和Handler函數,以及執行服務自身任務的線程函數,那麼你的服務程序設計就完成了。以下的圖總結了這些不同的函數和SCM之間的交互:



****************圖一***********************

  在本文的最後還有幾段程序的列表,其中列表一爲我們展示了一個可能是最簡單的服務。該服務只發出"嘟"的響聲。默認的狀態下,它每兩秒響一次。你可以通過啓動的參數來修改發聲的間隔。這個服務挺完整,它可以正確響應SCM傳來的每個控制信號。因此,這個程序可作爲你創建自己服務的一個很好的模板。

  main函數通過調用StartServiceCtrlDispatcher來註冊ServiceMain函數。註冊的操作使用了一個SERVICE_TABLE_ENTRY結構的數組。在這個例子中,該程序只包含了一個服務,因此在表中只會有一個項目。不過,對於一個EXE文件,可以創建幾個任務,這樣在表中就會有幾項,以識別不同的ServiceMain函數。在調用StartServiceCtrlDispatcher之前,可在main函數中放入初始化的代碼,不過這些代碼必須在少於30秒內完成,否則,SCM會認爲某些地方出錯而終止服務。

  在自動或者手動啓動服務時,將會調用ServiceMain函數。ServiceMain函數將包含有以下的步驟:

  1.它馬上調用RegisterServiceCtrlHandler來註冊Handler函數,作爲SCM控制該服務的Handler函數

  2.然後它將調用SendStatusToSCM函數,將當前的進程通報給SCM。第四個參數是一個“click count”值,在程序每次更新狀態時,它的值就會增加。SCM和其它的程序可以根據click count的值來知道初始化期間進行的處理。最後的參數是"wait hint",是用來告訴SCM在click count下次更新前,它需要等待的時間(以毫秒計算)。

  3.ServiceMain接着會創建一個事情,該事件在函數的底部使用,可讓它一直運行,直到SCM發出一個STOP的請求。

  4.接着,ServiceMain檢查啓動的參數。參數可在用戶手動啓動服務時,通過服務管理工具中的啓動參數傳送過來。這些參數以一個argv形式的數組進入ServiceMain函數。

  5.如果你的服務需要處理其它初始化的任務,它們應該放在這一步,在調用InitService之前。

  6.ServiceMain函數接着調用InitService函數,這將啓動線程並做服務的真正工作。如果該調用成功,SverviceMain將會通知SCM服務已經成功啓動。

  7.ServiceMain將調用WaitForSingleObject,用來等待terminateEvent事件對象被設置。這個對象通過Handler函數設置,一旦它被設置,ServiceMain將調用終止的函數來做清除的工作,然後就返回並停止服務。

  你從上面可以看到,在這個函數中並沒有多少靈活的地方。除了第5步外,你必須按步執行以上提到的任務,否則服務將不可以正確啓動。

  終止函數清除所有打開的句柄,並且發送一個狀態的信息給SCM,告訴它服務現已停止。

  在SCM要暫停、繼續、詢問或者停止服務時,它就會調用Handler函數。要停止服務,Handler設置terminateEvent,這樣做會導致ServiceMain(它會以一個獨立線程的形式執行)終止並且返回。一旦ServiceMain返回,服務就停止了。

  SendStatusToSCM 函數負責發送服務的當前狀態給SCM。

  在需要啓動服務線程時,ServiceMain就會調用InitService函數。該函數調用CreateThread來爲服務創建一個新的線程。

  ServiceThread函數包含有該服務真正要做的工作。在這個例子中,該線程包含有一個無限的循環,以一個預定義的時間間隔發出響聲。在你創建自己的服務時,你可以在該線程中放入任何的代碼,調用Win32函數或者你自己的函數。

  安裝和移除服務

  爲了使用上面提到的發響聲服務,你必須安裝它。安裝可讓SCM知道這個服務,並且讓SCM將它加入到控制面板中的服務列表中。表單二的代碼展示瞭如何安裝一個服務。

  表單2先通過OpenSCManager函數打開一個到SCM的連接。在OpenSCManager的調用中,你必須指定你要做的東西,以便SCM可以驗證這個行爲。如果你登錄的帳號沒有足夠的權限,該調用將返回NULL。

  CreateService的調用是真正用來裝入新服務的。它使用OpenSCManager返回至SCM的指針、名字、標籤和命令行中的EXE文件,還有一些標準的參數值。使用SERVICE_WIN32_OWN_PROCESS表明該服務的EXE文件只包含有一個服務,而SERVICE_DEMAND_START則表明該服務是手動而不是自動啓動的。使用命令行安裝程序的一個典型格式如下:

   install BeepService "Beeper" c:/winnt/beep.exe

  第一個參數是SCM內部使用的服務名字。這個名字也用在以後移除服務。第二個參數是一個標識符,即服務管理中顯示的名字。第三個參數指出該服務的執行文件的路徑。在你安裝服務後,可在控制面板的服務管理中啓動它。如果有錯,可在Win32 API的在線文檔中查出錯誤代碼的含義。

  要移除服務,你要按列表3的步驟進行。它首先打開一個到SCM的連接,然後使用OpenService函數打開一個與服務的連接。列表3中接着查詢服務來看它是否現已停止了。如果不是的話,就會停止它。DeleteService 用來從控制面板中移除服務,移除操作的典型格式如下:

  remove BeepService

  需要的話,你也可以在馬上重新安裝服務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章