首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

使用 C++ 建立 Win32 服務程式完整教學

     編輯:關於VC++

原始碼套件

  • ntservice.rar:NTService 服務範例
  • ntservicecpl.rar:NTServCpl 控制台程式
  • ntservicectrl.rar:NTServCtrl 服務監控程式

Win32 服務概述

服務為 OS 啟動即執行的背景程式,無需使用者登入。適用目錄複寫、行程監控、網路服務 (HTTP 等)。

開發框架設計

以單一 C++ 類別 CNTService 封裝服務核心功能,衍生類別僅需覆寫少數虛擬函數。內建安裝/移除/版本報告於服務 EXE 本體,單一檔案部署。

命令列參數

  • -v:報告服務名稱與版本
  • -i:安裝服務
  • -u:移除服務
  • 無參數:服務控制管理器啟動服務

事件日誌機制

服務無 UI,必須寫入系統/應用程式事件日誌。使用 ReportEvent API,需:

  1. 建立 .mc 訊息定義檔 (含 MessageId、SymbolicName、語言、文字)
  2. 執行 mc.exe 編譯產生 .h、.rc、.bin
  3. 專案 .rc 包含產生的 .rc 與 windows.h
  4. 程式碼包含產生的 .h 使用符號名稱
// 訊息範例
MessageId=100
SymbolicName=EVMSG_INSTALLED
Language=English
The %1 service was installed.
.

寫入日誌:

void CNTService::LogEvent(WORD wType, DWORD dwID, const char* pszS1, ...)
{
    // 組裝參數陣列,呼叫 RegisterEventSource + ReportEvent
}

服務核心架構

三個關鍵函數:

  • main:進入點,解析參數、安裝/移除/啟動
  • ServiceMain:真正服務進入點,向 SCM 註冊
  • Handler:處理 SCM 控制訊息

因 SCM 直接呼叫 ServiceMain/Handler,無法傳遞 this 指標,採用靜態成員變數 m_pThis 儲存單一服務物件指標 (限制每程式單一服務物件)。

CNTService 類別設計

封裝理念:「我要這物件做什麼」而非「Windows API 做什麼」。提供:

  • 命令列解析
  • 安裝/移除
  • 事件日誌
  • 虛擬函數供衍生類別覆寫

需覆寫的虛擬函數

  • Run():必須,服務主體工作迴圈
  • OnInit():初始化 (讀登錄檔等)
  • OnUserControl(DWORD):處理 ControlService 使用者自訂訊息 (128-255)
  • OnStop()/OnShutdown():清理作業

範例服務實作

CMyService 宣告

class CMyService : public CNTService
{
public:
    CMyService();
    virtual BOOL OnInit();
    virtual void Run();
    virtual BOOL OnUserControl(DWORD dwOpcode);
    void SaveStatus();
    int m_iStartParam, m_iIncParam, m_iState;
};

main 函數

int main(int argc, char* argv[])
{
    CMyService MyService;
    if (!MyService.ParseStandardArgs(argc, argv))
    {
        // DebugBreak(); // 除錯用
        MyService.StartService();
    }
    return MyService.m_Status.dwWin32ExitCode;
}

OnInit:讀取登錄檔參數

BOOL CMyService::OnInit()
{
    HKEY hkey;
    char szKey[1024];
    strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\");
    strcat(szKey, m_szServiceName);
    strcat(szKey, "\\Parameters");
    
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
    {
        DWORD dwType, dwSize = sizeof(m_iStartParam);
        RegQueryValueEx(hkey, "Start", NULL, &dwType, (BYTE*)&m_iStartParam, &dwSize);
        dwSize = sizeof(m_iIncParam);
        RegQueryValueEx(hkey, "Inc", NULL, &dwType, (BYTE*)&m_iIncParam, &dwSize);
        RegCloseKey(hkey);
    }
    m_iState = m_iStartParam;
    return TRUE;
}

Run:服務主迴圈

void CMyService::Run()
{
    while (m_bIsRunning)
    {
        DebugMsg("My service is sleeping (%lu)...", m_iState);
        Sleep(1000);
        m_iState += m_iIncParam;
    }
}

OnUserControl:儲存狀態到登錄檔

BOOL CMyService::OnUserControl(DWORD dwOpcode)
{
    if (dwOpcode == SERVICE_CONTROL_USER + 0)
    {
        SaveStatus(); // 寫入登錄檔供其他程式讀取
        return TRUE;
    }
    return FALSE;
}

服務除錯

  • DebugBreak() 啟動系統除錯器
  • CNTService::DebugMsg 輸出追蹤訊息
  • 需安裝 WinDbg (Platform SDK) 或使用 Visual Studio 除錯器
  • SCM 逾時機制限制:不可單步中斷服務主迴圈,僅能觀察輸出

結語

CNTService 類別大幅簡化 Win32 服務開發,衍生類別專注業務邏輯,適合快速建置背景服務。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved