VC++建立Service服务应用程序

来源:互联网 发布:定义二维数组 编辑:程序博客网 时间:2024/05/16 15:56
services.msc


mstsc


VC++建立Service服务应用程序 


几乎所有的操作系统在启动的时候都会启动一些不需要与用户交互的进程,这些进程在Windows中就被称作服务。它通常用于实现客户/服务器模式中的服务器方,如我们常见的Web服务IIS,当操作系统在启动后它就自动被运行,不管是否有人登陆到系统只要系统开启它就能得到运行。
服务程序、服务控制程序(SCP,service control program)和服务控制管理器(SCM,service control manager)组成了Windows服务。我们可以通过服务控制程序操纵服务控制管理器来配置、启动、暂停、停止服务程序。其中服务程序和服务控制程序可以由我们自己来编写扩展,而服务控制管理器(\windows\system32\servics.exe)则是操作系统内置的一个部件。首先我们来了解一下SCM的工作情况,然后我们介绍服务程序的编写和服务控制时所涉及API的使用。


服务控制管理器 
SCM本身也是一个服务程序(\windows\system32\servics.exe),作为windows的后台服务运行的。Winlogon在系统引导的早期会将SCM启动起来。SCM的服务入口函数首先创建一个初始化为无信号的同步事件对象(SvcCtrlEvent_A3752DX);接下来,它开始建立一个内部服务数据库,这个数据库要按事先规定好的一个顺序列出所有服务组,并记录与服务相关的详细信息;当这个数据库建立完成时SCM就开始按顺序启动那些启动方式为自动的服务,如果有服务要动行于指定用户账户中时还要调用LSASS,如果服务启动失败则会被放入一个名为ScFailedDrivers的列表中。当这些工作都完成后,SCM将同步事件对象SvcCtrlEvent_A3752DX置为有信号状态;并做好系统停机的准备。




当系统要关机时会向Windows子系统进程Csrss发送一个消息,以便调用Csrss的停机例程。Csrss会对所有活动的进程循环通知系统正在停机。对于除SCM以外的每一个系统进程如果没有返回退出的响应Csrss 都会等待由HKEY_USER\.DEFAULT\Control Panel\Desktop\WaitToKillAppTimeout指定的毫秒数(我的系统中是20000,也就是20秒),然后知通下一个进程结束。当遇到SCM时也会通知SCM进程系统正在停机,但不同的是会使用一个专用的超时间隔值(SCM在系统初始化时要向Csrss登记,于是Csrss就将SCM的进程ID保存了下来Csrss也就是通过个ID来识别SCM的)。这个专用的超时间隔位于HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\WaitToKillServiceTimeout中,有趣的是默认值也是20秒。这在段时间里SCM要通知所有在初始化时(服务程序自身初始化)请求SCM通知自己系统停机的服务,SCM有一个停机处理器负责这项工作。通知下达后SCM就等待服务退出,服务在接收到停机消息后会返回给SCM一个等待时间,SCM跟踪所有服务返回等待时间找出其中的最大值,当这个最大值达到后,如果有一个服务或多个服务又告诉SCM它们正在处理停机工作,那么SCM还会循环等待下去,但Csrss也再等待SCM当Csrss等待超时后,会继续后面的停机工作最终完成停机。
这就要求服务程序要在Csrss等待SCM的这段时内完成自己的停机处理工作,否则服务就没机会在系统停机前完成自己的关闭工作了。




Windows服务程序其实并不神秘,它只是遵循特定规则编写的一个程序。只要遵循这个特定的规则与服务控制管理器正确的交互,就可实现我们的服务程序。而我们只要能实现一个简单的服务程序,设计一个能处理复杂业务的服务也并非难事,因为从结构上看两者并没有太大的区别。只要遵循与SCM交互的规则,设计服务程序与设计普通的应用程序几乎没什么区别。




服务的安装与卸载


    服务程序编写完成并编译通过后,还要安装注册到操作系统中,这样它才会出现在管理工具->服务,那个管理器里面。API给我们提供了一个函数来实现我们注册服务的功能; 
SC_HANDLE CreateService( 
SC_HANDLE hSCManager, // 服务控制管理器的句柄   
LPCTSTR lpServiceName, // 指向服务的内部名称
LPCTSTR lpServiceName,, // 指向服务的显示名称 
DWORD dwDesiredAccess, // 服务的访问类型
DWORD dwServiceType,   // 服务的类型 
DWORD dwStartType,     // 服务的启动方式(自动,手动,禁用) 
DWORD dwErrorControl, // 错误控制方式 
LPCTSTR lpBinaryPathName, // 服务程序的路径 
LPCTSTR lpLoadOrderGroup, // 服务组的名称   
LPDWORD lpdwTagId,     // 服务的标签号 
LPCTSTR lpDependencies, // 服务依赖的服务或组名 
LPCTSTR lpServiceStartName, // 服务的启动帐户 
LPCTSTR lpPassword       // 服务启动帐户的密码); 






OpenSCManager 


SC_HANDLE OpenSCManager( 
LPCTSTR lpMachineName, 
LPCTSTR lpDatabaseName, DWORD dwDesiredAccess 
); 


Parameters
lpMachineName 
[in] Pointer to a null-terminated string that specifies the name of the target computer. If the pointer is NULL or points to an empty string, the function connects to the service control manager on the local computer. 
lpDatabaseName 
[in] Pointer to a null-terminated string that specifies the name of the service control manager database to open. This parameter should be set to SERVICES_ACTIVE_DATABASE. If it is NULL, the SERVICES_ACTIVE_DATABASE database is opened by default. 
dwDesiredAccess 
[in] Access to the service control manager. For a list of access rights, see Service Security and Access Rights. Before granting the requested access rights, the system checks the access token of the calling process against the discretionary access-control list of the security descriptor associated with the service control manager.
The SC_MANAGER_CONNECT access right is implicitly specified by calling this function.




安装一个服务
#include <windows.h>
#include <stdio.h>


SC_HANDLE schSCManager;


// Open a handle to the SC Manager database. 
 
schSCManager = OpenSCManager( 
    NULL,                    // local machine 
    NULL,                    // ServicesActive database 
    SC_MANAGER_ALL_ACCESS);  // full access rights 
 
if (NULL == schSCManager) 
    printf("OpenSCManager failed (%d)\n", GetLastError());


CreateService


SC_HANDLE CreateService( 
SC_HANDLE hSCManager, 
LPCTSTR lpServiceName, 
LPCTSTR lpDisplayName, 
DWORD dwDesiredAccess, 
DWORD dwServiceType, 
DWORD dwStartType, 
DWORD dwErrorControl, 
LPCTSTR lpBinaryPathName, 
LPCTSTR lpLoadOrderGroup, 
LPDWORD lpdwTagId, 
LPCTSTR lpDependencies, 
LPCTSTR lpServiceStartName, 
LPCTSTR lpPassword ); 
TCHAR szPath[MAX_PATH]; 
    if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )
    {
        printf("GetModuleFileName failed (%d)\n", GetLastError()); 
        return FALSE;
    }