CNTService 類
當我創建 C++ 對象封裝 Windows 函數時,我嘗試為我封裝的每個 Windows API 除了創建成員函數外,還做一些別的工作,我嘗試讓對象更容易使用,降低實現特定項目所需的代碼行數。因此我的對象是基於“我想讓這個對象做什麼?”而不是“Windows 用這些 APIs 做什麼?”
CNTService 類包含一些用來解析命令行的成員函數,為了處理服務的安裝和拆卸以及事件日志的記錄,你得在派生類中重寫一些虛擬函數來處理服務控制管理器的請求。下面我們將通過本文的例子服務實現來研究這些函數的使用。
如果你想創建盡可能簡單的服務,只需要重寫 CNTService::Run 即可,它是你編寫代碼實現具體服務任務的地方。你還需要實現 main 函數。如果服務需要實現一些初始化。如從注冊表讀取數據,還需重寫 CNTService::OnInit。如果你要向服務發送命令消息 ,那麼可以在服務中使用系統函數 ControlService,重寫 CNTService::OnUserControl 來處理請求。
在例子應用程序中使用 CNTService
NTService 在 CMyService 類中實現了它的大多數功能,CMyService 由 CNTService 派生。 MyService.h 頭文件如下:// myservice.h #include "ntservice.h" class CMyService : public CNTService { public: CMyService(); virtual BOOL OnInit(); virtual void Run(); virtual BOOL OnUserControl(DWORD dwOpcode); void SaveStatus(); // Control parameters int m_iStartParam; int m_iIncParam; // Current state int m_iState; }; 正像你所看到的,CMyService 改寫了 CNTService 的 OnInit、Run 和 OnUserControl。它還有一個函數叫 SaveStatus,這個函數被用於將數據寫入注冊表,那些成員變量用來保存當前狀態。例子服務每隔一定的時間對一個整型變量進行增量處理。開始值和增量值都存在注冊表的參數中。這樣做並沒有別的意圖。只是為了簡單示范。下面我們看看這個服務是如何實現的。
實現 main 函數
有了從 CNTService 派生的 CMyService,實現 main 函數很簡單,請看 NTServApp.cpp 文件:
int main(int argc, char* argv[]) { // 創建服務對象 CMyService MyService; // 解析標准參數 (安裝, 卸載, 版本等.) if (!MyService.ParseStandardArgs(argc, argv)) { // 未發現任何標准參數,所以啟動服務, // 取消下面 DebugBreak 代碼行的注釋, // 當服務啟動後進入調試器, //DebugBreak(); MyService.StartService(); } // 到這裡,服務已經停止 return MyService.m_Status.dwWin32ExitCode; } 這裡代碼不多,但執行後卻發生了很多事情,讓我們一步一步來看。首先,我們創建一個 MyService 類的實例。構造函數設置初始化狀態和服務名字(MyService.cpp):CMyService::CMyService():CNTService("NT Service Demonstration") { m_iStartParam = 0; m_iIncParam = 1; m_iState = m_iStartParam; } 接著調用 ParseStandardArgs 檢查命令行是否包含服務安裝(-i)、卸載(-u)以及報告其版本號(-v)的請求。CNTService::ParseStandardArgs 分別調用 CNTService::IsInstalled,CNTService::Install 和 CNTService::Uninstall 來處理這些請求。如果沒有可識別的命令行參數,則假設該服務控制管理器試圖啟動該服務並調用 StartService。該函數直到服務停止運行才返回。當你調試完代碼,即可把用於調試的代碼行注釋掉或刪除。
安裝和卸載服務
服務的安裝由 CNTService::Install 處理,它用 Win32 服務管理器注冊服務並在注冊表中建立一個條目以支持服務運行時日志消息。
服務的卸載由 CNTService::Uninstall 處理,它僅僅通知服務管理器該服務已經不再需要。CNTService::Uninstall 不會刪除服務實際的可執行文件。
編寫服務代碼
現在我們來編寫實現服務的具體代碼。對於 NTService 例子,有三個函數要寫。他們涉及初始化,運行服務的細節和響應控制請求。
初始化
注冊表有一個給服務用來存儲參數的地方:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
我就是選擇這裡來存儲我的服務配置信息。我創建了一個 Parameters 鍵,並在此存儲我要保存的值。所以當服務啟動時,OnInit 函數被調用;這個函數從注冊表中讀取初始設置。