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\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

  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

时间: 2024-10-26 23:48:57

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

windows下用C语言实现服务程序编写与安装

说是每学点知识就记录下,前段时间也没弄,还是自己懒惰了,今天逼着自己写写,当是在复习下,废话少说,进入正题. windows的服务程序 需要SERVICE_TABLE_ENTRY这个结构体(描述服务名和对应启动的函数eg:ServiceMain),然后StartServiceCtrlDispatcher来调用前面的结构体,在ServiceMain函数里要注册服务状态控制的方法,不然就不能控制服务的初始化.启动等,这个是基本的服务程序 主要代码如下(只有服务程序,下面会有解说安装.卸载等): 1

zz: C++后台服务程序开发模式

一直感觉 VC++ 太复杂了,但昨天看了汪蒲阳编著的因特网应用编程,其中写到后台服务程序的编写,论述的非常详细,而且逻辑清晰,看了之后感觉明白不少,故拿来与需要之人共享,并更正了原程序的一些错误,补充了一些材料.另外还有一种用 C++ 编写后台服务程序的思路(不算 .NET 上服务程序开发模型) , 以后整理好了再发上来.     在2000/XP等基于NT 的操作系统中,有一个服务管理器,它管理的后台进程被称为 service. 服务是一种应用程序类型,它在后台运行,与 UNIX 后台应用程序

Windows系统服务的编写。

实验资源下载地址:点击打开链接 由于微软在Vista之后,对系统服务进行了隔离,即服务层为会话0 层,,而登录用户为1,2....层,,因此,,如果需要服务层与用户进行交互,,只能绕过Session 0 层..因此己不能进行复杂的UI操作.. 服务是整合在Microsoft Windows操作系统中的结构.服务与用户的应用程序不同,因为你可以对他们进行配置,不需要一个激活的用户登录,就可以使这些服务在系统启动的时候运行,直到系统关闭.Windows中的服务,负责所有种类的后台活动,但不包括从远程

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

作者: daodaoliang 版本: V 0.0.1 日期: 2017年11月25日 1. Windows Service 编程实现 在windows平台下面编写 服务程序 免不了要去查看微软的开发者文档,相关的介绍在这里, 多了也就不在罗嗦了,如果你嫌弃直接用他们的API的话,可以使用别人已经封装好的,比如: 参考代码 连接 参考代码001 https://github.com/magicsih/WindowsService 参考代码002 https://github.com/Olster

IDEA打成jar包并在windows后台运行

IDEA打成jar包并在windows后台运行 一.IDEA打成jar包 1.File=>Project Structure=>Project 选择windows中安装的java版本,idea默认使用自己的java环境,需要改为windows中安装的版本,不然后期jar包在windows中运行时会java版本不对的错误. 2.File=>Project Structure=>Artifacts 接着选择main方法 选择完之后出现以下界面,点击"OK" 3.返回

Atitit.Java&#160;exe&#160;bat&#160;&#160;作为windows系统服务程序运行

Atitit.Java exe bat  作为windows系统服务程序运行 1. 使用SC命令+srvany.exe (不错,推荐)+net start1 1.1. First 创建一个java的运行bat1 1.2. 配置srvany 做serv wrapper1 1.3. 使用sc 创建/del服务1 1.4. 启动start/stop服务  Sc \\127.0.0.1 start ServicenameAti22 2. 1.修改注册表 主要有两种方法…… 还有一个好用的命令:sc de

Windows Shell Code编写中级拔高

标 题: Windows Shell Code编写中级拔高 时 间: 2013-05-28,23:59:54 一.基础知识&基本原理&一些思想方法 关于Windows Shell Code的文章论坛里的介绍也不少了,不过授之以鱼不如授之以渔,虽然有部分文章也做了大量讲解,但是几乎都是对照一份现成的代码来介绍为何那样写,为何这样写.所以如果Windows系统原理的知识相对薄弱的同学看起来还是云里雾里,也没弄懂,干脆把代码照抄走了,结果会遇到各种意想不到的异常,这篇文章叫中级拔高,而没有叫高级

python实现windows Service服务程序

python实现windows Service服务程序 win32serviceutil.ServiceFramework是封装得很好的Windows服务框架,本文通过继承它来实现. 通过SvcDoRun方法,实现服务启动,运行服务内的业务代码. 通过SvcStop方法,停止服务. WinPollManager.py代码如下: import win32serviceutil import win32service import win32event import winerror import

二、Windows 下 ShellCode 编写初步

第二章.Windows 下 ShellCode 编写初步 (一)shellcode 定义:最先的 Shell 指的是人机交互界面,ShellCode 是一组能完成我们想要的功能的机器代码,通常以十六进制数组的形式存在 NOTES:计算机每次都只是执行当前 EIP 指向的指令(单 CPU).在当前指令执行后,EIP 会自动加 1,从而指向下一条指令.如果有 JMP CALL RET 一类的指令,EIP 就会被强行改变成指定的地址,从而完成流程的跳转 (二)打开控制台窗口的 C 程序 升级版: 程序