守護進程因為要開機啟動,還要高權限,所以我就把它做成Windows服務了。
關於Windows服務的官方文檔,大家可以看https://msdn.microsoft.com/en-us/library/windows/desktop/ms686953(v=vs.85).aspx。
總的來說,服務的行為區別於普通應用程序的地方有以下幾點:
1. 一般來說,服務是運行於System用戶下的,當然也可以自己指定。也就是說服務可以在無用戶登錄的情況下運行
2. 一般來說,服務是沒有用戶交互的
3. 服務可以通過服務管理器管理(啟動、停止等等)
服務程序的編寫區別於普通應用程序的地方有以下幾點:
1. 服務程序執行的主體是ServiceMain函數,你需要在main函數中調用StartServiceCtrlDispatcher注冊你的ServiceMain函數。也就是說你的功能代碼應寫在你的ServiceMain函數中。StartServiceCtrlDispatcher直到服務停止才會返回
2. 在ServiceMain函數中,你要及時向服務管理器報告自己的狀態,好讓其向用戶展示自己。一般來說,ServiceMain函數的流程是這樣的:
class CWin32Service : public Singleton<CWin32Service> { friend class Singleton<CWin32Service>; private: CWin32Service(void); public: ~CWin32Service(void); public: bool init(const ServiceInfo& info); typedef std::vector<tstring> ArgList; typedef boost::function<bool(const ArgList&)> StartingFunction;//ArgList是應用程序的命令行參數 typedef boost::function<void(const ArgList&)> ServiceFunction; void register_starting_function(const StartingFunction& f); void register_running_function(const ServiceFunction& f); void register_control_code_function(const DWORD c, const ServiceFunction& f); bool go(); };
上面是其主要對外接口,接口基本上對應於上面ServiceMain的流程:
l Init:告知服務名稱等信息
l StartingFunction:通常都是做好初始化工作,加載配置什麼的,然後啟動工作線程
l running_function:通常都是等待退出信號,等待工作線程結束什麼的。此函數返回意味著服務結束了
l control_code_function:這是要處理的控制代碼。一般我們都會要處理stop control code的,不過你不設置也沒關系
l go:這個就是調用StartServiceCtrlDispatcher注冊ServiceMain函數。直到服務結束才會返回。ServiceMain中就是按流程調用上面注冊的函數和報告狀態
當然我們不能只完成服務程序的編寫,還要讓用戶能安裝服務、啟動服務、通知服務、卸載服務的方法,我把這些提供在了命令行裡:
l DaemonSvc.exe –intsall
l DaemonSvc.exe –start
l DaemonSvc.exe –stop
l DaemonSvc.exe –remove
這些動作的實現都被我拎出來放到了ServiceUtil裡面。
服務是沒辦法在VC裡直接按F5調試的,你那樣啟動起來的不是服務,是普通應用程序。為了方便調試,我把服務分了兩種模式:普通模式,服務模式:不帶參數啟動起來的是普通模式,帶-svc參數起來的是服務模式(所以你會看到我實現服務安裝的時候,給的命令行後面是有-svc的)。普通模式下,不走StartServiceCtrlDispatcher,直接ServiceMain,並且不和服務管理器打交道。
普通模式開發調試完畢後,可以安裝啟動服務,看下服務模式下是不是正常,此時也可以用VC附加到服務進程上調試。
服務類的使用示例:
int main(int argc, char * argv[]) { InitLog("", 0, LOG_DEBUG); //try to enable debug privilege for querying other processes' info WindowsUtil::set_privilege(SE_DEBUG_NAME, true); ServiceInfo si; si.name = TSTR("DaemonSvc"); si.display_name = si.name; CWin32Service& svc = CWin32Service::get_instance_ref(); if (!svc.init(si)) { ErrorLog("init service fail"); } else { InfoLog("init service success"); svc.register_starting_function(starting); svc.register_running_function(running); svc.register_control_code_function(SERVICE_CONTROL_STOP, stopping); svc.register_control_code_function(200, restart); if (!svc.go()) { ErrorLog("make service go fail"); } else { InfoLog("everything is OK"); } } return 0; }
源碼:https://git.oschina.net/mkdym/DaemonSvc.git (主)&& https://github.com/mkdym/DaemonSvc.git (提升逼格用的)。
2015年11月7日星期六