Windows 服務默認為 GUI 程序(_tWinMain 入口),printf/cout 無輸出。利用 _DEBUG 宏區分:
#ifdef _DEBUG
#define OUT(s) printf_s(s)
#define OUT_LN(s) printf_s(s "\r\n")
#else
#define OUT(s) OutputDebugString(s)
#define OUT_LN(s) OutputDebugString(s)
#endif
class CServiceModule : public ATL::CAtlServiceModuleT<...> {
public:
HRESULT Run(int nShowCmd = SW_HIDE) throw() {
OUT_LN("準備啟動服務");
HRESULT hr = Load();
if (FAILED(hr)) { OUT_LN("啟動失敗"); return hr; }
OUT_LN("服務已啟動");
hr = __super::Run(nShowCmd); // 進入服務主循環
UnLoad();
OUT_LN("服務正常退出");
return hr;
}
};
// 入口函數分離
#ifndef _DEBUG
extern "C" int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int nShowCmd) {
return _AtlModule.WinMain(nShowCmd);
}
#else
int _tmain(int argc, _TCHAR* argv[]) {
return _AtlModule.WinMain(SW_SHOW); // 控制台顯示
}
#endif
關鍵設置:專案屬性 → 鏈接器 → 系統 → 子系統:Not Set(自動根據入口函數判斷)。Debug 版自動成 Console,Release 版自動成 Windows。
重寫 ParseCommandLine 支持 -Auto(自動啟動)與 -service Dep1 Dep2 ...(依賴服務):
bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) {
// ... 標準解析 ...
if (WordCmpI(lpszToken, _T("Auto")) == 0) dwStartType = SERVICE_AUTO_START;
if (WordCmpI(lpszToken, _T("Service")) == 0) {
// 解析依賴服務列表存入 dependServices 雙重空字符結尾字符串
*pnRetCode = RegisterAppId(true); // 內部調用 CreateService 寫入依賴
return false;
}
return true;
}
BOOL Install() {
SC_HANDLE hService = CreateService(
hSCM, name, name, SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS, dwStartType, SERVICE_ERROR_NORMAL,
szFilePath, NULL, NULL, dependServices, NULL, NULL);
}
Services.exe -Auto -service CryptSvc RPCSS DcomLaunch
註冊後服務管理器顯示:啟動類型=自動,依賴關係正確列出三個服務。