zoukankan      html  css  js  c++  java
  • 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 ", "enter StartServiceCtrlDispatcher...");

    //通知服务管理器为每一个服务创建服务线程

    if(!StartServiceCtrlDispatcher(dispatchTable))

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

    else

    g_logout.Logout("%s ", "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 ", "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 ", "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) ", 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) ", 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) ", GetLastError());   
    115.         CloseServiceHandle(schSCManager);  
    116.         return;  
    117.     }  
    118.     else printf("Service installed successfully ");   
    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

  • 相关阅读:
    PDO事务处理不能保持一致性
    Android开发中的SQLite事务处理
    Mysql安装
    IIS下https配置及安全整改
    exchang2010OWA主界面添加修改密码选项
    查阅文件技巧
    RHEL yum
    CentOS之——CentOS7安装iptables防火墙
    Linux修改主机名称
    Vmware虚拟机设置静态IP地址
  • 原文地址:https://www.cnblogs.com/findumars/p/5636491.html
Copyright © 2011-2022 走看看