Windows系统服务编程
1.简介
系统服务是后台进程运行的程序,没有界面。它是通过服务管理控制器(Service Control Manager, SCM)管理的,SCM可以操作系统服务启动、停止、自动运行等。
SCM提供的功能:
(1)服务程序: 一种为一个或多个服务提供可执行代码的程序。服务程序使用连接到SCM并将状态信息发送到SCM的功能。
(2)服务配置程序: 查询或修改服务数据库的程序。服务配置程序使用以下功能来打开数据库,在数据库中安装或删除服务以及查询或修改已安装服务的配置和安全性参数。服务配置程序同时管理服务和驱动程序服务。
(3)服务控制程序: 启动并控制服务和驱动程序服务的程序。服务控制程序使用将请求发送到执行请求的SCM的功能。
win32服务程序主要包含两个部分:
一是服务入口函数ServiceMainW(),该函数完成服务程序初始化的工作;
二是服务控制函数ServiceCtrlHandler(),回调函数,该函数接收SCM发来的控制请求,完成对服务程序的控制,如启动、停止;
2.win32服务api介绍
这边说明下,项目是使用UNICODE字符集的,因此直接使用API CreateServiceW, 如果使用多字节字符集则为CreateServiceA(也可以直接使用CreateService由编译器设置做自动配置).。后面不在做此说明。
2.1 CreateServiceW 函数,创建一个服务对象,并将其添加到指定的服务控制管理器数据库中
SC_HANDLE CreateServiceW(
SC_HANDLE hSCManager, //服务控制管理器数据库的句柄
LPCWSTR lpServiceName, //要安装的服务的名称
LPCWSTR lpDisplayName, //用户界面程序用来标识服务的显示名称
DWORD dwDesiredAccess, //访问服务
DWORD dwServiceType, //服务类型
DWORD dwStartType, //服务启动选项
DWORD dwErrorControl, //如果此服务无法启动,则错误的严重性和采取的措施
LPCWSTR lpBinaryPathName, //服务二进制文件的标准路径。如果路径包含空格,则必须用引号引起来,以便正确解释
LPCWSTR lpLoadOrderGroup, //该服务所属的负载排序组的名称。如果服务不属于组,则指定NULL或空字符串。
LPDWORD lpdwTagId, //指向变量的指针,该变量接收在lpLoadOrderGroup参数指定的组中唯一的标记值。如果不更改现有标签,请指定NULL。
LPCWSTR lpDependencies, //如果服务没有依赖性,则指定NULL或空字符串
LPCWSTR lpServiceStartName, //服务将在其下运行的帐户的名称
LPCWSTR lpPassword //lpServiceStartName参数指定的帐户名的密码
);
返回值
如果函数成功,则返回值是服务的句柄。
如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用 GetLastError。
备注
该CreateService函数创建一个服务对象,并通过创建具有相同名称的主要为以下注册表项下的服务将其安装在服务控制管理器数据库:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
2.2 OpenSCManagerW 函数,建立到指定计算机上的服务控制管理器的连接,并打开指定的服务控制管理器数据库。
SC_HANDLE OpenSCManagerW(
LPCWSTR lpMachineName, //目标计算机的名称。如果指针为NULL或指向空字符串,则该函数将连接到本地计算机上的服务控制管理器。
LPCWSTR lpDatabaseName, //服务控制管理器数据库的名称。此参数应设置为SERVICES_ACTIVE_DATABASE。如果为NULL,则默认情况下将打开SERVICES_ACTIVE_DATABASE数据库。
DWORD dwDesiredAccess //对服务控制管理器的访问
);
返回值
如果函数成功,则返回值是指定服务控制管理器数据库的句柄。
如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用 GetLastError。
备注
(1)当进程使用 OpenSCManager函数打开服务控制管理器数据库的句柄时,系统将在授予请求的访问权限之前执行安全检查。
(2)只有具有管理员权限的进程才能打开CreateService函数可以使用的数据库句柄 。
(3)返回的句柄仅对调用OpenSCManager函数的进程有效 。可以通过调用CloseServiceHandle函数将其关闭 。
2.3 OpenServiceW 函数,打开现有服务
SC_HANDLE OpenServiceW(
SC_HANDLE hSCManager, //服务控制管理器数据库的句柄
LPCWSTR lpServiceName, //要打开的服务的名称
DWORD dwDesiredAccess //访问服务
);
返回值
如果函数成功,则返回值是服务的句柄。
如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用 GetLastError。
备注
(1)返回的句柄仅对调用OpenService的进程有效 。可以通过调用CloseServiceHandle函数将其关闭 。
(2)要使用OpenService,除了SC_MANAGER_CONNECT之外,不需要任何特权。
2.4 CloseServiceHandle 函数,关闭服务控制管理器或服务对象的句柄。
BOOL CloseServiceHandle(
SC_HANDLE hSCObject //服务控制管理器对象或要关闭的服务对象的句柄
);
返回值
如果函数成功,则返回值为非零。
如果函数失败,则返回值为零。要获取扩展的错误信息,请调用 GetLastError。
备注
该 CloseServiceHandle功能不会销毁手柄称为服务控制管理器对象。服务控制管理器对象无法销毁。可以通过调用DeleteService函数销毁服务对象。
2.5 StartServiceCtrlDispatcherW 函数,将服务进程的主线程连接到服务控制管理器,后者使该线程成为调用过程的服务控制调度程序线程。
BOOL StartServiceCtrlDispatcherW(
const SERVICE_TABLE_ENTRYW *lpServiceStartTable //指向SERVICE_TABLE_ENTRY结构数组的指针,该结构包含可以在调用过程中执行的每个服务的一个条目。表中最后一个条目的成员必须具有NULL值才能指定表的末尾。
);
返回值
如果函数成功,则返回值为非零。
如果函数失败,则返回值为零。要获取扩展的错误信息,请调用 GetLastError。
备注
(1)服务控制管理器启动服务过程时,将等待该过程调用StartServiceCtrlDispatcher函数。服务进程的主线程应在启动后尽快(在30秒内)进行此调用。如果StartServiceCtrlDispatcher成功,它将调用线程连接到服务控制管理器,直到该进程中所有正在运行的服务都进入SERVICE_STOPPED状态才返回。服务控制管理器使用此连接将控制和服务启动请求发送到服务进程的主线程。通过调用适当的HandlerEx函数来处理控制请求,或通过在启动新服务时创建新的线程来执行适当的ServiceMain函数,主线程充当调度程序。
(2)启动服务时,所有初始化任务都在服务的ServiceMain函数中完成。
2.6 RegisterServiceCtrlHandlerExW 函数,注册一个函数来处理扩展的服务控制请求。
SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerExW(
LPCWSTR lpServiceName, //调用线程运行的服务的名称
LPHANDLER_FUNCTION_EX lpHandlerProc, //指向要注册的处理函数的指针
LPVOID lpContext //任何用户定义的数据
);
返回值
如果函数成功,则返回值是服务状态句柄。
如果函数失败,则返回值为零。要获取扩展的错误信息,请调用 GetLastError。
备注
(1)新服务的 ServiceMain函数应立即调用 RegisterServiceCtrlHandlerEx函数,以向控件调度程序注册控件处理函数。
(2)该 RegisterServiceCtrlHandlerEx函数必须在第一次之前调用 SetServiceStatus调用,因为 RegisterServiceCtrlHandlerEx返回服务状态句柄调用者使用,因此没有其他服务可以在不经意间设置该服务状态。另外,在服务指定通过SetServiceStatus函数接受的控件之前,控件处理程序必须到位才能接收控件请求 。
(3)当通过控制请求调用控制处理程序函数时,仅当服务状态已更改时(例如,当服务正在处理停止或关闭控件时),服务必须调用 SetServiceStatus向服务控制管理器报告状态。如果服务状态未更改,则该服务不应将状态报告给服务控制管理器。
(4)服务状态句柄不必关闭。
2.7 SetServiceStatus 函数,更新呼叫控制的服务控制管理器的状态信息。
BOOL SetServiceStatus(
SERVICE_STATUS_HANDLE hServiceStatus, //当前服务的状态信息结构的句柄
LPSERVICE_STATUS lpServiceStatus //指向SERVICE_STATUS结构的指针 包含呼叫服务的最新状态信息
);
返回值
如果函数成功,则返回值为非零。