服务
Windows服务,是指运行在windows nt操作系统后台的计算机程序.
Windows服务必须符合服务控制管理器的接口规则和协议(SCM)
如何创建一个Windows服务
分为:
1.完成服务程序主函数(进程入口点(Main函数
2.完成服务程序内容主函数(ServiceMain
3.服务的注册器和卸载器
服务程序主函数
这个阶段,主要干两件事:
1.设定好SERVICE_TABLE_ENTRY 结构变量,传入服务名和服务主函数
2.调用StartServiceCtrlDispatcher函数
以下是SERVICE_TABLE_ENTRY结构
typedef struct _SERVICE_TABLE_ENTRYW { LPWSTR lpServiceName; LPSERVICE_MAIN_FUNCTIONW lpServiceProc; }SERVICE_TABLE_ENTRYW, *LPSERVICE_TABLE_ENTRYW;
|
所以我们的服务程序的主函数只需要像这样写即可
int main(){ SERVICE_TABLE_ENTRY Table[] = { {L"servicename",ServiceMain},{NULL,NULL} }; StartServiceCtrlDispatcher(Table); }
|
在SERVICE_TABLE_ENTRY里我们定义好了一个服务的名字以及其入口函数,然后使用StartServiceCtrlDispatcher去调用这个结构。
第一步就是这么简单,接下来让我们去实现服务的入口函数
完成服务程序内容主函数
这一步主要要干这几个事情:
1.创建服务内容主函数
2在服务内容主函数里实现 SERVICE_STATUS
结构的填充,用于与SCM交流
3.实现服务句柄,并根据有服务句柄改变状态信息,从而实现SCM发来的控制请求
4.逻辑
创建服务内容主函数
根据我们在 SERVICE_TABLE_ENTRY 结构中定义的服务入口函数,创建对应函数
void WINAPI Servicename(DWORD argc, LPTSTR* argv)
|
实现 SERVICE_STATUS
结构
SERVICE_STATUS
结构定义如下
typedef struct _SERVICE_STATUS { DWORD dwServiceType; DWORD dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; } SERVICE_STATUS, *LPSERVICE_STATUS;
|
每个项都对应了参数…太多了,建议去官方文档对照
https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_status
dwservicetype 指定了服务的类型
dwCurrentState 指定服务当前的状态(挂起,暂停,启动,停止…)
dwControlsAccepted 指定了服务句柄可以接受的参数
dwWin32ExitCode 服务用于报告错误的错误代码
dwServiceSpecificExitCode 服务特定的错误代码
dwCheckPoint 不太懂
dwWaitHint 挂起的 启动,停止,暂停或继续操作所需的估计时间,以毫秒为单位 。若指定时间已过去而dwCheckPoint未增加或者dwCurrentState尚未更改,则中止服务。
总而言之,就是创建一个结构用于收集服务的各个信息
实现服务句柄&改变状态信息
实现服务句柄主要是依靠RegisterServiceCtrlHandlerA函数实现的。
这个函数的作用是注册一个函数来处理指定服务控制请求。
这个函数的结构是这样
SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerA( LPCSTR lpServiceName, LPHANDLER_FUNCTION lpHandlerProc );
|
lpservicename是指定服务名,lphandlerproc是被注册的函数。所以我们要使用这个函数来注册另一个函数,首先要完成被注册函数的内部逻辑。这里做个范例
void WINAPI ctrl(DWORD Opcode) //定义式固定 { switch (Opcode) { case SERVICE_CONTROL_PAUSE: m_ServiceStatus.dwCurrentState = SERVICE_PAUSED; break; case SERVICE_CONTROL_CONTINUE: m_ServiceStatus.dwCurrentState = SERVICE_RUNNING; break; case SERVICE_CONTROL_STOP: m_ServiceStatus.dwWin32ExitCode = 0; m_ServiceStatus.dwCurrentState = SERVICE_STOPPED; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0;
SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus); bRunning = false; break; case SERVICE_CONTROL_INTERROGATE: break; } return; }
|
我们发现这个函数会根据传入的值改变 SERVICE_STATUS
结构。
若把他注册,那么从服务管理控制器发来的控制指令会被当作参数传入该函数,然后该函数会修改 SERVICE_STATUS
。
那么改变状态信息的函数主要就是SetServiceStatus 了
BOOL SetServiceStatus( SERVICE_STATUS_HANDLE hServiceStatus, LPSERVICE_STATUS lpServiceStatus );
|
hservicestatus 指向服务句柄 lpservicestatus 指向 SERVICE_STATUS
结构。
完成这个函数后,服务就会向服务控制管理器发送自己最新的状态信息(即 SERVICE_STATUS
里定义的东西)。
DEMO
#include<Windows.h>
SERVICE_STATUS m_ServiceStatus; SERVICE_STATUS_HANDLE m_ServiceStatusHandle; BOOL bRunning; int main() { WCHAR Servicename[] = L"ServiceA"; SERVICE_TABLE_ENTRY Table[] = { {Servicename,ServiceMain},{NULL,NULL} }; StartServiceCtrlDispatcher(Table); }
void WINAPI ServiceMain(DWORD argc, LPTSTR* argv) {
m_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; m_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; m_ServiceStatus.dwWin32ExitCode = 0; m_ServiceStatus.dwServiceSpecificExitCode = 0; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0;
m_ServiceStatusHandle = RegisterServiceCtrlHandler(L"ServiceA", HandlerFunc); m_ServiceStatus.dwCurrentState = SERVICE_RUNNING; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0; SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus); bRunning = true; while (bRunning) { //your code } return 0; }
void WINAPI HandlerFunc(DWORD code) { switch (code) { case SERVICE_CONTROL_PAUSE: m_ServiceStatus.dwCurrentState = SERVICE_PAUSED; break; case SERVICE_CONTROL_CONTINUE: m_ServiceStatus.dwCurrentState = SERVICE_RUNNING; break; case SERVICE_CONTROL_STOP: m_ServiceStatus.dwWin32ExitCode = 0; m_ServiceStatus.dwCurrentState = SERVICE_STOPPED; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0;
SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus); bRunning = false; break; case SERVICE_CONTROL_INTERROGATE: break; } }
|