C++ MFC对话框程序如何解决点击按钮停止Windows服务代码失效问题
MFC停止Windows服务代码故障排查与修复
现有代码核心缺陷
- 循环逻辑不符合需求:
MAX_VALUE定义为17、循环从0开始,会遍历0-16共17个服务,和你要停止16个服务的目标不匹配。 - API调用参数错误:
OpenService要求传入服务的系统注册名(即注册表HKLM\SYSTEM\CurrentControlSet\Services下的对应键名),而非服务管理器中显示的友好名称,你代码里拼接的带空格名称如果是显示名,会直接打开失败;同时OpenSCManager仅申请了SC_MANAGER_CONNECT权限,不足以完成后续服务停止操作。 - 句柄作用域与资源泄漏:
hndService定义在if (hndSvcMan != NULL)的代码块内部,块外无法访问,根本无法执行后续停止操作;同时每次循环重复打开SCM句柄、所有打开的句柄都未调用CloseServiceHandle释放,会造成句柄泄漏。 - 核心逻辑缺失:代码仅尝试打开SCM和服务,完全没有调用
ControlService发送停止控制码的逻辑,不可能实现停止服务的效果。 - 无错误处理:所有API调用失败后没有捕获错误码,无法定位具体失败原因。
- 权限问题未考虑:系统服务操作需要管理员权限,未提权的程序直接调用相关API会被系统拒绝。
修复后可直接运行的代码
void CTestMFCAppDlg::OnBnClickedButton2() { // 打开服务控制管理器,申请足够操作权限 SC_HANDLE hndSvcMan = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); if (hndSvcMan == NULL) { DWORD err = GetLastError(); CString errTip; errTip.Format(_T("打开服务控制管理器失败,错误码:%d,请确认程序以管理员身份运行"), err); MessageBox(errTip, _T("错误"), MB_ICONERROR); return; } // 遍历1-16共16个服务,如果你的服务序号从0开始就修改循环初始值和上限 const int MAX_SERVICE_COUNT = 16; for (int numberOfService = 1; numberOfService <= MAX_SERVICE_COUNT; numberOfService++) { CString strServiceRegName; // 注意:这里必须替换为服务的实际注册名,不要用服务列表里的显示名 // 可在管理员cmd执行 sc query | findstr /i "Sense" 确认实际名称格式 strServiceRegName.Format(_T("Sense Counter %d"), numberOfService); // 打开服务,申请停止和查询状态权限 SC_HANDLE hndService = OpenService(hndSvcMan, strServiceRegName, SERVICE_STOP | SERVICE_QUERY_STATUS); if (hndService == NULL) { DWORD err = GetLastError(); CString errTip; errTip.Format(_T("打开服务%s失败,错误码:%d"), strServiceRegName, err); OutputDebugString(errTip); continue; } // 发送停止服务指令 SERVICE_STATUS svcStatus; BOOL bStopRet = ControlService(hndService, SERVICE_CONTROL_STOP, &svcStatus); if (!bStopRet) { DWORD err = GetLastError(); // 错误码1062代表服务已处于停止状态,属于正常情况无需报错 if (err != ERROR_SERVICE_NOT_ACTIVE) { CString errTip; errTip.Format(_T("停止服务%s失败,错误码:%d"), strServiceRegName, err); OutputDebugString(errTip); } } else { // 最多等待10秒确认服务完全停止 int waitSec = 0; while (svcStatus.dwCurrentState != SERVICE_STOPPED && waitSec < 10) { Sleep(1000); QueryServiceStatus(hndService, &svcStatus); waitSec++; } } // 关闭服务句柄,避免资源泄漏 CloseServiceHandle(hndService); } // 关闭SCM句柄 CloseServiceHandle(hndSvcMan); MessageBox(_T("服务停止操作执行完成"), _T("提示"), MB_OK); }
配置注意事项
- 服务名确认:必须使用服务的系统注册名调用接口,带空格的友好显示名无法被
OpenService识别。 - 管理员权限配置:需要在项目中添加UAC清单,设置运行权限为
requireAdministrator,否则在开启UAC的Windows系统上会因权限不足操作失败。 - 错误码定位:执行失败时可直接根据输出的错误码定位原因:常见错误码
5为权限不足,1060为服务名不存在,1051为服务被其他依赖项占用无法停止。
内容的提问来源于stack exchange,提问作者JanČi
相关产品推荐
相关产品推荐

