我的理解:服务启动过程可以通过几个有阶段性的函数来进行了解。
一 StartServiceCtrlDispatcher函数
当SCM启动一个服务进程时,该进程立即调用StartServiceCtrlDispatcher函数。该函数接收一个入口点列表,每个入口点列表对应于该进程中的一个服务。每个入口点是由它所对应的服务的名称来标识的。例如下面的代码:
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
{ NULL, NULL }
};
StartServiceCtrlDispatcher( DispatchTable );
SERVICE_TABLE_ENTRY的定义如下:
typedef struct _SERVICE_TABLE_ENTRY {
LPTSTR lpServiceName;//服务的名称
LPSERVICE_MAIN_FUNCTION lpServiceProc;//该服务对应的服务入口点
} SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;
DispatchTable即是这里所说的入口点列表。现在我还不知道当有两个或者两个以上的服务时,StartServiceCtrlDispatcher函数是如何确定本次该执行哪个服务的。应该是通过服务的名字,只是我没有看到有传一个服务名字给StartServiceCtrlDispatcher函数。如果您知道,恳请指点一下,非常感谢。
StartServiceCtrlDispatcher创建了一个命名管道来跟SCM进行通信,在建立了该通信管道以后,它进入一个循环,等待SCM通过该管道发送过来的命令。每次SCM启动一个属于该进程的服务时,它发送一个“服务启动”命令。StartServiceCtrlDispatcher一直在等待来自SCM的命令,只有当该进程的所有服务都停止时它才会将控制返回至该进程的main函数,以便服务进程在退出以前做一些资源清理工作。
二 ServiceMain函数
ServiceMain函数就是上面提到的服务入口点,即SERVICE_TABLE_ENTRY结构体中第二成员所保存的对象。上面提到的“StartServiceCtrlDispatcher函数接收一个入口点列表”,这个入口点列表中每一个表项都保存有一个ServiceMain函数对应的指针(这样说应该不准确,反正类似于回调函数),调用该地址即调用ServiceMain函数。这样ServiceMain函数就被调用了。
ServiceMain函数的第一个动作时调用RegisterServiceCtrlHandler函数。此函数接收一个指向服务控制处理器函数的指针(MSDN中这样描述此指针指向的函数:
An application-defined callback function used with the RegisterServiceCtrlHandler function. A service program can use it as the control handler function of a particular service.)
这个服务控制处理器函数原型如下:
VOID WINAPI Handler(
[in] DWORD fdwControl
);
一个服务实现Handler函数以便处理各种来自SCM的命令。RegisterServiceCtrlHandler函数并不与SCM通信,但它为StartServiceCtrlDispatcher函数将此函数存储在本地进程内存中。服务入口点继续初始化该服务,包括分配内存、创建通信端点,以及从注册表中读入私有配置数据。大多数服务都遵从一个惯例:将服务的参数保存在其服务注册表键下的名为Parameters的子键下面。在入口点初始化该服务的过程中,它可能会利用SetServiceStatus函数来定期地给SCM发送状态消息,以便告诉SCM该服务的启动过程正在如何进行。当入口点完成了初始化以后,服务线程通常进入一个循环,等待来自客户应用的请求。例如,Web服务器会初始化一个TCP监听套接字,等待进来的HTTP连接请求。