如何获取Windows服务列表并打印名称、状态及可执行文件路径
实现思路与完整代码
你现有代码存在以下问题:
- 直接覆盖
OpenSCManager返回的句柄,会导致资源泄漏,且无法执行后续的服务枚举操作 - 仅支持查询固定名称的单个服务,未实现全量服务枚举逻辑
QueryServiceConfig调用逻辑错误:第一次调用需要先传入空缓冲区获取所需内存大小,再分配对应大小的内存后二次调用才能拿到正确配置- 无任何资源释放逻辑,会产生内存、句柄泄漏
具体实现逻辑
- 打开服务控制管理器,申请
SC_MANAGER_ENUMERATE_SERVICE权限即可(遵循最小权限原则,不需要全权限) - 调用
EnumServicesStatusEx枚举所有服务类型的服务,一次性获取服务的名称、显示名称、当前运行状态 - 遍历枚举结果中的每个服务,打开服务后调用
QueryServiceConfig两次:第一次获取配置所需的缓冲区大小,第二次读取配置获取可执行文件路径 - 打印对应信息后,依次释放申请的内存、关闭打开的服务句柄、服务控制管理器句柄
注意:程序需要以管理员身份运行,否则部分系统级服务的信息无法正常读取
完整可运行代码
#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
相关产品推荐
相关产品推荐

