现在的位置: 首页 > 综合 > 正文

How to Create a Windows NT/ Windows 2000 Service

2012年09月25日 ⁄ 综合 ⁄ 共 7776字 ⁄ 字号 评论关闭

最近看了一篇关于在NT/2000下创建服务程序的文章,将其翻译如下,帮助自己理解,其中可能有些地方翻译的不合适,希望读到此篇文章的朋友帮忙指正!(原文post在最下面)


一个Windows的服务程序是一个特别设计用于与Windows NT/2000的SCM(服务控制管理器)进行通讯的EXE(可执行程序)。这个服务控制管理器(SCM)维护一个已安装服务和驱动服务的数据库,并且提供一个统一安全的控制方式。SCM是在计算机引导的时候开始运行,它可以作为一个远程过程调用(PRC)服务器。作为一个程序开发者,我们要编写简单的服务程序的时候,我们要实现的程序被分为以下四个部分。

1,Win32/控制台主程序。
2,一个称之为ServiceMain()的函数,作为主程序的服务,它是一个服务的进入点。
3,一个服务控制处理函数,完成与SCM的通信。
4,一个服务安装函数/卸载函数,用于注册EXE为一个系统服务。


首先,让我们看一些控制台应用程序的主程序(它也可以是WinMain(),GUI图形界面程序)


#include "Winsvc.h"   //Header file for Services.(服务程序用到的头文件)

main()
{
     SERVICE_TABLE_ENTRY Table[]={{"Service1",ServiceMain},{NULL,NULL}}; 
     StartServiceCtrlDispatcher(Table);
}


main()函数唯一做的事情就是填充SERVICE_TABLE_ENTRY结构这样一个数组。


typedef struct _SERVICE_TABLE_ENTRY {
    LPWSTR                      lpServiceName;
    LPSERVICE_MAIN_FUNCTIONW    lpServiceProc;
}SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;


数组[0][0]位置上的元素是服务的名字(任何你喜欢的字符串名字)。数组[0][1]位置上的元素服务主函数的名字,我开始的时候已经在一个列表里指定过了。它实际上是一个指向服务主函数的函数指针,函数指针的名字可以任意。现在,我开始进行第一步:使用SERVICE_TABLE_ENTRY数组调用StartServiceCtrlDispatcher()函数。注意函数声明的形式(这句不知怎么翻译)。数组[1][0]位置上的元素和[1][1]位置上的元素都是NULL,就是表明这是数组的结束。(不是必须这样),我们可以增加多个数组元素到这个列表里,如果我们有多个服务在一个EXE要同时运行的话。


ServiceMain()函数典型的声明方式:


void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)


现在让我们一起分析一下我们的ServiceMain()函数。
这个函数的几个主要步骤:

1,用合适的值填充SERVICE_STATUS结构体用于与SCM进行通信
2,注册服务控制处理函数,就像前面提到的
3,调用实际的处理函数


在继续进行之前,我们需要在这里声明两个全局变量:


SERVICE_STATUS m_ServiceStatus;
SERVICE_STATUS_HANDLE m_ServiceStatusHandle;


ServiceMain()函数可以接收命令行参数就像任何的C++的main()函数一样。第一个参数指明了将被传递给服务的参数个数,这里总是至少有一个参数(就是应用程序的名字)。第二个参数是指向字符串指针数组的指针,数组中第一个指针元素总是指向服务的名字。SERVICE_STATUS这个数据结构是用于填充当前服务的状态的,并且将它通报给SCM。我们可以使用API函数SetServiceStatus()完成上面的工作。SERVICE_STATUS的数据成员可以被设置为下面的值:


dwServiceType        = SERVICE_WIN32;   
dwCurrentState       = SERVICE_START_PENDING;  //Means Trying To Start(Initially)
dwControlsAccepted   = SERVICE_ACCEPT_STOP;  //Accepts Stop/Start only in Service control program. Usually in the control Panel (NT) / Administrative tools (2000). We can also set our service to accept PAUSE and CONTINUE functionality also.


在ServiceMain()开始的时候,我们应该将SERVICE_STATUS结构中的dwCurrentState设置为SERVICE_START_PENDING。这是为了通知SCM(我们的)服务正要开始运行。如果在使用中出现了任何错误,我们应该通过设置SERVICE_STOPPED通知SCM。默认情况下,SCM会从服务中寻找一个活动(activity在这里不知该怎么翻译比较恰当),在两分钟内如果这个活动没有显示任何进展,SCM将会把它kill掉。


API函数RegisterServiceCtrlHandler()是被用于设置服务控制处理函数为SCM的服务。这个函数早先有两个参数,一个服务名字(字符串)和一个指向服务控制处理函数的指针。服务控制处理函数需要事先声明。


一旦我们一直到达这里,我们现在可以设置dwCurrentState为SERVICE_RUNNING,用于通知服务已经开始执行了。下来的任务就是调用实际的处理过程。


现在让我们一起分析我们的服务控制处理函数

这个服务控制处理函数被用于完成SCM和(我们的)服务程序的通信,例如,一个用户在服务上的操作,像,开始、停止、暂停、或者继续等。它基本上包含了处理所有情况的交互语句。在此,我们将调用合适的步骤去清理和终止进程。这个函数接收一个操作代码,它的值可以为如下:SERVICE_CONTROL_PAUSE,SERVICE_CONTROL_CONTINUE,SERVICE_CONTROL_STOP,SERVICE_CONTROL_INTERROGATE etc。我们需要适当的写每一个步骤。


现在Service Installer/ Uninstaller

要安装一个服务,we need to make some entries in the system registry。Windows有一些API函数来做这些工作,而不是使用the registry functions。这些API函数是CreateService()和DeleteService()。对这两个函数我们需要拥有适当的权限打开SCM数据库。我比较喜欢SC_MANAGER_ALL_ACCESS。对于安装一个服务,首先应该通过OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS)打开SCM,然后使用适当的我们服务的可执行文件的path调用CreateService(),这里我们也必须给出我们服务的名字。我们需要这个名字,如果我们想删除一个特别的服务的时候。在删除一个服务的时候,我们首先需要通过服务的名字打开一个具体的服务,然后对它调用DeleteService()。这就是所有我们所需要的。更多细节看一下所给的代码。


Thank You

Anish C.V.



原文如下:


How to Create a Windows NT/ Windows 2000 Service


A Windows service is an EXE specially designed to communicate with the SCM (Service Control Manager) of Windows NT/2000. The Service Control Manager (SCM) maintains a database of installed services and driver services, and provides a unified and secure means of controlling them. SCM is started at system boot and it is a remote procedure call (RPC) server. As a developer to try a simple service, we can divide the program in to four parts.

1. Main program of  Win 32 / Console Application.
2. A so called ServiceMain(), main program of Service. Entry point of a service.
3. A Service Control Handler, a function to communicate with SCM.
4. A Service Installer/ Uninstaller, To register an EXE as a Service.


Firstly let us take a look at the Main program  of the Console application (It can also be a WinMain()).

#include "Winsvc.h"   //Header file for Services.

main()
{
 SERVICE_TABLE_ENTRY Table[]={{"Service1",ServiceMain},{NULL,NULL}}; 
 StartServiceCtrlDispatcher(Table);
}

 The only thing done by the main() is to fill a SERVICE_TABLE_ENTRY array. The position [0][0] contains the name of the Service (Any string you like). Position [0][1] contains the name of the Service Main function, I specified in the list earlier. It actually is a function pointer to the Service main function. The name can be any thing. Now we start the first step to a service by calling StartServiceCtrlDispatcher() with the SERVICE_TABLE_ENTRY array. Note that the function signature should be of the form. The [1][0] and [1][1] positions are NULL, just to say the end of the array. (Not a must). We can add more entries to the list if we have more than one service running from the same EXE.

The declaration of a typical ServiceMain()

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)

Now Let us analyze our ServiceMain function.

The main steps of this function are
 
1. Fill the SERVICE_STATUS structure with appropriate values to communicate with the SCM.
2. Register the Service Control Handler Function said earlier in the list.
3. Call the actual processing functions.


For proceeding we need two global variables here.

SERVICE_STATUS m_ServiceStatus;
SERVICE_STATUS_HANDLE m_ServiceStatusHandle;

The ServiceMain() can accept command line arguments just as any c++ main() function. The first parameter contains the number of arguments being passed to the service. There will always be at least one argument. The second parameter is a pointer to an array of string pointers. The first item in the array always points to the service name.  The SERVICE_STATUS data structure is used to fill the current state of the Service and notify it to the SCM. We use an API function SetServiceStatus() for the purpose.  The data members of SERVICE_STATUS to look for are


dwServiceType        = SERVICE_WIN32;   
dwCurrentState       = SERVICE_START_PENDING;  //Means Trying To Start(Initially)
dwControlsAccepted   = SERVICE_ACCEPT_STOP;  //Accepts Stop/Start only in Service control program. Usually in the control Panel (NT) / Administrative tools (2000). We can also set our service to accept PAUSE and CONTINUE functionality also.


In the beginning of the ServiceMain() we should set the dwCurrentState  of SERVICE_STATUS to SERVICE_START_PENDING. This signals the SCM that the service is starting. If any error occurs in the way we should notify the SCM by passing SERVICE_STOPPED. By default the SCM will look for an activity from the service and if it fails to show any progress with in 2 minutes SCM kills that service.


The API function RegisterServiceCtrlHandler() is used to set the Service Control Handler Function of the Service with the SCM. The function takes two parameters as earlier, one service name (String) and the pointer to the Service Control Handler Function. That function should with the signature.


Once we get till here we now set dwCurrentState  as SERVICE_RUNNING to notify that the service has started to function.  The next step is to call the actual processing steps.


Now Let us analyze our Service Control Handler function

The Service Control Handler function is used by the SCM to communicate to the Service program about a user action on the service like a start, stop, pause or continue. It basically contains a switch statement to deal with each case. Here we will call appropriate steps to clean up and terminate the process. This function receives an opcode which can have values like SERVICE_CONTROL_PAUSE,SERVICE_CONTROL_CONTINUE,SERVICE_CONTROL_STOP,SERVICE_CONTROL_INTERROGATE etc. We have to write appropriate steps on each.


Now Service Installer/ Uninstaller

For Installing a service we need to make some entries in the system registry. Windows has some API's to do these steps, instead of using the registry functions. They are CreateService() and DeleteService(). For both these functions we need to open the SCM database with appropriate rights. I prefer SC_MANAGER_ALL_ACCESS. For installing a service first open the SCM by OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS). Then invoke the CreateService() with appropriate Binary file path of our service. Here also we have to give the name of our service. We need this name if we want to delete a particular service. In deleting a service we need to open the specific service first by its name and then invoke the DeleteService() on it. That's all what we need. Take a look at the code given with it for more details.


Thank You

Anish C.V.


http://www.codeguru.com/Cpp/W-P/system/ntservices/article.php/c5701/

抱歉!评论已关闭.