1、windows NT 与 Windows 9x 有一个非常重要的区别,即 Windows NT 提供了很多功能强大的 Service(服务 )。这些 Service 可以随着 NT 的启动而自启动,也可以让用户通过控制面板启动,还可以被 Win32 应用程序起停。甚至在没有用户登录系统的情况下,这些Service 也能执行。许多 FTP、 WWW服务器和数据库就是以 Service 的形式存在于 NT 上,从而实现了无人值守。就连最新版的“黑客”程序 Back Orifice 2000 也是以 Service形式在 NT 上藏身的。由于 Service 的编程较复杂,许多开发者想开发自己的 S
2、ervice 但往往都望而却步。鉴于此,下面我们就从头到尾来构造一个全新的 Service,读者只要在程序中注明的地方加上自己的代码,那么就可以轻松拥有一个自己的 Service。在编写 Service之前,先介绍一下几个重要的函数: - 1. SC_HANDLE OpenSCManager( LPCTSTR lpMachineName, LPCTSTR lpDatabaseName, DWORD dwDesiredAccess) - OpenSCManager 函数打开指定计算机上的 service control manager database。其中参数 lpMachineName 指定
3、计算机名,若为空则指定为本机。 LpDatabaseName为指定要打开的 service control manager database 名 , 默认为空。dwDesiredAccess 指定操作的权限 , 可以为下面取值之一: - SC_MANAGER_ALL_ACCESS /所有权限 - SC_MANAGER_CONNECT /允许连接到 service control manager database - SC_MANAGER_CREATE_SERVICE /允许创建服务对象并把它加入 database - SC_MANAGER_ENUMERATE_SERVICE /允许枚举 dat
4、abase 中的 Service - SC_MANAGER_LOCK /允许锁住 database - SC_MANAGER_QUERY_LOCK_STATUS /允许查询 database 的封锁信息 - 函数执行成功则返回一个指向 service control manager database 的句柄,失败则返回 NULL。注意: WINNT通过一个名为 service control manager database的数据库来管理所有的 Service,因此对 Service 的任何操作都应打开此数据库。 - 2. SC_HANDLE CreateService(SC_HANDLE h
5、SCManager, LPCTSTR lpServiceName, LPCTSTR lpDisplayName, DWORD dwDesiredAccess, DWORD dwServiceType, DWORD dwStartType, DWORD dwErrorControl, LPCTSTR lpBinaryPathName, LPCTSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCTSTR lpDependencies, LPCTSTR lpServiceStartName, LPCTSTR lpPassword) - CreatService
6、函数产生一个新的 SERVICE。其中参数 hSCManager 为指向 service control manager database 的句柄,由 OpenSCManager 返回。 LpServiceName 为SERVICE 的名字, lpDisplayName 为 Service 显示用名, dwDesiredAccess 是访问权限,本程序中用 SERVICE_ALL_ACCESS 。 wServiceType,指明 SERVICE 类型,本程序中用SERVICE_WIN32_OWN_PROCESS| SERVICE_INTERACTIVE_PROCESS。 dwStartType
7、 为 Service启动方式,本程序采用自启动,即 dwStartType 等于 SERVICE_AUTO_START 。 dwErrorControl 说明当 Service 在启动中出错时采取什么动作,本程序采用SERVICE_ERROR_IGNORE 即忽约错误,读者可以改为其他的。 LpBinaryPathName 指明 Service本体程序的路径名。剩下的五个参数一般可设为 NULL。如函数调用成功则返回这个新Service 的句柄,失败则返回 NULL。与 此函数对应的是 DeleteService( hService),它删除指定的 Service。 - 3. SC_HANDL
8、E OpenService(SC_HANDLE hSCManager,LPCTSTR lpServiceName, DWORD dwDesiredAccess ) - OpenService 函数打开指定的 Service。其中参数 hSCManager 为指向 service control manager database 的句柄,由 OpenSCManager 返回。 LpServiceName 为Service的名字, dwDesiredAccess是访问权限,其可选值比较多,读者可以参看 SDK Help. 函数调用成功则返回打开的 Service 句柄,失败则返回 NULL。 -
9、4. BOOL StartService( SC_HANDLE hService, DWORD dwNumServiceArgs,LPCTSTR *lpServiceArgVectors ) - StartService 函数启动指定的 Service。其中参数 hService 为指向 Service 的句柄,由 OpenService 返回。 dwNumServiceAr 为启动服务所需的参数的个数。lpszServiceArgs 为 启 动 服务所需的参数。函数执行成功则返回 True, 失败则返回 False。 - 5. BOOL ControlService(SC_HANDLE hS
10、ervice DWORD dwControl,LPSERVICE_STATUS lpServiceStatus ) - Service 程序没有专门的停止函数,而是用 ControlService 函数来控制 Service的暂停、继续、停止等操作。参数 dwControl 指定发出的控制命令,可以为以下几个值: SERVICE_CONTROL_STOP /停止 Service SERVICE_CONTROL_PAUSE /暂停 Service SERVICE_CONTROL_CONTINUE /继续 Service SERVICE_CONTROL_INTERROGATE /查询 Servic
11、e 的状态 SERVICE_CONTROL_SHUTDOWN /让 ControlService 调用失效 - 参数 lpServiceStatus 是一个指向 SERVICE_STATUS 的指针。 SERVICE_STATUS 是一个比较重要的结构,它包含了 Service 的各种信息,如当前状态、可接受何种控制命令等等。 - 6. BOOL QueryServiceStatus( SC_HANDLE hService,LPSERVICE_STATUS lpServiceStatus ) - QueryServiceStatus 函数比较简单,它查询并返回当前 Service 的状态。 -
12、 编制一个 Service一般需要两个程序,一个是 Service本体,一个是用于对 Service进行控制的控制程序。通常 Service 本体是一个 console程序,而控制程序则是一个普通的Win32 应用程序(当然,用户不用控制程序而通过控制面板也可对 Service 进行启、停,但不能进行添加、删除操作。) - 首先,我们来编写 Service 本体。对于 Service本体来说,它一般又由以下三部分组成: main()、 ServiceMain()、 Handler(),下面是 main()的源代码:(注:由于篇幅的关系,大部分程序都没进行错误处理,读者可以自己添上) int m
13、ain(int argc, char *argv) SERVICE_TABLE_ENTRY ste2; /一个 Service进程可以有多个线程,这是每个 /线程的入口表 ste0.lpServiceName= “W.Z.SERVICE “; /线程名字 ste0.lpServiceProc=ServiceMain; /线程入口地址 ste1.lpServiceName=NULL; /最后一个必须为 NULL ste1.lpServiceProc=NULL; StartServiceCtrlDispatcher(ste); return 0; - main()是 Service 的主线程。当
14、servie control manager 开始一个 Service进程时,它总是等待这个 Service 去调用 StartServiceCtrlDispatcher()函数。 main( )作为这个进程的主线程应该在程序开始后尽快调用 StartServiceCtrlDispatcher()。StartServiceCtrlDispatcher()在被调用后并不立即返回,它 把本 Service 的主线程连接到 service control manager,从而让 service control manager 通过这个连接发送开始、停止等控制命令给主线程。主线程在这时就扮演了一个命令
15、的转发器的角色,它或者调用 Handle( )去处理停止、继续等控制要求,或者产生一个新线程去执行ServiceMain。 StartServiceCtrlDispatcher()在整个 Service 结束时才返回。 - ServiceMain()是 Service 真正的入口 点,必须在 main()中进行了正确的定义。ServiceMain( )的两个参数是由 StartService()传递过来的。下面是 ServiceMain()的源代码: void WINAPI ServiceMain(DWORD dwArgc,LPTSTR *lpszArgv) ssh=RegisterServi
16、ceCtrlHandler ( “W.Z.SERVICE “,Handler); ss.dwServiceType=SERVICE_WIN32_OWN _PROCESS|SERVICE_INTERACTIVE_PROCESS; ss.dwCurrentState=SERVICE_START_PENDING; /如用户程序的代码比较多 (执行时间超过 1秒),这儿要设成 SERVICE_ START_PENDING,待用户程序完成后再设为 SERVICE_RUNNING。 ss.dwControlsAccepted=SERVICE_ACCEPT_ STOP;/表明 Service 目前能接受的命
17、令是停止命令。 ss.dwWin32ExitCode=NO_ERROR; ss.dwCheckPoint=0; ss.dwWaitHint=0; SetServiceStatus(ssh, /必须随时更新数据库中 Service 的状态。 Mycode(); /这儿可放入用户自己的代码 ss.dwServiceType=SERVICE_WIN32_OWN_ PROCESS|SERVICE_INTERACTIVE_PROCESS; ss.dwCurrentState=SERVICE_RUNNING; ss.dwControlsAccepted=SERVICE_ACCEPT_STOP; ss.dw
18、Win32ExitCode=NO_ERROR; ss.dwCheckPoint=0; ss.dwWaitHint=0; SetServiceStatus(ssh, Mycode();/ 这儿也可放入用户自己的代码 在 ServiceMain()中应该立即调用 RegisterServiceCtrlHandler()注册一个 Handler 去处理控制程序或控制面板对 Service 的控制要求。 Handler()被转发器调用去处理要求, 下面是 Handler()的源代码 : void WINAPI Handler(DWORD Opcode) switch(Opcode) case SERV
19、ICE_CONTROL_STOP: /停止 Service Mycode(); /这儿可放入用户自己的相关代码 ss.dwWin32ExitCode = 0; ss.dwCurrentState =SERVICE_STOPPED; /把 Service 的当前状态置为 STOP ss.dwCheckPoint = 0; ss.dwWaitHint = 0; SetServiceStatus (ssh, /必须随时更新数据库中 Service 的状态 break; case SERVICE_CONTROL_INTERROGATE: SetServiceStatus (ssh, /必须随时更新数据
20、库中 Service 的状态 break; - 好了, Service 本体程序已基本完成,我们接着来看一下 Service 的 控制程序: - 控制程序是一个标准的 window 程序,上面主要有四个按纽: Create Service、Delete Service、 start、 stop,分别用来产生、删除、开始和停止 Service。下面是它们的部分源代码: 1. 产生 Service void _fastcall TForm1:CreateBtnClick (TObject *Sender) scm=OpenSCManager(NULL,NULL, SC_MANAGER_CREATE
21、_SERVICE); if (scm!=NULL) svc=CreateService(scm, “W.Z.SERVICE “, “W.Z.SERVICE “,/Service 名字 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS |SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START, /以自动方式开始 SERVICE_ERROR_IGNORE, “C:ntservice.exe “, /Service 本体程序路径, 必须与具体位置相符 NULL,NULL,NULL,NULL,NULL); if (svc
22、!=NULL) CloseServiceHandle(svc); CloseServiceHandle(scm); 2. 删除 Service void _fastcall TForm1:DeleteBtnClick (TObject *Sender) scm=OpenSCManager(NULL,NULL, SC_MANAGER_CONNECT); if (scm!=NULL) svc=OpenService(scm, “W.Z.SERVICE “, SERVICE_ALL_ACCESS); if (svc!=NULL) QueryServiceStatus(svc, if (Service
23、Status.dwCurrentState= SERVICE_RUNNING)/删除前,先停止此 Service. ControlService(svc, SERVICE_CONTROL_STOP, DeleteService(svc); CloseServiceHandle(svc); /删除 Service 后,最好再调用 CloseServiceHandle /以便立即从数据库中移走此条目。 CloseServiceHandle(scm); 3. 开始 Service void _fastcall TForm1:StartBtnClick(TObject *Sender) scm=Ope
24、nSCManager(NULL,NULL,SC_MANAGER_CONNECT); if (scm!=NULL) svc=OpenService(scm, “W.Z.SERVICE “,SERVICE_START); if (svc!=NULL) StartService(svc,0,NULL);/开始 Service CloseServiceHandle(svc); CloseServiceHandle(scm); 4.停止 Service void _fastcall TForm1:StopBtnClick (TObject *Sender) scm=OpenSCManager(NULL,
25、NULL, SC_MANAGER_ALL_ACCESS); if (scm!=NULL) svc=OpenService(scm, “W.Z.SERVICE “, SERVICE_STOP|SERVICE_QUERY_STATUS); if (svc!=NULL) QueryServiceStatus(svc, if (ServiceStatus.dwCurrentState= SERVICE_RUNNING) ControlService(svc, SERVICE_CONTROL_STOP, CloseServiceHandle(svc); CloseServiceHandle(scm);