Windows 服務程式在使用者登入前即於背景執行,適合監控、備份、伺服器等長期任務。但預設服務無法直接顯示 GUI(Session 0 隔離)。本文詳細示範:以 ATL COM 服務為藍本,移植 CServiceModule 至 MFC 專案,並啟用「與桌面互動」實現服務啟動時自動彈出對話框。
services.msc)查看所有服務。HKLM\SYSTEM\CurrentControlSet\Services\。ImagePath(執行檔路徑)、Start(2=自動, 3=手動)、Type(0x20=獨立進程)、ObjectName(LocalSystem)。TestSvc → 選 Service (EXE) → Finish。TestSvc.exe。TestSvc.exe /regserver 註冊為服務,/unregserver 移除。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 資料庫(優於直接寫註冊表)。TestSvc.cpp 中 CServiceModule 完整實現複製至 MFC 專案的 MyApp.cpp。stdafx.h 中的類別宣告、#include <winsvc.h>、全域 _Module 變數複製至 MFC 專案 stdafx.h。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);
}
IDS_SERVICENAME = "MyMfcService"。_tWinMain(使用 #if 0 ... #endif),MFC 使用 CWinApp 入口。inline 關鍵字避免連結錯誤(Install/Uninstall)。void CMfc1Dlg::OnButtonInstall() { _Module.Install(); }
void CMfc1Dlg::OnButtonUninstall() { _Module.Uninstall(); }
BOOL CMfc1App::InitInstance()
{
// ... 標準初始化 ...
_Module.Init(NULL, m_hInstance, IDS_SERVICENAME, NULL);
_Module.m_bService = TRUE; // 關鍵:標記為服務模式
_Module.Start(); // 進入服務訊息迴圈(阻塞)
// 以下代碼在服務停止前不會執行
return FALSE;
}
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);
}
預設服務運行於 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 互動。此架構適用於需長期駐留且需人工介入配置的監控類軟體。