寫一個Windows上的守護進程(6)Windows服務


寫一個Windows上的守護進程(6)Windows服務

守護進程因為要開機啟動,還要高權限,所以我就把它做成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函數的流程是這樣的:

clip_image002

3. 作為一個正常的服務,要響應用戶的請求,比如停止,這要調用RegisterServiceCtrlHandler注冊自己的處理函數,也可以處理自定義的control code;要響應系統請求,比如關機,這要調用SetConsoleCtrlHandler注冊自己的處理函數

4. 要想自己的程序成為服務,還得向Windows注冊,這要調用CreateService,還有停止服務,啟動服務之類的,詳情參看MSDN,上面都有例子

 

我把這些步驟封裝了下,做成了一個單例類——很顯然,單例很適用,一個進程對應一個服務——CWin32Service:

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日星期六


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM