当前位置:   article > 正文

windows后台服务程序编写_win10 创建一个后台服务

win10 创建一个后台服务

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完整例子代码:

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


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/69277
推荐阅读
相关标签
  

闽ICP备14008679号