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

VC++ 開發帶圖形界面的 Windows 服務程序:從 COM 服務到 MFC 服務的完整遷移指南

     編輯:關於VC++

前言

Windows 服務程式在使用者登入前即於背景執行,適合監控、備份、伺服器等長期任務。但預設服務無法直接顯示 GUI(Session 0 隔離)。本文詳細示範:以 ATL COM 服務為藍本,移植 CServiceModule 至 MFC 專案,並啟用「與桌面互動」實現服務啟動時自動彈出對話框。

一、Windows 服務基礎知識

  • 管理工具 → 服務(services.msc)查看所有服務。
  • 服務對應註冊表 HKLM\SYSTEM\CurrentControlSet\Services\。
  • 關鍵值:ImagePath(執行檔路徑)、Start(2=自動, 3=手動)、Type(0x20=獨立進程)、ObjectName(LocalSystem)。

二、建立 ATL COM 服務範本(作為代碼來源)

  1. VC6.0 → New Project → ATL COM AppWizard → 專案名 TestSvc → 選 Service (EXE) → Finish。
  2. 編譯生成 TestSvc.exe。
  3. 命令列執行 TestSvc.exe /regserver 註冊為服務,/unregserver 移除。

三、核心類別 CServiceModule 解析

ATL 產生的 CServiceModule 封裝了服務安裝、解除安裝、啟動、控制處理器(Handler)、訊息迴圈等完整邏輯:

  • _tWinMain:進入點,解析命令列參數。
  • Start():建立 SERVICE_TABLE_ENTRY 映射服務名至 ServiceMain。
  • ServiceMain:註冊 Handler 處理 SCM 指令(啟動/停止/暫停/繼續)。
  • Handler:接收 SERVICE_CONTROL_STOP 等操作碼,發送 PostThreadMessage 通知主執行緒退出。
  • Run():標準 GetMessage/DispatchMessage 迴圈。
  • Install()/Uninstall():呼叫 OpenSCManager/CreateService 操作 SCM 資料庫(優於直接寫註冊表)。

四、將 CServiceModule 移植至 MFC 專案

1. 複製核心代碼

  • 將 TestSvc.cpp 中 CServiceModule 完整實現複製至 MFC 專案的 MyApp.cpp。
  • 將 stdafx.h 中的類別宣告、#include <winsvc.h>、全域 _Module 變數複製至 MFC 專案 stdafx.h。

2. 精簡:移除 COM 相關代碼

MFC 專案通常不需 COM 伺服器功能,刪除/註釋:

  • RegisterServer(), UnregisterServer()
  • CComModule::Init 呼叫
  • IDR_TestSvc, LIBID_TestSvcLib, CoInitializeSecurity 等 COM 符號

重寫 Run() 為純訊息迴圈:

void CServiceModule::Run()
{
    _Module.dwThreadID = GetCurrentThreadId();
    LogEvent(_T("Service started"));
    if (m_bService) SetServiceStatus(SERVICE_RUNNING);
    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))
        DispatchMessage(&msg);
}

3. 資源與入口點調整

  • 資源檔新增字串 IDS_SERVICENAME = "MyMfcService"。
  • 註釋 _tWinMain(使用 #if 0 ... #endif),MFC 使用 CWinApp 入口。
  • 移除 inline 關鍵字避免連結錯誤(Install/Uninstall)。

五、MFC 應用程式類別整合服務邏輯

1. 對話框加入「安裝/解除安裝服務」按鈕

void CMfc1Dlg::OnButtonInstall() { _Module.Install(); }
void CMfc1Dlg::OnButtonUninstall() { _Module.Uninstall(); }

2. 修改 InitInstance:啟動服務模式並顯示對話框

BOOL CMfc1App::InitInstance()
{
    // ... 標準初始化 ...

    _Module.Init(NULL, m_hInstance, IDS_SERVICENAME, NULL);
    _Module.m_bService = TRUE; // 關鍵:標記為服務模式
    _Module.Start();           // 進入服務訊息迴圈(阻塞)

    // 以下代碼在服務停止前不會執行
    return FALSE;
}

3. 將對話框建立移至 Run() 中

void CServiceModule::Run()
{
    _Module.dwThreadID = GetCurrentThreadId();
    LogEvent(_T("Service started"));
    if (m_bService) SetServiceStatus(SERVICE_RUNNING);

    // ★ 在服務狀態設定後建立並顯示主對話框
    CMfc1Dlg dlg;
    m_pMainWnd = &dlg;
    dlg.DoModal(); // 或 Create + ShowWindow 配合訊息迴圈

    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))
        DispatchMessage(&msg);
}

六、關鍵:啟用「與桌面互動」顯示 GUI

預設服務運行於 Session 0,無法存取使用者桌面。修改 Install() 中的 CreateService 呼叫,加入 SERVICE_INTERACTIVE_PROCESS 標誌:

SC_HANDLE hService = ::CreateService(
    hSCM, m_szServiceName, m_szServiceName,
    SERVICE_ALL_ACCESS,
    SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, // ★ 關鍵標誌
    SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
    szFilePath, NULL, NULL, _T("RPCSS\0"), NULL, NULL);

重新編譯、解除安裝舊服務、安裝新服務。在「服務」管理單元中啟動服務,MFC 對話框將正常彈出於當前使用者桌面。

七、設定自動啟動

將 SERVICE_DEMAND_START (3) 改為 SERVICE_AUTO_START (2) 即可隨系統自動啟動。

結語

本文實現了「MFC 程式 ↔ Windows 服務」的雙向轉換:平時作為一般應用程式開發除錯,部署時註冊為服務實現開機自啟與 GUI 互動。此架構適用於需長期駐留且需人工介入配置的監控類軟體。

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