14-服务

服务 service(没有界面,也称后台进程)

使用场景:

  1. 需要开机就自动启动的,可以暂停和重新启动,且不需要任何用户界面的程序。
  2. 需要常驻系统运行的程序。服务与用户关联,不影响其他用户。
  • 服务进程:没有窗口的进程(没有消息)
  • 服务控制管理器 SCM(服务控制管理):用于管理已经注册的服务。可以对其操作。

14-服务_第1张图片
Note:

  1. 服务程序必须为Release控制程序。
  2. 服务是非3环的,所以不能在基于3环的程序中调试。且由SCM启动
  3. 既有跑在内环的服务也有跑在三环的服务;
  4. 管理服务:scm
  5. windows由一个服务控制管理器的程序管理各种服务程序的,安装, 停止,卸载,暂停,修改属性等等
  6. 还可以通过命令行,输入sc,控制各种服务程序
  7. 也可以自己写控制服务程序, 专门控制自己的服务程序

调试输出:OutPutDebugString

步骤:

  1. 打开SCM,给权限(打开SCM做什么)一般全部都要。
  2. 创建服务
  3. 关闭资源句柄

服务的程序入口点:Servicemain

  • 由main函数调用:告知SCM过程函数在哪里。
  • 调用SERVICE_TABLE_ENTRY 结构体数组注册。

服务的大体结构

服务有scm(系统的一个组件),用来管理所有的服务:比如通知服务启动,暂停,停止,继续;
每当scm要管理服务的时候,就调他一个回调函数;
服务主要体现在回调函数,想干什么,在回调函数中写自己的逻辑即可;

常用API

创建一个简单的Windows服务需要涉及以下API:

  1. OpenSCManager:打开服务控制管理器。
  2. CreateService:创建Windows服务。
  3. StartService:启动Windows服务。
  4. SetServiceStatus:设置Windows服务状态。
  5. RegisterServiceCtrlHandler:注册Windows服务的控制处理程序。
OpenSCManager() 打开SCM:服务控制管理器
LPCSTR lpMachineName,  计算机名,本地计算机给NULL
LPCSTR lpDatabaseName, 服务的数据库,一般给NULL
DWORD dwDesiredAccess,SCM的具体访问权限,一般给 SC_MANAGER_ALL_ACCESS 
CreateService(); 安装/创建服务
SC_HANDLE hSCManager:SCM句柄,由系统函数OpenSCManager 返回
LPCTSTR lpServiceName:以NULL 结尾的服务名,用于创建登记数据库中的关键字
LPCTSTR lpDisplayName:以NULL 结尾的服务名,用于用户界面标识服务
DWORD dwDesiredAccess:指定服务返回类型
DWORD dwServiceType:指定服务类型,三环程序需要使用给定类型SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS
DWORD dwStartType:指定何时启动服务
DWORD dwErrorControl:指定服务启动失败的严重程度
LPCTSTR lpBinaryPathName:指定服务程序二进制文件的全路径
LPCTSTR lpLoadOrderGroup:指定顺序装入的服务组名,给NULL0
LPDWORD lpdwTagId:忽略,给NULL0
LPCTSTR lpDependencies:指定启动该服务前必须先启动的服务或服务组,给NULL0
LPCTSTR lpServiceStartName:以NULL 结尾的字符串,指定服务帐号。如是NULL,则表示使用LocalSystem帐号
LPCTSTR lpPassword:以NULL结尾的字符串,指定对应的口令。为NULL表示无口令。但使用LocalSystem时填NULL

服务程序实现CODE:需设为Release版本,使用OutputDebugString
服务程序里的main()函数的唯一作用:使用StartServiceCtrlDispatcher()r告诉SCM,ServiceMain()的位置

  • RegisterServiceCtrlHandler():注册接收控制码的回调函数
  • SetServiceStatus():设置服务程序的状态

代码测试

#include 
#include 
#define SERVICE_NAME (LPSTR)"MyService"

template<typename T>
void Output(T szFmt) {}


template<typename T,typename... ARGS>
void Output(T szFmt,ARGS... args)
{
    char szBuff[MAXBYTE] = {};
    wsprintf(szBuff,szFmt,args...);
    OutputDebugString(szBuff);
}
VOID WINAPI Handler(DWORD fdwControl)
{
    switch (fdwControl)
    {
    case SERVICE_CONTROL_CONTINUE:
        Output("[Service]:服务开始运行");
        break;
    case SERVICE_CONTROL_PAUSE:
        Output("[Service]:服务暂停");
        break;
    case SERVICE_CONTROL_STOP:
    case SERVICE_CONTROL_SHUTDOWN:
        Output("[Service]:服务停止");
        break;
    default:
        break;
    }
}
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
    //注册控制状态回调函数
    SERVICE_STATUS_HANDLE hservice=  RegisterServiceCtrlHandler(SERVICE_NAME, Handler);
    if (hservice == NULL)
    {
        //输出错误信息
        Output("[Service]:RegisterServiceCtrlHandler failed %d",GetLastError());
        return;
    }
}

// servicemain 数组,以全0为结尾
SERVICE_TABLE_ENTRY g_DisPatcher[] =
{
    {SERVICE_NAME,ServiceMain},
    {NULL,NULL}
};


int main()
{
    //注册ServiceMain
    BOOL bRet = StartServiceCtrlDispatcher(g_DisPatcher);

    if (!bRet)
    {
        Output("[Service]:StartServiceCtrlDispatcher failed %d",GetLastError());
    }
    
}

在win7环境中使用sc 去启动该服务
14-服务_第2张图片
相关命令
sc create 服务名 binPath= 程序路径
sc create MyService binPath= C:\Users\Onlyxiu\Desktop\41期\第二阶段\14-服务\ServicesTest.exe DisplayName= "服务测试" start= demand
删除服务
sc delete Myservice

使用services.msc 命令打开服务管理 工具
14-服务_第3张图片
运行会出现该错误
14-服务_第4张图片

使用View Dependencies工具查看,你会发现该程序加载如下DLL,若你没有配好相关的环境,就会报错。
14-服务_第5张图片
解决方法:
打开项目的属性 C/C++ ,选择代码生成,将运行库改为多线程(MT)
14-服务_第6张图片
再次使用View Dependencies工具查看
14-服务_第7张图片
再次运行查看调试输出的信息,显示报错
14-服务_第8张图片

完整代码测试

// ServicesTest.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include 
#include 
#define SERVICE_NAME (LPSTR)"MyService"  // 定义服务名称为"MyService"


SERVICE_STATUS_HANDLE g_hservice = NULL;  // 服务状态句柄
template<typename T>
void Output(T szFmt) { OutputDebugString(szFmt); }  // 定义输出调试信息的函数

VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv);  // 定义服务主函数

template<typename T, typename... ARGS>
void Output(T szFmt, ARGS... args)
{
    char szBuff[MAXBYTE] = {};
    wsprintf(szBuff, szFmt, args...);  // 格式化输出调试信息
    OutputDebugString(szBuff);
}
// servicemain 数组,以全0为结尾
SERVICE_TABLE_ENTRY g_DisPatcher[] =
{
    {SERVICE_NAME,ServiceMain},  // 服务调度表中第一项为服务名称,第二项为服务主函数
    {NULL,NULL}  // 服务调度表以全零结尾
};

VOID WINAPI Handler(DWORD fdwControl)  // 定义处理控制状态的函数
{

    DWORD dwStatus = SERVICE_RUNNING;

    switch (fdwControl)
    {
    case SERVICE_CONTROL_CONTINUE:
        Output("[Service]:服务开始运行");  // 若控制状态为SERVICE_CONTROL_CONTINUE,输出服务开始运行的调试信息
        break;
    case SERVICE_CONTROL_PAUSE:
        Output("[Service]:服务暂停");  // 若控制状态为SERVICE_CONTROL_PAUSE,输出服务暂停的调试信息
        dwStatus = SERVICE_PAUSED;  // 设置服务状态为暂停
        break;
    case SERVICE_CONTROL_STOP:
    case SERVICE_CONTROL_SHUTDOWN:
        Output("[Service]:服务停止");  // 若控制状态为SERVICE_CONTROL_STOP或SERVICE_CONTROL_SHUTDOWN,输出服务停止的调试信息
        dwStatus = SERVICE_STOPPED;  // 设置服务状态为停止
        break;
    default:
        break;
    }
    //通知 SCM ,初始化成功

    SERVICE_STATUS ss = {
        SERVICE_WIN32_OWN_PROCESS, // 3环的exe程序
        dwStatus,//服务状态
        SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE,//接受的控制
        NO_ERROR,//没有发生错误
    };
    if (!SetServiceStatus(g_hservice, &ss))  // 通过服务状态句柄通知SCM服务状态已更改
    {
        Output("[Service]:SetServiceStatus failed %d", GetLastError());  // 若通知失败,输出错误信息
    }
}

VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)  // 定义服务主函数
{

    OutputDebugString("[Service]: ServiceMain Begin");  // 输出服务开始的调试信息
    //注册控制状态回调函数
    g_hservice = RegisterServiceCtrlHandler(SERVICE_NAME, Handler);  // 注册处理控制状态的函数
    if (g_hservice == NULL)
    {
        //输出错误信息
        Output("[Service]:RegisterServiceCtrlHandler failed %d", GetLastError());

        return;
    }
    //初始化...
    //通知 SCM ,初始化成功

    SERVICE_STATUS ss = {
        ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS, // 3环的exe程序
        ss.dwCurrentState = SERVICE_RUNNING,//服务开始运行
        ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE,//接受的控制
        ss.dwWin32ExitCode = NO_ERROR,//没有发生错误
    };
    if (!SetServiceStatus(g_hservice, &ss))
    {
        Output("[Service]:SetServiceStatus failed %d", GetLastError());
    }
}

int main()
{
    OutputDebugString("[Service]: Hello World!");


    //注册ServiceMain
    BOOL bRet = StartServiceCtrlDispatcher(g_DisPatcher);

    if (!bRet)
    {
        Output("[Service]:StartServiceCtrlDispatcher failed %d", GetLastError());
    }


}


安装服务的时候可以写注册表

服务的安装卸载

MFC界面
14-服务_第9张图片

  1. OpenSCManager函数:用于打开服务控制管理器,以便管理系统中的服务。需要指定一个字符串参数,用于指定服务控制管理器的名称。
  2. CreateService函数:用于创建一个新的服务。需要指定服务控制管理器的句柄、服务名称、服务显示名称、服务访问权限、服务类型、启动类型、错误控制等参数。
  3. StartService函数:用于启动一个已安装的服务。需要指定服务句柄和命令行参数。
  4. ControlService函数:用于发送控制码给一个已安装的服务。需要指定服务句柄和控制码。
  5. DeleteService函数:用于删除一个已安装的服务。需要指定服务句柄。

创建服务对象并将其添加到指定的服务控制管理器数据库中。

#include 
#define SERVICE_NAME (LPSTR)"MyService" // 定义服务名为"MyService"

void CHookTestDlg::OnBnClickedInstall()
{
	//获取SCM
	SC_HANDLE hSCM = OpenSCManager(NULL,NULL, SC_MANAGER_ALL_ACCESS); // 获取SCM管理器的句柄
	if (hSCM == NULL)
	{
		AfxMessageBox("OpenSCManager failed"); // 打开SCM管理器失败
		return;
	}
	//安装
	SC_HANDLE hService = CreateService(
		hSCM,
		SERVICE_NAME, // 指定服务名
		"服务测试", // 指定服务描述
		SERVICE_ALL_ACCESS, // 指定访问权限
		SERVICE_WIN32_OWN_PROCESS, // 指定服务类型
		SERVICE_DEMAND_START, // 指定启动类型
		SERVICE_ERROR_NORMAL,
		"C:\\Users\\Onlyxiu\\Desktop\\41期\\第二阶段\\14-服务\\ServicesTest.exe", // 指定服务路径
		NULL,
		NULL,
		NULL,
		NULL,
		NULL
		);
	if (hService == NULL)
	{
		AfxMessageBox("CreateService failed"); // 创建服务失败
	}
	
	//关闭句柄
	CloseServiceHandle(hService); // 关闭服务句柄
	CloseServiceHandle(hSCM); // 关闭SCM管理器句柄
}


void CHookTestDlg::OnBnClickedUninstall()
{
	//获取SCM
	SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); // 获取SCM管理器的句柄
	if (hSCM == NULL)
	{
		AfxMessageBox("OpenSCManager failed"); // 打开SCM管理器失败
		return;
	}
	//获取服务句柄
	SC_HANDLE hService = OpenService(
		hSCM, 
		SERVICE_NAME, // 指定服务名
		SERVICE_ALL_ACCESS // 指定访问权限
	);
	if (hService == NULL)
	{
		AfxMessageBox("OpenService failed"); // 打开服务失败
		return;
	}
	//卸载服务
	if (!DeleteService(hService)) // 删除服务
	{
		AfxMessageBox("卸载服务失败"); // 卸载服务失败
	}
	//关闭句柄
	CloseServiceHandle(hService); // 关闭服务句柄
	CloseServiceHandle(hSCM); // 关闭SCM管理器句柄
}



void CHookTestDlg::OnBnClickedStart()
{
	// 获取SCM
	SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCM == NULL)
	{
		AfxMessageBox("OpenSCManager failed"); // 打开SCM失败
		return;
	}
	// 获取服务句柄
	SC_HANDLE hService = OpenService(
		hSCM,
		SERVICE_NAME, // 定义服务名的宏
		SERVICE_ALL_ACCESS
	);
	if (hService == NULL)
	{
		AfxMessageBox("OpenService failed"); // 打开服务失败
		return;
	}
	// 启动服务
	if (!StartService(hService, NULL, NULL))
	{
		AfxMessageBox("启动服务失败"); // 启动服务失败
	}
	// 关闭句柄
	CloseServiceHandle(hService);
	CloseServiceHandle(hSCM);
}

void CHookTestDlg::OnBnClickedPause()
{
	// 获取SCM
	SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCM == NULL)
	{
		AfxMessageBox("OpenSCManager failed"); // 打开SCM失败
		return;
	}
	// 获取服务句柄
	SC_HANDLE hService = OpenService(
		hSCM,
		SERVICE_NAME, // 定义服务名的宏
		SERVICE_ALL_ACCESS
	);
	if (hService == NULL)
	{
		AfxMessageBox("OpenService failed"); // 打开服务失败
		return;
	}
	SERVICE_STATUS ss = {};
	// 暂停服务
	if (!ControlService(hService, SERVICE_CONTROL_PAUSE, &ss))
	{
		AfxMessageBox("暂停服务失败"); // 暂停服务失败
	}
	// 关闭句柄
	CloseServiceHandle(hService);
	CloseServiceHandle(hSCM);
}


void CHookTestDlg::OnBnClickedResume()
{
	// 获取SCM
	SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCM == NULL)
	{
		AfxMessageBox("OpenSCManager failed"); // 如果无法打开SCM,则显示错误消息
		return;
	}
	// 获取服务句柄
	SC_HANDLE hService = OpenService(
		hSCM,
		SERVICE_NAME,
		SERVICE_ALL_ACCESS
	);
	if (hService == NULL)
	{
		AfxMessageBox("OpenService failed"); // 如果无法打开服务,则显示错误消息
		return;
	}
	SERVICE_STATUS ss = {};
	// 恢复服务
	if (!ControlService(hService, SERVICE_CONTROL_CONTINUE, &ss))
	{
		AfxMessageBox("恢复服务失败"); // 如果无法恢复服务,则显示错误消息
	}
	// 关闭句柄
	CloseServiceHandle(hService);
	CloseServiceHandle(hSCM);
}

void CHookTestDlg::OnBnClickedStop()
{
	// 获取SCM
	SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCM == NULL)
	{
		AfxMessageBox("OpenSCManager failed"); // 如果无法打开SCM,则显示错误消息
		return;
	}
	// 获取服务句柄
	SC_HANDLE hService = OpenService(
		hSCM,
		SERVICE_NAME,
		SERVICE_ALL_ACCESS
	);
	if (hService == NULL)
	{
		AfxMessageBox("OpenService failed"); // 如果无法打开服务,则显示错误消息
		return;
	}
	SERVICE_STATUS ss = {};
	// 停止服务
	if (!ControlService(hService, SERVICE_CONTROL_STOP, &ss))
	{
		AfxMessageBox("停止服务失败"); // 如果无法停止服务,则显示错误消息
	}
	// 关闭句柄
	CloseServiceHandle(hService);
	CloseServiceHandle(hSCM);
}

服务遍历

  1. OpenSCManager函数:同样也用于打开服务控制管理器,以便管理系统中的服务。
  2. EnumServicesStatusEx函数:用于枚举指定服务控制管理器中的所有服务。需要指定服务控制管理器的句柄、服务类型、服务状态等参数。
  3. CloseServiceHandle函数:用于关闭服务控制管理器句柄
#include 
#include 
using namespace std;

int main()
{
	//获取SCM
	SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); // 获取服务控制管理器句柄,用于之后操作系统中的服务
	if (hSCM == NULL) // 判断句柄获取是否成功
	{
		cout << "OpenSCManager failed" << endl; // 若失败则输出错误信息
		return 0;
	}

	// 枚举系统中的服务信息
	ENUM_SERVICE_STATUS ess[0x500] = {}; // 定义一个ENUM_SERVICE_STATUS类型的数组来存储服务信息
	DWORD dwNeed = 0;
	DWORD dwServicesReturned = 0;
	DWORD dwHandleNext = 0;
	if(!EnumServicesStatus(
		hSCM, // 服务控制管理器句柄
		SERVICE_WIN32, // 要枚举的服务类型(这里选择了SERVICE_WIN32)
		SERVICE_STATE_ALL, // 要获取的服务状态(这里选择了SERVICE_STATE_ALL,表示获取所有服务状态)
		ess, // 存放服务信息的缓冲区
		sizeof(ess), // 缓冲区大小
		&dwNeed, // 需要的缓冲区大小
		&dwServicesReturned, // 返回的服务个数
		&dwHandleNext // 用于遍历服务的句柄
	))
	{
		cout << "EnumServicesStatus failed" << endl; // 若获取服务信息失败,则输出错误信息
	}

	// 输出服务信息
	for (size_t i = 0; i < dwServicesReturned; i++) // 遍历获取到的服务信息
	{
		cout.setf(std::ios::left); // 设置输出方式为左对齐
		cout.width(43); // 设置输出宽度为43个字符
		cout << ess[i].lpServiceName << "  :  "; // 输出服务名称
		cout.width(20); // 设置输出宽度为20个字符
		cout << ess[i].lpDisplayName << endl; // 输出服务显示名称
	}
	
	CloseServiceHandle(hSCM); // 关闭服务控制管理器句柄
	system("pause"); // 暂停程序的执行,以便用户查看输出结果

	return 0;
}

你可能感兴趣的:(Windows编程,windows,C++,Windows编程)