Windows服务是其实一种特殊的二进制可执行文件,后缀名一般为EXE,之所以说它特殊,因为它具有同Windows NT/2K系统的服务控制管理器(SCM: Service Control Manager)通信。
服务控制管理器通过维护数据库对已经安装到系统的所有服务和驱动程序进行统一而安全的控制和管理。服务控制管理器是一个远程进程调用(RPC)服务器,在系统导入时自动启动。
一个简单的服务程序至少包括一些几个部分:
1. Win32/控制台应用主程序;
2. 一个服务主程序,作为服务的导入点;
3. 一个服务控制处理器,就是同服务控制管理器SCM通信的函数;
4. 一个服务安装/反安装程序用于将一个EXE文件注册为一个服务。
下面我们针对上述几个部分分别介绍怎样构造一个Windows服务。
控制台应用主程序
在Win32下为WinMain函数,在控制台下为main函数,是服务的主程序。下面是服务主程序中至少要包含的语句。
#include "Winsvc.h" //服务头文件
main()
{
......
SERVICE_TABLE_ENTRY Table[]={{"gkeyService",gkeyServiceMain},{NULL,NULL}};
StartServiceCtrlDispatcher(Table);
......
}
当然这是一个非常简单的主程序了。这里main只做了一件事情,就是填写SERVICE_TABLE_ENTRY结构数组Table。Table[0][0]是服务的名字(可以是您喜欢的任意字符串,此处我用的是gkeyService);Table[0][1]指定了服务主程序的名字,实际上这是一个指向服务主程序的函数指针,它也可以用您喜欢的函数名字(我用的是gkeyServiceMain)。现在通过调用参数为SERVICE_TABLE_ENTRY结构数组的函数StartServiceCtrlDispatcher()开始启动服务解析。注意这个函数的参数必须要符合一定的格式,Table[1][0]和Table[1][1]必须是NULL,就是说到了数组的结尾。当然并非必须这样,如果需要在这个执行程序中运行多个服务,可以在这个数组列表中加入更多的入口,构成多对服务名称和服务中程序,自然您需要在以下的步骤中需要为每个服务构造相应的完成函数。
服务主程序
典型的服务主程序的声明如下:
void WINAPI gkeyServiceMain( DWORD argc, LPTSTR *argv )
在gkeyServiceMain函数中,需要实现的主要步骤包括:
1. 用合适的值填写SERVICE_STATUS结构来完成同服务控制管理器SCM的通信;
2. 在列表中注册前面所说的服务控制处理函数;
3. 调用实际的处理函数。
为了完成上述功能,需要使用两个全局变量:
SERVICE_STATUS m_ServiceStatus;
SERVICE_STATUS_HANDLE m_ServiceStatusHandle;
服务主程序gkeyServiceMain()能够象通常的c/c++里的main()函数一样接受命令行参数,并且接受参数的方式也完全一样。第一个参数argc包含了传递给服务的参数个数,同c/c++的main()一样至少有一个参数就是服务应用本身。第二个参数是一个字符指针数组的指针。同main()函数一样,数组的第一个值总是指向服务的名字。
使用SERVICE_STATUS数据结构记录服务的当前状态,并将状态及时通告给服务控制管理器SCM,使用一个API函数SetServiceStatus()来实现这一目标。SERVICE_STATUS的数据成结构员如下:
dwServiceType = SERVICE_WIN32;
dwCurrentState = SERVICE_START_PENDING; // 试图启动(初始状态)
dwControlsAccepted = SERVICE_ACCEPT_STOP; // 仅接收服务控制程序的启动/停止,服务控制程序通常在
Windows NT下的控制面板或者Windows 2K下的管理工具,我们也可以设置服务接受暂停/继续功能。
在服务主程序gkeyServiceMain()的开始应该设置SERVICE_STATUS的状态字段dwCurrentState为SERVICE_START_PENDING,通知SCM服务处于运行状态。如果发生错误,应该发送SERVICE_STOPPED通知服务控制管理器SCM。缺省状态下,服务控制管理器SCM将监视服务的活动,如果2分钟之类没有发现进程活动就杀死这个服务。
使用API函数RegisterServiceCtrlHandler()设置服务控制管理器SCM的服务控制处理函数,这个函数需要两个参数,一个是服务名称字符串,一个是服务控制处理函数句柄。
现在要设置dwCurrentState为SERVICE_RUNNING用以通知服务已经启动。
服务控制处理函数
服务控制管理器SCM使用服务控制处理函数和服务程序进行通信来了解服务的诸如启动、停止、暂停或继续等用户指令,它主要包含一个switch语句来处理每种情况,调用相应的步骤来启动、急需、清除和中断进程。函数收到一个象SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_CONTINUE, SERVICE_CONTROL_STOP, SERVICE_CONTROL_INTERROGATE等操作码,就需要为每种指令提供相应的处理步骤。
安装/反安装
要安装一个服务,在系统注册时需要生成一些入口,通常使用Windows有现成的API而不是注册函数来完成这些步骤,这些函数有CreateService()和DeleteService()。为了安装服务,首先使用OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS)打开服务控制管理器SCM。然后调用CreateService()来建立服务,给出服务的名字,如果要删除指定的服务,也将需要使用这个名字删除。
例子代码如下:
// 创建服务
String strSrvName = Application->ExeName;
SC_HANDLE schService = CreateService(
scm,
"ccrunSrv", // 服务名称
"ccrun's Service", // 服务详细说明
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
SERVICE_AUTO_START, // 以自动方式开始
SERVICE_ERROR_NORMAL,
strSrvName.c_str(), // Service本体程序路径,必须与具体位置相符
NULL,
NULL,
NULL,
NULL,
NULL);
if(schService != NULL)
{
CloseServiceHandle(schService);
}
//---------------------------------------------------------------------------
// 开始Service
sHandle = OpenService(scm, "ccrunSrv", SERVICE_START);
if(sHandle!=NULL)
{
StartService(sHandle, 0, NULL);
CloseServiceHandle(sHandle);
}
//---------------------------------------------------------------------------
// 关闭服务管理器
CloseServiceHandle(scm);