ServiceMain函数必须严格使用VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)签名,不可自定义名称或返回类型;它由StartServiceCtrlDispatcher按名查找调用,需立即注册控制处理器、分步上报状态(如SERVICE_START_PENDING→SERVICE_RUNNING),并在30秒内完成初始化,否则SCM将终止进程。

服务主函数必须用 ServiceMain 命名且符合特定签名
Windows 服务进程启动后,StartServiceCtrlDispatcher 会按名称查找并调用 ServiceMain 函数。它不是任意命名的入口,也不能返回 int 或接受命令行参数。签名必须是:
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv);
常见错误是写成 main() 风格或加 __stdcall 拼写错误(如 WINAPI 写成 WINALPI),导致服务启动失败且事件日志只报“服务未及时响应启动请求”。
-
dwArgc是服务控制管理器传入的参数个数,通常为 1(服务名本身),不包含传统命令行参数 -
lpszArgv[0]是服务名,可用于多服务共用一个可执行文件时做分发 - 该函数需尽快调用
RegisterServiceCtrlHandlerEx设置控制处理器,否则无法响应暂停/停止等指令
服务状态必须主动上报,不能只靠 SetServiceStatus 一次调用
服务处于 SERVICE_START_PENDING 状态时,若 30 秒内未更新状态为 SERVICE_RUNNING,SCM 会直接终止进程。很多初学者在 ServiceMain 开头调一次 SetServiceStatus 就开始执行业务逻辑,结果因初始化耗时过长被杀。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 初始化阶段应分步上报:先设
SERVICE_START_PENDING,完成关键资源(如日志句柄、配置加载)后再切到SERVICE_RUNNING - 长时间操作(如网络连接、数据库初始化)建议用独立线程,主线程保持对 SCM 控制消息的响应能力
- 每次调用
SetServiceStatus前必须更新SERVICE_STATUS结构体中的dwCheckPoint和dwWaitHint,否则状态可能卡住
控制处理器函数里别阻塞,尤其不能用 WaitForSingleObject 等待无限期事件
HandlerEx 函数运行在 SCM 分配的专用线程中,必须快速返回。常见陷阱是在线程里调用 WaitForSingleObject(hMyEvent, INFINITE),导致 SCM 认为服务无响应,后续所有控制请求(包括停止)都被丢弃。
立即学习“C++免费学习笔记(深入)”;
- 只应在
HandlerEx中设置全局标志(如g_ServiceStopRequested = TRUE),具体清理工作交给主业务线程执行 - 若需等待业务线程退出,应在
ServiceMain的末尾做,而不是在HandlerEx里 -
HandlerEx返回前必须再次调用SetServiceStatus更新状态为SERVICE_STOP_PENDING,否则 SCM 不会继续等待
调试服务不能双击运行,必须用 sc create + net start 或服务管理器
直接运行 EXE 文件不会触发 SCM 加载流程,ServiceMain 根本不会被调用。调试时若仍想用 Visual Studio,必须手动附加到服务进程(services.msc 启动后查 PID),或者改用控制台模式开发——把服务逻辑封装成独立函数,在 _tmain 中模拟 SCM 调用 ServiceMain。
- 安装服务命令示例:
sc create MySvc binPath= "C:\path\svc.exe" start= auto(注意等号后有空格) - 卸载前务必先
sc stop MySvc,否则sc delete会失败并提示“服务正在运行” - 日志输出不能依赖
printf或控制台,要用OutputDebugString配合 DbgView,或写入文件(注意权限:服务默认以LocalSystem运行,对C:\根目录无写入权)

















