最近项目比较多,现在终于算是告一段落。现在整理一下目前用到的一些功能和一些注意点。
本次说的是c++获取系统的服务名称和状态(主要用于监控项目发布的服务状态,配合监控界面和后台守护服务确保服务正常运行)。
1、代码实现
#include <map>
#include <Windows.h> //此头文件不建议放在.h类的文件里#define SERVICE_STA_UN 111typedef struct
{string strName;//服务名称int nSta; //服务状态
}AllSerInfo; //服务结构体map<string,AllSerInfo> g_allService; void GetServices() { g_allService.clear();do{AllSerInfo temp_stu;//1 打开服务管理SC_HANDLE schandle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (schandle == NULL){cout << "打开服务管理失败" << endl;break; //跳出}//2 初始化申请内存LPENUM_SERVICE_STATUS service_staus; //系统服务的结构 DWORD cbBytesNeeded = 0;DWORD serviceReturned = 0;DWORD resumeHandle = 0;DWORD len = sizeof(ENUM_SERVICE_STATUSW);service_staus = (LPENUM_SERVICE_STATUS)LocalAlloc(LPTR, MAX_SERVICE_SIZE);if (service_staus == nullptr){cout << "申请内存失败" << endl;break;}//3 枚举服务内容BOOL ess = ::EnumServicesStatus(schandle, //句柄SERVICE_WIN32, //服务类型 SERVICE_DRIVER驱动 SERVICE_TYPE_ALL所有SERVICE_STATE_ALL, //服务的状态service_staus, //输出参数,系统服务的结构MAX_SERVICE_SIZE, //结构的大小&cbBytesNeeded, //输出参数,接收返回所需的服务&serviceReturned, //输出参数,接收返回服务的数量&resumeHandle); //输入输出参数,第一次调用必须为0,返回0代表成功if (!ess){cout << "枚举失败" << endl;break;}//4 解析赋值服务名称和状态for (int i = 0; i < serviceReturned; i++){ENUM_SERVICE_STATUS service = service_staus[i];string displayName = service.lpDisplayName;string serviceName = service.lpServiceName;temp_stu.strName = serviceName;switch (service.ServiceStatus.dwCurrentState) //服务状态{case SERVICE_STOPPED:temp_stu.nSta = SERVICE_STOPPED; //服务未运行。break;case SERVICE_START_PENDING:temp_stu.nSta = SERVICE_START_PENDING; //服务正在启动。break;case SERVICE_STOP_PENDING:temp_stu.nSta = SERVICE_STOP_PENDING; // 服务正在停止。break;case SERVICE_RUNNING:temp_stu.nSta = SERVICE_RUNNING; //服务正在运行。break;case SERVICE_CONTINUE_PENDING:temp_stu.nSta = SERVICE_CONTINUE_PENDING; // 服务即将继续。break;case SERVICE_PAUSE_PENDING:temp_stu.nSta = SERVICE_PAUSE_PENDING; // 服务即将暂停。break;case SERVICE_PAUSED:temp_stu.nSta = SERVICE_PAUSED; // 服务已暂停。break;default:cout << "未知状态" << endl;temp_stu.nSta = SERVICE_STA_UN ; break;}//5 如果需要对比服务路径是否正确可以用下面的 否则可以不调用LPQUERY_SERVICE_CONFIG lpServiceConfig = nullptr; //服务详细信息结构SC_HANDLE service_current = nullptr; //当前的服务句柄service_current = ::OpenService(schandle, service.lpServiceName, SERVICE_QUERY_CONFIG); //打开当前服务if (service_current == nullptr){cout << "当前服务打开失败" << endl;break;}lpServiceConfig = (LPQUERY_SERVICE_CONFIG)::LocalAlloc(LPTR, MAX_QUERY_SIZE); //分配内存,最大为8KBif (lpServiceConfig == nullptr){cout << "申请LPQUERY_SERVICE_CONFIG内存失败" << endl;break;}if (!::QueryServiceConfig(service_current, lpServiceConfig, MAX_QUERY_SIZE, &resumeHandle)){cout << "查询服务失败" << endl;break;}cout << "服务显示名称:" << displayName << " 服务名称:" << serviceName << "路径:" << lpServiceConfig->lpBinaryPathName << endl;g_allService[serviceName] = temp_stu;LocalFree(lpServiceConfig);::CloseServiceHandle(service_current);// 关闭当前服务}LocalFree(service_staus);::CloseServiceHandle(schandle); //关闭服务} while (0);
}
2、方法解析
2.1 OpenSCManager
与指定计算机上的服务控制管理器建立连接,并打开指定的服务控制管理器数据库。
SC_HANDLE OpenSCManagerA([in, optional] LPCSTR lpMachineName,[in, optional] LPCSTR lpDatabaseName,[in] DWORD dwDesiredAccess
);
参数 | 解析 |
---|---|
lpMachineName | 目标计算机的名称。 如果指针为 NULL 或指向空字符串,则函数将连接到本地计算机上的服务控制管理器。 |
lpDatabaseName | 服务控制管理器数据库的名称。 此参数应设置为 SERVICES_ACTIVE_DATABASE。 如果为 NULL,则默认打开SERVICES_ACTIVE_DATABASE数据库。 |
dwDesiredAccess | 对服务控制管理器的访问。 有关访问权限的列表,请参阅 服务安全性和访问权限。 |
dwDesiredAccess 传参
//
// Service Control Manager object specific access types
//
#define SC_MANAGER_CONNECT 0x0001 //允许连接服务控制管理器。
#define SC_MANAGER_CREATE_SERVICE 0x0002 //表示拥有注册(创建)服务的权限。
#define SC_MANAGER_ENUMERATE_SERVICE 0x0004 //表示拥有枚举系统服务的权限
#define SC_MANAGER_LOCK 0x0008 // 允许锁定服务数据库。
#define SC_MANAGER_QUERY_LOCK_STATUS 0x0010 //允许查询服务数据库的锁定状态。
#define SC_MANAGER_MODIFY_BOOT_CONFIG 0x0020 //允许修改系统启动配置。//SC_MANAGER_ALL_ACCESS 表示拥有一切权限
#define SC_MANAGER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \SC_MANAGER_CONNECT | \SC_MANAGER_CREATE_SERVICE | \SC_MANAGER_ENUMERATE_SERVICE | \SC_MANAGER_LOCK | \SC_MANAGER_QUERY_LOCK_STATUS | \SC_MANAGER_MODIFY_BOOT_CONFIG)
2.2 LocalAlloc
从堆中分配指定的字节数。
注意 与其他内存管理功能相比,本地函数的开销更大,提供的功能更少。 除非文档指出应使用本地函数,否则新应用程序应使用 堆 函数。 有关详细信息,请参阅 全局和本地函数。
语法:
DECLSPEC_ALLOCATOR HLOCAL LocalAlloc([in] UINT uFlags,[in] SIZE_T uBytes
);
Windows 内存管理不提供单独的本地堆和全局堆。 因此, LocalAllo 和 GlobalAlloc 函数实质上是相同的。(有机会可以看看,还有对应的lock锁的应用)
可移动内存标志 LHND、 LMEM_MOVABLE 和 NONZEROLHND 会增加不必要的开销,并要求安全使用锁定。 除非文档明确指出应使用它们,否则应避免使用它们。
除非文档特别指出应使用本地函数,否则新应用程序应使用 堆 函数。 例如,某些 Windows 函数分配必须使用 LocalFree 释放的内存。
如果堆不包含满足请求的足够可用空间, 则 LocalAlloc 返回 NULL。 由于 NULL 用于指示错误,因此永远不会分配虚拟地址 0。 因此,很容易检测到 NULL 指针的使用。
如果 LocalAlloc 函数成功,它将至少分配请求的量。 如果分配的金额大于请求的金额,则进程可以使用整个金额。 若要确定实际分配的字节数,请使用 LocalSize 函数。
若要释放内存,请使用 LocalFree
函数。 使用 GlobalFree 释放通过 LocalAlloc 分配的内存是不安全的。
在上面的例子,原先我是直接网上copy 的,里面没有调用LocalAlloc 释放。大家拷贝注意验证
示例:(LocalAlloc类似 将Global换成Local)
// Malloc memory
hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, nSize);
// Lock memory
pMem = (BYTE *) GlobalLock(hMem);
..................
// Unlock memory
GlobalUnlock(hMem);
GlobalFree(hMem);
3 后记
在网上看到个设置SCM服务加解析的,解释很详细感觉不错贴过来备忘
注册服务:
#include <stdio.h>
#include <windows.h>
#include <Shlwapi.h>
#include<strsafe.h>
#pragma comment(lib,"Shlwapi")//注册服务自启动
void AutoRunService(char* szFilePath, char* lpDisplayName)
{char szServiceName[MAX_PATH] = { 0 };//服务名称SC_HANDLE hSCM;//服务控制管理器,根据服务控制管理器,可以对服务进行查询、创建、启动、停止操作。SC_HANDLE hService; //本次要注册的服务句柄//查到服务控制管理器的句柄hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);//从.sys文件中拿到服务名,服务名即等价于文件名::StringCchCopy(szServiceName,_countof(szServiceName), szFilePath);::PathStripPath(szServiceName);//查一下要注册的服务已经被服务控制管理器管理了没有hService = ::OpenService(hSCM, szServiceName, SERVICE_ALL_ACCESS);if (hService == NULL) {//没有则创建出此服务,并让此服务开机自启动 然后启动服务。SC_HANDLE hNewService = ::CreateService(hSCM, szServiceName, lpDisplayName, SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,SERVICE_ERROR_IGNORE, szFilePath,NULL, NULL, NULL, NULL, NULL);::StartService(hNewService, 0, NULL);::CloseServiceHandle(hNewService);printf("[*] 创建服务完成 \n");}::CloseServiceHandle(hService);::CloseServiceHandle(hSCM);}// 设置服务状态
BOOL SetServiceStatus(char* lpServiceName, int Status)
{SERVICE_STATUS ss;SC_HANDLE hSCM;//服务控制管理器,根据服务控制管理器,可以对服务进行查询、创建、启动、停止操作。BOOL bRet = TRUE;SC_HANDLE hService;//要设置服务状态的服务//查到服务控制管理器的句柄hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);//从服务控制管理器中 依据服务名称 查到此设置服务状态的服务,hService = ::OpenService(hSCM, lpServiceName, SERVICE_ALL_ACCESS);if (hService != NULL){if (Status == 1) //用户要求停止服务{if (!::ControlService(hService, SERVICE_CONTROL_STOP, &ss)){bRet = FALSE;}}else if (Status == 2) //用户要求启动服务{if (!::StartService(hService, 0, NULL)){bRet = FALSE;}}else if (Status == 3) //用户要求删除服务{if (!DeleteService(hService)){bRet = FALSE;}}else{//todo...}}CloseServiceHandle(hSCM);CloseServiceHandle(hService);return bRet;}
int main(int argc, char* argv[])
{// 注册为自启动服务将d:/myservice.exe 注册为自启动服务 后面是描述信息AutoRunService((char*)"d:/myservice.exe", (char*)"Microsoft Windows Security Services");// 根据服务名称管理服务 1=>停止服务 2=>启动服务 3=>删除服务BOOL ret = SetServiceStatus((char*)"myservice.exe", 2);printf("状态: %d \n", ret);return 0;}