How to Create a Windows NT/ Windows 2000 Service

來源:互聯網
上載者:User

最近看了一篇關於在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/

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.