windows后台服務程序編寫


Windows后台服務程序編寫

1. 為什么要編寫后台服務程序

工作中有一個程序需要寫成后台服務的形式,摸索了一下,跟大家分享。

在windows操作系統中后台進程被稱為 service。 服務是一種應用程序類型,它在后台運行,通常沒有交互界面。服務應用程序通常可以 在本地和通過網絡為用戶提供一些功能,是為其它進程服務的。通過將程序注冊為服務,可以使自己的程序隨系統啟動而最先運行,隨系統關閉而最后停止。也可以windows服務管理器手動控制服務的啟動、關閉。

 

2. 編寫后台服務程序步驟

Windows提供了一套后台服務程序編程接口,用戶在編寫后台服務程序時需要遵循一定的編程框架,否則服務程序不能正常運行。

服務程序通常編寫成控制台類型的應用程序,總的來說,一個遵守服務控制管理程序接口要求的程序 包含下面三個函數: 

1)服務程序主函數(main):調用系統函數 StartServiceCtrlDispatcher 連接程序主線程到服務控制管理程序。 

和其它進程一樣,Main函數是服務進程的入口函數,服務控制管理器(SCM)在啟動服務程序時,會從服務程序的main函數開始執行。在進入點函數里面要完成ServiceMain的初始化,准確點說是初始化一個SERVICE_TABLE_ENTRY結構數組,這個結構記錄了這個服務程序里面所包含的所有服務的名稱和服務的進入點函數。然后再調用接口StartServiceCtrlDispatcher 。

Main函數的函數框架如下:

int _tmain(int argc, _TCHAR* argv[])

{

//服務入口點函數表

SERVICE_TABLE_ENTRY dispatchTable[]=

{

{TEXT(SZSERVICENAME),(LPSERVICE_MAIN_FUNCTION)Service_Main},

{ NULL,NULL}

};

if((argc>1)&&((*argv[1]=='-')||(argv[1]=="/")))

{

/*

參數個數大於1是安裝或者刪除服務,該操作是由用戶來執行的

當然也可以講這一部分功能另寫一個程序來實現

*/

if(_stricmp("install",argv[1]+1)==0)

{

installService();

}

else if(_stricmp("remove",argv[1]+1)==0)

{

removeService();

}

else if(_stricmp("debug",argv[1]+1)==0)

{

bDebugServer=true;

debugService(argc,argv);

}

 

 

}

else

{   

/*

如果未能和上面的如何參數匹配,則可能是服務控制管理程序來啟動該程序。 立即調用StartServiceCtrlDispatcher 函數

*/

g_logout.Logout("%s\n", "enter StartServiceCtrlDispatcher...");

 

//通知服務管理器為每一個服務創建服務線程

if(!StartServiceCtrlDispatcher(dispatchTable))

g_logout.Logout("%s\n", "StartServiceCtrlDispatcher failed.");

else

g_logout.Logout("%s\n", "StartServiceCtrlDispatcher OK.");

}

 

return 0;

 

}

SCM啟動一個服務程序之后,它會等待該程序的主線程去調StartServiceCtrlDispatcher。如果那個函數在兩分鍾內沒有被調用,SCM將會認為這個服務有問題,並調用TerminateProcess去殺死這個進程。這就要求你的主線程要盡可能快的調用StartServiceCtrlDispatcher。

 

2)服務入口點函數(ServiceMain):執行服務初始化任務,同時執行多個服務的服務進程有多個服務入口函數。 

在服務入口函數里,必須立即注冊服務控制回調函數。然后調用函數SetServiceStatus 通知SCM 服務現在的狀態,否則SCM會認為服務啟動失敗。

ServiceMain函數框架如下:

void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)

{

//注冊服務控制處理函數

sshStatusHandle=RegisterServiceCtrlHandler(TEXT(SZSERVICENAME),Service_Ctrl);

//如果注冊失敗

if(!sshStatusHandle)

{

g_logout.Logout("%s\n", "RegisterServiceCtrlHandler failed...");

return;

}

 

//初始化 SERVICE_STATUS 結構中的成員

ssStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS; //可執行文件中只有一個單獨的服務

ssStatus.dwServiceSpecificExitCode=0;

ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; //允許用SCP去停止該服務

 

//更新服務狀態

if(ReportStatusToSCMgr(SERVICE_START_PENDING,//服務狀態,服務仍在初始化

NO_ERROR,              

3000))                  //等待時間

SvcInit( dwArgc, lpszArgv ); //服務初始化函數

else

g_logout.Logout("%s\n", "ReportStatusToSCMgr SERVICE_START_PENDING failed...");

 

 

}

 

服務初始化函數SvcInit:

該函數的寫法比較重要。在函數中創建一個等待事件,然后一直等待該事件。該線程在服務接到請求之前一直處於掛起狀態,直到接到服務停止消息。

VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)

{

    /*創建事件*/

    ghSvcStopEvent = CreateEvent(

                         NULL,    // default security attributes

                         TRUE,    // manual reset event

                         FALSE,   // not signaled

                         NULL);   // no name

 

    if ( ghSvcStopEvent == NULL)

    {

        ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );

        return;

    }

 

    // Report running status when initialization is complete.

 

    ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );

 

// 在這里執行服務線程的創建...

 

    while(1)

    {

        // 等待停止事件被觸發

 

        WaitForSingleObject(ghSvcStopEvent, INFINITE);

        ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );

        return;

    }

}

 

3)控制服務處理程序函數(Handler):在服務程序收到控制請求時由控制分發線程引用。(此處是Service_Ctrl)。 

void WINAPI Service_Ctrl(DWORD dwCtrlCode)

{

//處理控制請求碼

switch(dwCtrlCode)

{

//先更新服務狀態為 SERVICDE_STOP_PENDING,再停止服務。

case SERVICE_CONTROL_STOP:

ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);

ServiceStop();     //由具體的服務程序實現

/*ssStatus.dwCurrentState=SERVICE_STOPPED;*/

 

//其它控制請求...

 

default:

break;

}

}

 

3. 注意事項

1)安裝服務可以另寫一個程序,也可以並在服務程序當中,比較簡單,這里就不介紹了。

2)服務初始化函數SvcInit里創建等待事件比較重要,在服務接收到服務停止消息后,觸發該事件。

3)Service_Main在等待事件觸發后立即返回,服務進程就會退出了。

 

附msdn完整例子代碼:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. #include <windows.h>  
  2. #include <tchar.h>  
  3. #include <strsafe.h>  
  4. #include "sample.h"  
  5.   
  6. #pragma comment(lib, "advapi32.lib")  
  7.   
  8. #define SVCNAME TEXT("SvcName")  
  9.   
  10. SERVICE_STATUS          gSvcStatus;   
  11. SERVICE_STATUS_HANDLE   gSvcStatusHandle;   
  12. HANDLE                  ghSvcStopEvent = NULL;  
  13.   
  14. VOID SvcInstall(void);  
  15. VOID WINAPI SvcCtrlHandler( DWORD );   
  16. VOID WINAPI SvcMain( DWORD, LPTSTR * );   
  17.   
  18. VOID ReportSvcStatus( DWORD, DWORD, DWORD );  
  19. VOID SvcInit( DWORD, LPTSTR * );   
  20. VOID SvcReportEvent( LPTSTR );  
  21.   
  22.   
  23. //  
  24. // Purpose:   
  25. //   Entry point for the process  
  26. //  
  27. // Parameters:  
  28. //   None  
  29. //   
  30. // Return value:  
  31. //   None  
  32. //  
  33. void __cdecl _tmain(int argc, TCHAR *argv[])   
  34. {   
  35.     // If command-line parameter is "install", install the service.   
  36.     // Otherwise, the service is probably being started by the SCM.  
  37.   
  38.     if( lstrcmpi( argv[1], TEXT("install")) == 0 )  
  39.     {  
  40.         SvcInstall();  
  41.         return;  
  42.     }  
  43.   
  44.     // TO_DO: Add any additional services for the process to this table.  
  45.     SERVICE_TABLE_ENTRY DispatchTable[] =   
  46.     {   
  47.         { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },   
  48.         { NULL, NULL }   
  49.     };   
  50.    
  51.     // This call returns when the service has stopped.   
  52.     // The process should simply terminate when the call returns.  
  53.   
  54.     if (!StartServiceCtrlDispatcher( DispatchTable ))   
  55.     {   
  56.         SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));   
  57.     }   
  58. }   
  59.   
  60. //  
  61. // Purpose:   
  62. //   Installs a service in the SCM database  
  63. //  
  64. // Parameters:  
  65. //   None  
  66. //   
  67. // Return value:  
  68. //   None  
  69. //  
  70. VOID SvcInstall()  
  71. {  
  72.     SC_HANDLE schSCManager;  
  73.     SC_HANDLE schService;  
  74.     TCHAR szPath[MAX_PATH];  
  75.   
  76.     if( !GetModuleFileName( "", szPath, MAX_PATH ) )  
  77.     {  
  78.         printf("Cannot install service (%d)\n", GetLastError());  
  79.         return;  
  80.     }  
  81.   
  82.     // Get a handle to the SCM database.   
  83.    
  84.     schSCManager = OpenSCManager(   
  85.         NULL,                    // local computer  
  86.         NULL,                    // ServicesActive database   
  87.         SC_MANAGER_ALL_ACCESS);  // full access rights   
  88.    
  89.     if (NULL == schSCManager)   
  90.     {  
  91.         printf("OpenSCManager failed (%d)\n", GetLastError());  
  92.         return;  
  93.     }  
  94.   
  95.     // Create the service  
  96.   
  97.     schService = CreateService(   
  98.         schSCManager,              // SCM database   
  99.         SVCNAME,                   // name of service   
  100.         SVCNAME,                   // service name to display   
  101.         SERVICE_ALL_ACCESS,        // desired access   
  102.         SERVICE_WIN32_OWN_PROCESS, // service type   
  103.         SERVICE_DEMAND_START,      // start type   
  104.         SERVICE_ERROR_NORMAL,      // error control type   
  105.         szPath,                    // path to service's binary   
  106.         NULL,                      // no load ordering group   
  107.         NULL,                      // no tag identifier   
  108.         NULL,                      // no dependencies   
  109.         NULL,                      // LocalSystem account   
  110.         NULL);                     // no password   
  111.    
  112.     if (schService == NULL)   
  113.     {  
  114.         printf("CreateService failed (%d)\n", GetLastError());   
  115.         CloseServiceHandle(schSCManager);  
  116.         return;  
  117.     }  
  118.     else printf("Service installed successfully\n");   
  119.   
  120.     CloseServiceHandle(schService);   
  121.     CloseServiceHandle(schSCManager);  
  122. }  
  123.   
  124. //  
  125. // Purpose:   
  126. //   Entry point for the service  
  127. //  
  128. // Parameters:  
  129. //   dwArgc - Number of arguments in the lpszArgv array  
  130. //   lpszArgv - Array of strings. The first string is the name of  
  131. //     the service and subsequent strings are passed by the process  
  132. //     that called the StartService function to start the service.  
  133. //   
  134. // Return value:  
  135. //   None.  
  136. //  
  137. VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )  
  138. {  
  139.     // Register the handler function for the service  
  140.   
  141.     gSvcStatusHandle = RegisterServiceCtrlHandler(   
  142.         SVCNAME,   
  143.         SvcCtrlHandler);  
  144.   
  145.     if( !gSvcStatusHandle )  
  146.     {   
  147.         SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));   
  148.         return;   
  149.     }   
  150.   
  151.     // These SERVICE_STATUS members remain as set here  
  152.   
  153.     gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;   
  154.     gSvcStatus.dwServiceSpecificExitCode = 0;      
  155.   
  156.     // Report initial status to the SCM  
  157.   
  158.     ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );  
  159.   
  160.     // Perform service-specific initialization and work.  
  161.   
  162.     SvcInit( dwArgc, lpszArgv );  
  163. }  
  164.   
  165. //  
  166. // Purpose:   
  167. //   The service code  
  168. //  
  169. // Parameters:  
  170. //   dwArgc - Number of arguments in the lpszArgv array  
  171. //   lpszArgv - Array of strings. The first string is the name of  
  172. //     the service and subsequent strings are passed by the process  
  173. //     that called the StartService function to start the service.  
  174. //   
  175. // Return value:  
  176. //   None  
  177. //  
  178. VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)  
  179. {  
  180.     // TO_DO: Declare and set any required variables.  
  181.     //   Be sure to periodically call ReportSvcStatus() with   
  182.     //   SERVICE_START_PENDING. If initialization fails, call  
  183.     //   ReportSvcStatus with SERVICE_STOPPED.  
  184.   
  185.     // Create an event. The control handler function, SvcCtrlHandler,  
  186.     // signals this event when it receives the stop control code.  
  187.   
  188.     ghSvcStopEvent = CreateEvent(  
  189.                          NULL,    // default security attributes  
  190.                          TRUE,    // manual reset event  
  191.                          FALSE,   // not signaled  
  192.                          NULL);   // no name  
  193.   
  194.     if ( ghSvcStopEvent == NULL)  
  195.     {  
  196.         ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );  
  197.         return;  
  198.     }  
  199.   
  200.     // Report running status when initialization is complete.  
  201.   
  202.     ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );  
  203.   
  204.     // TO_DO: Perform work until service stops.  
  205.   
  206.     while(1)  
  207.     {  
  208.         // Check whether to stop the service.  
  209.   
  210.         WaitForSingleObject(ghSvcStopEvent, INFINITE);  
  211.   
  212.         ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );  
  213.         return;  
  214.     }  
  215. }  
  216.   
  217. //  
  218. // Purpose:   
  219. //   Sets the current service status and reports it to the SCM.  
  220. //  
  221. // Parameters:  
  222. //   dwCurrentState - The current state (see SERVICE_STATUS)  
  223. //   dwWin32ExitCode - The system error code  
  224. //   dwWaitHint - Estimated time for pending operation,   
  225. //     in milliseconds  
  226. //   
  227. // Return value:  
  228. //   None  
  229. //  
  230. VOID ReportSvcStatus( DWORD dwCurrentState,  
  231.                       DWORD dwWin32ExitCode,  
  232.                       DWORD dwWaitHint)  
  233. {  
  234.     static DWORD dwCheckPoint = 1;  
  235.   
  236.     // Fill in the SERVICE_STATUS structure.  
  237.   
  238.     gSvcStatus.dwCurrentState = dwCurrentState;  
  239.     gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;  
  240.     gSvcStatus.dwWaitHint = dwWaitHint;  
  241.   
  242.     if (dwCurrentState == SERVICE_START_PENDING)  
  243.         gSvcStatus.dwControlsAccepted = 0;  
  244.     else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;  
  245.   
  246.     if ( (dwCurrentState == SERVICE_RUNNING) ||  
  247.            (dwCurrentState == SERVICE_STOPPED) )  
  248.         gSvcStatus.dwCheckPoint = 0;  
  249.     else gSvcStatus.dwCheckPoint = dwCheckPoint++;  
  250.   
  251.     // Report the status of the service to the SCM.  
  252.     SetServiceStatus( gSvcStatusHandle, &gSvcStatus );  
  253. }  
  254.   
  255. //  
  256. // Purpose:   
  257. //   Called by SCM whenever a control code is sent to the service  
  258. //   using the ControlService function.  
  259. //  
  260. // Parameters:  
  261. //   dwCtrl - control code  
  262. //   
  263. // Return value:  
  264. //   None  
  265. //  
  266. VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )  
  267. {  
  268.    // Handle the requested control code.   
  269.   
  270.    switch(dwCtrl)   
  271.    {    
  272.       case SERVICE_CONTROL_STOP:   
  273.          ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);  
  274.   
  275.          // Signal the service to stop.  
  276.   
  277.          SetEvent(ghSvcStopEvent);  
  278.          ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);  
  279.            
  280.          return;  
  281.    
  282.       case SERVICE_CONTROL_INTERROGATE:   
  283.          break;   
  284.    
  285.       default:   
  286.          break;  
  287.    }   
  288.      
  289. }  
  290.   
  291. //  
  292. // Purpose:   
  293. //   Logs messages to the event log  
  294. //  
  295. // Parameters:  
  296. //   szFunction - name of function that failed  
  297. //   
  298. // Return value:  
  299. //   None  
  300. //  
  301. // Remarks:  
  302. //   The service must have an entry in the Application event log.  
  303. //  
  304. VOID SvcReportEvent(LPTSTR szFunction)   
  305. {   
  306.     HANDLE hEventSource;  
  307.     LPCTSTR lpszStrings[2];  
  308.     TCHAR Buffer[80];  
  309.   
  310.     hEventSource = RegisterEventSource(NULL, SVCNAME);  
  311.   
  312.     if( NULL != hEventSource )  
  313.     {  
  314.         StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError());  
  315.   
  316.         lpszStrings[0] = SVCNAME;  
  317.         lpszStrings[1] = Buffer;  
  318.   
  319.         ReportEvent(hEventSource,        // event log handle  
  320.                     EVENTLOG_ERROR_TYPE, // event type  
  321.                     0,                   // event category  
  322.                     SVC_ERROR,           // event identifier  
  323.                     NULL,                // no security identifier  
  324.                     2,                   // size of lpszStrings array  
  325.                     0,                   // no binary data  
  326.                     lpszStrings,         // array of strings  
  327.                     NULL);               // no binary data  
  328.   
  329.         DeregisterEventSource(hEventSource);  
  330.     }  
  331. }  

http://blog.csdn.net/chence19871/article/details/42169443


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM