You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:06:07