Service Programs
- SERVICE_WIN32_OWN_PROCESS,一个单独的进程,承载一个服务。
- SERVICE_WIN32_SHARE_PROCESS ,一个单独的进程,承载多个服务。
SCM规定了一个Service Program必须包含3个接口:(仅限于普通服务,驱动另说)
- 服务入口。
- 服务的主函数。
- 服务控制的回调函数。
服务入口
当SCM启动service program后,它就等着service program调用StartServiceCtrlDispatcher函数,对于SERVICE_WIN32_OWN_PROCESS 的服务,需要立即调用StartServiceCtrlDispatcher函数。对于SERVICE_WIN32_SHARE_PROCESS 的服务,可能有一些总体的初始化之后才调用,但不能超过30秒,如果达不到这个30秒的要求,只能创建另外的线程来做初始化的工作,而主线程调用StartServiceCtrlDispatcher函数。
StartServiceCtrlDispatcher函数调用成功后,线程并不直接返回,而是等进程进入SERVICE_STOPPED 后才返回。
SCM通过命名管道给这个线程发送控制请求,而这个线程则扮演控制调度器的角色,控制调度器要做的事情是:
- 当服务启动时创建一个新的线程来调用合适的入口函数。
- 当有服务控制请求时去调用合适的回调函数。
服务的主函数
- 初始化全局变量。
- 立马调用RegisterServiceCtrlHandler函数去注册一个回调函数,用于处理一些服务的请求。
- 初始化其他变量,如果需要的时间很短,就直接在ServiceMain里搞定。如果时间很长,应该用SetServiceStatus函数设置服务状态为SERVICE_START_PENDING,以保证服务在准备好之前SCM不发送控制请求。
- 初始化完成后,用SetServiceStatus函数设置服务状态成:SERVICE_RUNNING 。
- 执行服务的具体任务,完成后返回调用者。
- 如果没有初始化和运行错误,如果清理工作时间很长,应该使用SetServiceStatus函数设置服务状态成SERVICE_STOP_PENDING ,完成后再设置成SERVICE_STOPPED 。
服务控制的回调函数
服务状态的切换
服务的初始状态是SERVICE_STOPPED,当SCM启动服务时,状态是SERVICE_START_PENDING,用于做初始化的工作。完成初始化后,状态是SERVICE_RUNNING ,这也表示服务启动成功,否则表示启动失败。
只有某些状态间的切换是有效的,如下图:
服务的状态决定了SCM怎么与服务进行交互,比如状态是SERVICE_STOP_PENDING的话,SCM就认为服务正在结束,而不转发任何控制请求。那么下一个状态只能是SERVICE_STOPPED ,因上它只能从SERVICE_STOP_PENDING转换才有效。
下图是状态转换的更多细节。只有当服务指定了accept的话,SCM才发送控制请求。所以某个服务可能不会收到下图中所有的请求。
服务中的多线程
如果在所有线程退出之前就将服务已经停止的状态汇报给SCM的话,可能导致服务不能关闭或直接重启。
服务和注册表
服务不能访问 HKEY_CURRENT_USER 和 HKEY_CLASSES_ROOT,如果强行访问会失败。