windows c++ (5) 获取系统服务名称和状态

2023-12-28 11:59:17

最近项目比较多,现在终于算是告一段落。现在整理一下目前用到的一些功能和一些注意点。
本次说的是c++获取系统的服务名称和状态(主要用于监控项目发布的服务状态,配合监控界面和后台守护服务确保服务正常运行)。

1、代码实现

#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);
}

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 内存管理不提供单独的本地堆和全局堆。 因此, LocalAlloGlobalAlloc 函数实质上是相同的。(有机会可以看看,还有对应的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;

}


文章来源:https://blog.csdn.net/weixin_36196023/article/details/135260911
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。