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. }

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

windows后台服务程序编写的更多相关文章

  1. C#Windows Service服务程序的安装/卸载、启动/停止 桌面客户端管理程序设计

    C#Windows Service服务程序的安装/卸载.启动/停止 桌面客户端管理程序设计 关于Windows Service程序的安装与卸载如果每次使用命令行操作,那简直要奔溃了,太麻烦而且还容易出 ...

  2. windows服务的编写,手动安装与卸载

    windows服务的编写 1.要添加的引用 using System.ServiceProcess; using System.ServiceModel ; using WcfServiceLibra ...

  3. windows下服务程序相关(别人提供的5种封装使用)

    作者: daodaoliang 版本: V 0.0.1 日期: 2017年11月25日 1. Windows Service 编程实现 在windows平台下面编写 服务程序 免不了要去查看微软的开发 ...

  4. 使用Python写Windows Service服务程序

    1.背景 如果你想用Python开发Windows程序,并让其开机启动等,就必须写成windows的服务程序Windows Service,用Python来做这个事情必须要借助第三方模块pywin32 ...

  5. DTCMS插件的制作实例电子资源管理(二)Admin后台页面编写

    总目录 插件目录结构(一) Admin后台页面编写(二) 前台模板页编写(三) URL重写(四) 本实例旨在以一个实际的项目中的例子来介绍如何在dtcms中制作插件,本系列文章非入门教程,部分逻辑实现 ...

  6. 【JavaService】部署Java jar为Windows后台服务

    将Java jar文件部署为Windows后台服务有多种方法:Service Installer.Java service Wrapper.JavaService.exe等等.这里介绍下使用JavaS ...

  7. 在windows后台调用webservice

    1.首先要创建个webservice,然后再webservice写一个方法如图 2.然后将WebService1.asmx 在浏览器中浏览会出现如图所示(该地址很重要,复制此地址在下边程序中要用到) ...

  8. 二、Windows 下 ShellCode 编写初步

    第二章.Windows 下 ShellCode 编写初步 (一)shellcode 定义:最先的 Shell 指的是人机交互界面,ShellCode 是一组能完成我们想要的功能的机器代码,通常以十六进 ...

  9. java程序在windows后台执行的办法

    1.新建run.txt文件 2.在文件中输入一下内容: @echo off start javaw -jar xx.jar exit 3.保存,修改文件名为run.bat4.双击即可 5.删除wind ...

随机推荐

  1. urllib编码

    python: 1.unquote 2.decode 3.encode

  2. sqlAlchemy 按DateTime字段的年或月进行group_by查询

    一.根据”create_date“查询每天的数据 1.查询2016年5月每天的数据 session.query(extract('day', User.create_date).label('day' ...

  3. smartjs - DataManager API

    dataServices 数据服务的管理器:首先看下具体的代码 //数据服务 dataServices = st.factory({ name: "dataServices", p ...

  4. 实时阴影渲染(一):PSSM平行分割阴影图

    PSSM(Parallel Split Shadow Map)平行分割阴影图,是一种根据距离远近采用多个深度纹理渲染阴影的方法 适合用于室外大场景中的平行光比如太阳形成的阴影 本系列需要读者了解基本的 ...

  5. BOX2D测试

    ; ; Box2DTestLayer = cc.Layer.extend({ world:null, //GLESDebugDraw *m_debugDraw; ctor:function () { ...

  6. 2014年互联网IT待遇(包括国内民企、外企、金融机构)

    一.民企 1. 百度 13k*14.6,special 14~17k*14.6 开发类 13K*14.6 (2014) 测试类.前端类 12K*14.6 (2014) 2. 腾讯 11.5k*16,s ...

  7. java、js的编码、解码

    如果在地址栏挂载参数,特别是包含中文,往往要进行编码,取值时再解码,以下是java和js中编码.解码的各自方法. java: @Test public void test3() throws Unsu ...

  8. DB2查询当前时间与指定时间的时间差(相隔的秒数)

    DB2查询当前时间与指定时间的时间差(相隔的秒数). 例子:“拍品表 auct_item”中有个“结束时间 end_date”的字段,求结束时间与当前时间的间隔秒数. select  (DAYS(a. ...

  9. 区间dp模型之括号匹配打印路径 poj(1141)

    题目链接:Brackets Sequence 题目描写叙述:给出一串由'(')'' [ ' ' ] '组成的串,让你输出加入最少括号之后使得括号匹配的串. 分析:是区间dp的经典模型括号匹配.解说:h ...

  10. Android IPC机制基础

    概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...