最近项目比较多,现在终于算是告一段落。现在整理一下目前用到的一些功能和一些注意点。
本次说的是c++获取系统的服务名称和状态(主要用于监控项目发布的服务状态,配合监控界面和后台守护服务确保服务正常运行)。
#include <map>
#include <Windows.h> //此头文件不建议放在.h类的文件里
#define SERVICE_STA_UN 111
typedef 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); //分配内存,最大为8KB
if (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);
}
与指定计算机上的服务控制管理器建立连接,并打开指定的服务控制管理器数据库。
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)
从堆中分配指定的字节数。
注意 与其他内存管理功能相比,本地函数的开销更大,提供的功能更少。 除非文档指出应使用本地函数,否则新应用程序应使用 堆 函数。 有关详细信息,请参阅 全局和本地函数。
语法:
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);
在网上看到个设置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;
}