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

如何获取Windows服务列表并打印名称、状态及可执行文件路径

实现思路与完整代码

你现有代码存在以下问题:

  • 直接覆盖OpenSCManager返回的句柄,会导致资源泄漏,且无法执行后续的服务枚举操作
  • 仅支持查询固定名称的单个服务,未实现全量服务枚举逻辑
  • QueryServiceConfig调用逻辑错误:第一次调用需要先传入空缓冲区获取所需内存大小,再分配对应大小的内存后二次调用才能拿到正确配置
  • 无任何资源释放逻辑,会产生内存、句柄泄漏
具体实现逻辑
  1. 打开服务控制管理器,申请SC_MANAGER_ENUMERATE_SERVICE权限即可(遵循最小权限原则,不需要全权限)
  2. 调用EnumServicesStatusEx枚举所有服务类型的服务,一次性获取服务的名称、显示名称、当前运行状态
  3. 遍历枚举结果中的每个服务,打开服务后调用QueryServiceConfig两次:第一次获取配置所需的缓冲区大小,第二次读取配置获取可执行文件路径
  4. 打印对应信息后,依次释放申请的内存、关闭打开的服务句柄、服务控制管理器句柄

注意:程序需要以管理员身份运行,否则部分系统级服务的信息无法正常读取

完整可运行代码
#include <Windows.h>
#include <iostream>
#include <tchar.h>

int _tmain()
{
    // 1. 打开服务控制管理器
    SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
    if (hSCM == NULL) {
        std::cout << "打开服务控制管理器失败,错误码:" << GetLastError() << std::endl;
        return 1;
    }

    DWORD dwBytesNeeded = 0, dwServicesReturned = 0, dwResumeHandle = 0;
    // 第一次调用EnumServicesStatusEx获取需要的缓冲区大小
    EnumServicesStatusEx(hSCM, SERVICE_WIN32, SERVICE_STATE_ALL, SERVICE_ENUMERATE_PROCESS_NAME,
        NULL, 0, &dwBytesNeeded, &dwServicesReturned, &dwResumeHandle, NULL);
    if (GetLastError() != ERROR_MORE_DATA) {
        std::cout << "获取服务枚举缓冲区大小失败,错误码:" << GetLastError() << std::endl;
        CloseServiceHandle(hSCM);
        return 1;
    }

    // 分配缓冲区
    LPENUM_SERVICE_STATUS_PROCESSEX lpServices = (LPENUM_SERVICE_STATUS_PROCESSEX)LocalAlloc(LPTR, dwBytesNeeded);
    if (lpServices == NULL) {
        std::cout << "内存分配失败" << std::endl;
        CloseServiceHandle(hSCM);
        return 1;
    }

    // 第二次调用枚举所有服务
    if (!EnumServicesStatusEx(hSCM, SERVICE_WIN32, SERVICE_STATE_ALL, SERVICE_ENUMERATE_PROCESS_NAME,
        (LPBYTE)lpServices, dwBytesNeeded, &dwBytesNeeded, &dwServicesReturned, &dwResumeHandle, NULL)) {
        std::cout << "枚举服务失败,错误码:" << GetLastError() << std::endl;
        LocalFree(lpServices);
        CloseServiceHandle(hSCM);
        return 1;
    }

    // 遍历每个服务
    for (DWORD i = 0; i < dwServicesReturned; i++) {
        _tprintf(_T("服务名称:%s\n"), lpServices[i].lpServiceName);
        _tprintf(_T("显示名称:%s\n"), lpServices[i].lpDisplayName);
        
        // 转换运行状态为可读文本
        LPCTSTR szStatus = _T("未知");
        switch (lpServices[i].ServiceStatusProcess.dwCurrentState) {
            case SERVICE_STOPPED: szStatus = _T("已停止"); break;
            case SERVICE_RUNNING: szStatus = _T("运行中"); break;
            case SERVICE_PAUSED: szStatus = _T("已暂停"); break;
            case SERVICE_START_PENDING: szStatus = _T("正在启动"); break;
            case SERVICE_STOP_PENDING: szStatus = _T("正在停止"); break;
            case SERVICE_PAUSE_PENDING: szStatus = _T("正在暂停"); break;
            case SERVICE_CONTINUE_PENDING: szStatus = _T("正在恢复"); break;
        }
        _tprintf(_T("运行状态:%s\n"), szStatus);

        // 打开服务获取配置
        SC_HANDLE hService = OpenService(hSCM, lpServices[i].lpServiceName, SERVICE_QUERY_CONFIG);
        if (hService != NULL) {
            DWORD dwConfigSize = 0;
            // 第一次调用获取配置缓冲区大小
            QueryServiceConfig(hService, NULL, 0, &dwConfigSize);
            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
                LPQUERY_SERVICE_CONFIG lpConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, dwConfigSize);
                if (lpConfig != NULL) {
                    if (QueryServiceConfig(hService, lpConfig, dwConfigSize, &dwConfigSize)) {
                        _tprintf(_T("可执行文件路径:%s\n"), lpConfig->lpBinaryPathName);
                    }
                    LocalFree(lpConfig);
                }
            }
            CloseServiceHandle(hService);
        }
        _tprintf(_T("----------------------------------------\n"));
    }

    // 释放资源
    LocalFree(lpServices);
    CloseServiceHandle(hSCM);
    system("pause");
    return 0;
}

内容的提问来源于stack exchange,提问作者Kishan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 09:06:03