设为首页 收藏本站
查看: 525|回复: 0

[经验分享] windows后台服务程序编写

[复制链接]

尚未签到

发表于 2017-6-28 14:29:32 | 显示全部楼层 |阅读模式
  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
  https://code.csdn.net/assets/CODE_ico.pnghttps://code.csdn.net/assets/ico_fork.svg

  • #include <windows.h>  
  • #include <tchar.h>  
  • #include <strsafe.h>  
  • #include "sample.h"  

  • #pragma comment(lib, "advapi32.lib")  

  • #define SVCNAME TEXT("SvcName")  

  • SERVICE_STATUS          gSvcStatus;
  • SERVICE_STATUS_HANDLE   gSvcStatusHandle;   
  • HANDLE                  ghSvcStopEvent = NULL;  

  • VOID SvcInstall(void);  
  • VOID WINAPI SvcCtrlHandler( DWORD );   
  • VOID WINAPI SvcMain( DWORD, LPTSTR * );   

  • VOID ReportSvcStatus( DWORD, DWORD, DWORD );  
  • VOID SvcInit( DWORD, LPTSTR * );   
  • VOID SvcReportEvent( LPTSTR );  


  • //  
  • // Purpose:   
  • //   Entry point for the process  
  • //  
  • // Parameters:  
  • //   None  
  • //   
  • // Return value:  
  • //   None  
  • //  
  • void __cdecl _tmain(int argc, TCHAR *argv[])   
  • {
  •     // If command-line parameter is "install", install the service.   
  •     // Otherwise, the service is probably being started by the SCM.  

  •     if( lstrcmpi( argv[1], TEXT("install")) == 0 )  
  •     {
  •         SvcInstall();
  •         return;  
  •     }

  •     // TO_DO: Add any additional services for the process to this table.  
  •     SERVICE_TABLE_ENTRY DispatchTable[] =
  •     {
  •         { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
  •         { NULL, NULL }
  •     };

  •     // This call returns when the service has stopped.   
  •     // The process should simply terminate when the call returns.  

  •     if (!StartServiceCtrlDispatcher( DispatchTable ))   
  •     {
  •         SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));   
  •     }
  • }

  • //  
  • // Purpose:   
  • //   Installs a service in the SCM database  
  • //  
  • // Parameters:  
  • //   None  
  • //   
  • // Return value:  
  • //   None  
  • //  
  • VOID SvcInstall()  
  • {
  •     SC_HANDLE schSCManager;  
  •     SC_HANDLE schService;  
  •     TCHAR szPath[MAX_PATH];  

  •     if( !GetModuleFileName( "", szPath, MAX_PATH ) )  
  •     {
  •         printf("Cannot install service (%d)\n", GetLastError());  
  •         return;  
  •     }

  •     // Get a handle to the SCM database.   

  •     schSCManager = OpenSCManager(
  •         NULL,                    // local computer  
  •         NULL,                    // ServicesActive database   
  •         SC_MANAGER_ALL_ACCESS);  // full access rights   

  •     if (NULL == schSCManager)   
  •     {
  •         printf("OpenSCManager failed (%d)\n", GetLastError());  
  •         return;  
  •     }

  •     // Create the service  

  •     schService = CreateService(
  •         schSCManager,              // SCM database   
  •         SVCNAME,                   // name of service   
  •         SVCNAME,                   // service name to display   
  •         SERVICE_ALL_ACCESS,        // desired access   
  •         SERVICE_WIN32_OWN_PROCESS, // service type   
  •         SERVICE_DEMAND_START,      // start type   
  •         SERVICE_ERROR_NORMAL,      // error control type   
  •         szPath,                    // path to service's binary   
  •         NULL,                      // no load ordering group   
  •         NULL,                      // no tag identifier   
  •         NULL,                      // no dependencies   
  •         NULL,                      // LocalSystem account   
  •         NULL);                     // no password   

  •     if (schService == NULL)   
  •     {
  •         printf("CreateService failed (%d)\n", GetLastError());   
  •         CloseServiceHandle(schSCManager);
  •         return;  
  •     }
  •     else printf("Service installed successfully\n");   

  •     CloseServiceHandle(schService);
  •     CloseServiceHandle(schSCManager);
  • }

  • //  
  • // Purpose:   
  • //   Entry point for the service  
  • //  
  • // Parameters:  
  • //   dwArgc - Number of arguments in the lpszArgv array  
  • //   lpszArgv - Array of strings. The first string is the name of  
  • //     the service and subsequent strings are passed by the process  
  • //     that called the StartService function to start the service.  
  • //   
  • // Return value:  
  • //   None.  
  • //  
  • VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )  
  • {
  •     // Register the handler function for the service  

  •     gSvcStatusHandle = RegisterServiceCtrlHandler(
  •         SVCNAME,
  •         SvcCtrlHandler);

  •     if( !gSvcStatusHandle )  
  •     {
  •         SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));   
  •         return;   
  •     }

  •     // These SERVICE_STATUS members remain as set here  

  •     gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  •     gSvcStatus.dwServiceSpecificExitCode = 0;

  •     // Report initial status to the SCM  

  •     ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );

  •     // Perform service-specific initialization and work.  

  •     SvcInit( dwArgc, lpszArgv );
  • }

  • //  
  • // Purpose:   
  • //   The service code  
  • //  
  • // Parameters:  
  • //   dwArgc - Number of arguments in the lpszArgv array  
  • //   lpszArgv - Array of strings. The first string is the name of  
  • //     the service and subsequent strings are passed by the process  
  • //     that called the StartService function to start the service.  
  • //   
  • // Return value:  
  • //   None  
  • //  
  • VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)  
  • {
  •     // TO_DO: Declare and set any required variables.  
  •     //   Be sure to periodically call ReportSvcStatus() with   
  •     //   SERVICE_START_PENDING. If initialization fails, call  
  •     //   ReportSvcStatus with SERVICE_STOPPED.  

  •     // Create an event. The control handler function, SvcCtrlHandler,  
  •     // signals this event when it receives the stop control code.  

  •     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 );

  •     // TO_DO: Perform work until service stops.  

  •     while(1)  
  •     {
  •         // Check whether to stop the service.  

  •         WaitForSingleObject(ghSvcStopEvent, INFINITE);

  •         ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
  •         return;  
  •     }
  • }

  • //  
  • // Purpose:   
  • //   Sets the current service status and reports it to the SCM.  
  • //  
  • // Parameters:  
  • //   dwCurrentState - The current state (see SERVICE_STATUS)  
  • //   dwWin32ExitCode - The system error code  
  • //   dwWaitHint - Estimated time for pending operation,   
  • //     in milliseconds  
  • //   
  • // Return value:  
  • //   None  
  • //  
  • VOID ReportSvcStatus( DWORD dwCurrentState,  
  •                       DWORD dwWin32ExitCode,  
  •                       DWORD dwWaitHint)  
  • {
  •     static DWORD dwCheckPoint = 1;  

  •     // Fill in the SERVICE_STATUS structure.  

  •     gSvcStatus.dwCurrentState = dwCurrentState;
  •     gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
  •     gSvcStatus.dwWaitHint = dwWaitHint;

  •     if (dwCurrentState == SERVICE_START_PENDING)  
  •         gSvcStatus.dwControlsAccepted = 0;
  •     else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;  

  •     if ( (dwCurrentState == SERVICE_RUNNING) ||  
  •            (dwCurrentState == SERVICE_STOPPED) )
  •         gSvcStatus.dwCheckPoint = 0;
  •     else gSvcStatus.dwCheckPoint = dwCheckPoint++;  

  •     // Report the status of the service to the SCM.  
  •     SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
  • }

  • //  
  • // Purpose:   
  • //   Called by SCM whenever a control code is sent to the service  
  • //   using the ControlService function.  
  • //  
  • // Parameters:  
  • //   dwCtrl - control code  
  • //   
  • // Return value:  
  • //   None  
  • //  
  • VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )  
  • {
  •    // Handle the requested control code.   

  •    switch(dwCtrl)   
  •    {
  •       case SERVICE_CONTROL_STOP:   
  •          ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);

  •          // Signal the service to stop.  

  •          SetEvent(ghSvcStopEvent);
  •          ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);

  •          return;  

  •       case SERVICE_CONTROL_INTERROGATE:   
  •          break;   

  •       default:   
  •          break;  
  •    }

  • }

  • //  
  • // Purpose:   
  • //   Logs messages to the event log  
  • //  
  • // Parameters:  
  • //   szFunction - name of function that failed  
  • //   
  • // Return value:  
  • //   None  
  • //  
  • // Remarks:  
  • //   The service must have an entry in the Application event log.  
  • //  
  • VOID SvcReportEvent(LPTSTR szFunction)   
  • {
  •     HANDLE hEventSource;  
  •     LPCTSTR lpszStrings[2];  
  •     TCHAR Buffer[80];  

  •     hEventSource = RegisterEventSource(NULL, SVCNAME);

  •     if( NULL != hEventSource )  
  •     {
  •         StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError());  

  •         lpszStrings[0] = SVCNAME;
  •         lpszStrings[1] = Buffer;

  •         ReportEvent(hEventSource,        // event log handle  
  •                     EVENTLOG_ERROR_TYPE, // event type  
  •                     0,                   // event category  
  •                     SVC_ERROR,           // event identifier  
  •                     NULL,                // no security identifier  
  •                     2,                   // size of lpszStrings array  
  •                     0,                   // no binary data  
  •                     lpszStrings,         // array of strings  
  •                     NULL);               // no binary data  

  •         DeregisterEventSource(hEventSource);
  •     }
  • }

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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-389035-1-1.html 上篇帖子: 从零开始学 Java - Windows 下安装 Eclipse 下篇帖子: windows server 2008 增加远程桌面连接数
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表