(三)對服務的深入討論之下
現在我們還剩下一個函數可以在細節上討論,那就是服務的 CtrlHandler 函數。
當調用 RegisterServiceCtrlHandler 函數時, SCM 得到並儲存這個回呼函數的地址。一個 SCP 調一個告訴 SCM 如何去控制服務的 Win32 函數,現在已經有10個預定義的控制請求:
Control code |
Meaning |
SERVICE_CONTROL_STOP |
Requests the service to stop. ThehService handle must have SERVICE_STOP access. |
SERVICE_CONTROL_PAUSE |
Requests the service to pause. The hService handle must have SERVICE_PAUSE_CONTINUE access. |
SERVICE_CONTROL_CONTINUE |
Requests the paused service to resume. The hService handle must have SERVICE_PAUSE_CONTINUE access. |
SERVICE_CONTROL_INTERROGATE |
Requests the service to update immediately its current status information to the service control manager. The hService handle must have SERVICE_INTERROGATE access. |
SERVICE_CONTROL_SHUTDOWN |
Requests the service to perform cleanup tasks, because the system is shutting down. For more information, see Remarks. |
SERVICE_CONTROL_PARAMCHANGE |
Windows 2000: Requests the service to reread its startup parameters. The hService handle must have SERVICE_PAUSE_CONTINUE access. |
SERVICE_CONTROL_NETBINDCHANGE |
Windows 2000: Requests the service to update its network binding. The hService handle must have SERVICE_PAUSE_CONTINUE access. |
SERVICE_CONTROL_NETBINDREMOVE |
Windows 2000: Notifies a network service that a component for binding has been removed. The service should reread its binding information and unbind from the removed component. |
SERVICE_CONTROL_NETBINDENABLE |
Windows 2000: Notifies a network service that a disabled binding has been enabled. The service should reread its binding information and add the new binding. |
SERVICE_CONTROL_NETBINDDISABLE |
Windows 2000: Notifies a network service that one of its bindings has been disabled. The service should reread its binding information and remove the binding. |
上表中標有 Windows 2000 字樣的就是 2000 中新添加的控制碼。除了這些代碼之外,服務也可以接受使用者定義的,範圍在 128-255 之間的代碼。
當 CtrlHandler 函數收到一個 SERVICE_CONTROL_STOP 、 SERVICE_CONTROL_PAUSE 、 SERVICE_CONTROL_CONTINUE 控制碼的時候, SetServiceStatus 必須被調用去確認這個代碼,並指定你認為服務處理這個狀態變化所需要的時間。
例如:你的服務收到了停止請求,首先要把 SERVICE_STATUS 結構的 dwCurrentState 成員設定成 SERVICE_STOP_PENDING ,這樣可以使 SCM 確定你已經收到了控制碼。當一個服務的暫停或停止操作正在執行的時候,必須指定你認為這種操作所需要的時間:這是因為一個服務也許不能立即改變它的狀態,它可能必須等待一個網路請求被完成或者資料被重新整理到一個磁碟機上。指定時間的方法就像我上一章說的那樣,用成員 dwCheckPoint 和 dwWaitHint 來指明它完成狀態改變所需要的時間。如果需要,可以用增加 dwCheckPoint 成員的值和設定 dwWaitHint 成員的值去指明你期待的服務到達下一步的時間的方式周期性的報告進展情況。
當整個啟動的過程完成之後,要再一次調用 SetServiceStatus 。這時就要把 SERVICE_STATUS 結構的 dwCurrentState 成員設定成 SERVICE_STOPPED ,當報告狀態碼的同時,一定要把成員 dwCheckPoint 和 dwWaitHint 設定為0,因為服務已經完成了它的狀態變化。暫停或繼續服務的時候方法也一樣。
當 CtrlHandler 函數收到一個 SERVICE_CONTROL_INTERROGATE 控制碼的時候,服務將簡單的將 dwCurrentState 成員設定成服務當前的狀態,同時,把成員 dwCheckPoint 和 dwWaitHint 設定為0,然後再調用 SetServiceStatus 就可以了。
在作業系統關閉的時候, CtrlHandler 函數收到一個 SERVICE_CONTROL_SHUTDOWN 控制碼。服務根本無須回應這個代碼,因為系統即將關閉。它將執行儲存資料所需要的最小行動集,這是為了確定機器能及時關閉。預設時系統只給很少的時間去關閉所有的服務, MSDN 裡面說大概是20秒的時間,不過那可能是 Windows NT 4 的設定,在我的 Windows2000 Server 裡這個時間是10秒,你可以手動的修改這個數值,它被記錄在 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control 子鍵裡面的 WaitToKillServiceTimeout ,單位是毫秒。
當 CtrlHandler 函數收到任何使用者定義的代碼時,它應該執行期望的使用者自訂行動。除非使用者自訂的行動要強制服務去暫停、繼續或停止,否則不調 SetServiceStatus 函數。如果使用者定義的行動強迫服務的狀態發生變化, SetServiceStatus 將被調用去設定 dwCurrentState 、 dwCheckPoint 和 dwWaitHint ,具體控制碼和前面說的一樣。
如果你的 CtrlHandler 函數需要很長的時間執行操作的話,千萬要注意:假如 CtrlHandler 函數在30秒內沒有返回的話, SCM 將返回一個錯誤,這不是我們所期望的。所以如果出現上述情況,最好的辦法是再建立一個線程,讓它去繼續執行操作,以便使得 CtrlHandler 函數能夠迅速的返回。例如,當收到一個 SERVICE_CONTROL_STOP 請求的時候,就像上面說的一樣,服務可能正在等待一個網路請求被完成或者資料被重新整理到一個磁碟機上,而這些操作所需要的時間是你不能估計的,那麼就要建立一個新的線程等待操作完成後執行停止命令, CtrlHandler 函數在返回之前仍然要報告 SERVICE_STOP_PENDING 狀態,當新的線程執行完操作之後,再由它將服務的狀態設定成 SERVICE_STOPPED 。如果當前操作的時間可以估計的到就不要這樣做,仍然使用前面交待的方法處理。
CtrlHandler 函數我就先講這些,下面說說服務怎麼安裝。一個服務程式可以使用 CreateService 函數將服務的資訊添加到