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. Win服务程序编写以及安装一般步骤

    Win服务程序编写以及安装一般步骤 Windows服务的优点有:1. 能够自动运行.2. 不要求用户交互.3. 在后台运行.本文将介绍常见服务程序编写的一般步骤以及注意事项. 设计服务程序实例: 创建 ...

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

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

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

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

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

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

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

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

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

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

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

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

  8. 在windows后台调用webservice

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

  9. 二、Windows 下 ShellCode 编写初步

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

随机推荐

  1. js如何控制css伪元素内容(before,after)

    曾经遇到的问题,在对抗UC浏览器屏蔽需要把内容输出到css 伪元素中输出.有个疑问如何用js控制它.于是在segmentfault提问,如下是对问题的整理: 如何用js控制css伪类after 简单粗 ...

  2. Unix环境下PS1变量的设置

    我的ps1命令提示符: export PS1="\[\e[31;1m\]\u @ \[\e[34;1m\]\h \[\e[36;1m\]\w \[\e[33;1m\]\t $ \[\e[37 ...

  3. python针对于mysql的增删改查

    无论是BS还是CS得项目,没有数据库是不行的. 本文是对python对mysql的操作的总结.适合有一定基础的开发者,最好是按部就班学习的人阅读.因为我认为人生不能永远都是从零开始,那简直就是灾难. ...

  4. 自动生成XML空节点格式的差异

    我们用C#开发了上位机配置软件,用C开发了嵌入式软件,然后他们之间的参数交互靠XML文件来沟通. C#中添加一个空的节点有以下几种情况. 不给节点的InnerText赋值: <root> ...

  5. How Node.js Multiprocess Load Balancing Works

    As of version 0.6.0 of node, load multiple process load balancing is available for node. The concept ...

  6. USB Mass Storage协议分析

    目录 简介 指令数据和状态协议 CBW指令格式 CSWCommand Status Wrapper状态格式 SCSI命令集 Format Unit Inquiry MODE SELECT 简介 USB ...

  7. Android 绘图工具库AChartEngine

    From: http://www.oschina.net/p/achartengine AChartEngine是为android应用而设计的绘图工具库.目前该库的最新稳定版本是0.7,支持绘制以下类 ...

  8. php算法之快速排序

    /** * 快速排序 * 原理: * 快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists). * 最差时间复杂度 O(n*n) * ...

  9. 浅析WebGIS

    浅析WebGIS 摘要:随着网络的发展,利用Web公布信息越来越普及化.而地理信息系统(GIS)与网络的结合就产生了万维网地理信息系统(WebGIS),它引起了地理信息公布的新的变革,对实现GIS信息 ...

  10. iOS使用ffmpeg播放rstp实时监控视频数据流

    一.编译针对iOS平台的ffmpeg库(kxmovie) 最近有一个项目.须要播放各种格式的音频.视频以及网络摄像头实时监控的视频流数据,经过多种折腾之后,最后选择了kxmovie,kxmovie项目 ...