一直感觉VC++太复杂了,但昨天看了汪蒲阳编著的因特网应用编程,其中写到后台服务程序的编写,论述的非常详细,而且逻辑清晰,看了之后感觉明白不少,故拿来与需要之人共享,并更正了原程序的一些错误,补充了一些材料。另外还有一种用C++编写后台服务程序的思路(不算.NET上服务程序开发模型),以后整理好了再发上来。 

在2000/XP等基于NT 的操作系统中,有一个服务管理器,它管理的后台进程被称为 service。服务是一种应用程序类型,它在后台运行,与 UNIX 后台应用程序类似。服务应用程序通常可以在本地和通过网络为用户提供一些功能,例如客户端/服务器应用程序、Web 服务器、数据库服务器以及其他基于服务器的应用程序。    后台服务 程序是在后台悄悄运行的。我们通过将自己的程序登记为服务,可以使自己的程序不出现在任务管理器中,并且随系统启动而最先运行,随系统关闭而最后停止。

     服务控制管理器是一个RPC 服务器,它显露了一组应用编程接口,程序员可以方便的编写程序来配置服务和控制远程服务器中服务程序。     服务程序通常编写成控制台类型的应用程序,总的来说,一个遵守服务控制管理程序接口要求的程序包含下面三个函数:1、服务程序主函数(main):调用系统函数 StartServiceCtrlDispatcher 连接程序主线程到服务控制管理程序。2、服务入口点函数(ServiceMain):执行服务初始化任务,同时执行多个服务的服务进程有多个服务入口函数。3、控制服务处理程序函数(Handler):在服务程序收到控制请求时由控制分发线程引用。(此处是Service_Ctrl)。 另外在系统运行此服务之前需要安装登记服务程序:installService 函数。删除服务程序则需要先删除服务安装登记:removeService 函数。

服务类型:类型
说明
SERVICE_FILE_SYSTEM_DRIVER=2
文件系统驱动服务。
SERVICE_KERNEL_DRIVER=1
驱动服务。
SERVICE_WIN32_OWN_PROCESS=16
独占一个进程的服务。
SERVICE_WIN32_SHARE_PROCESS=32
与其他服务共享一个进程的服务。

新建WIN32控制台程序, 其源文件名为service.cpp 。我用的开发工具是VC++.NET。 1.服务程序主函数    服务控制管理程序启动服务程序后,等待服务程序主函数调用系统函StartServiceCtrlDispatcher。一个SERVICE_WIN32_OWN_PROCESS 类型的服务应该立即调用 StartServiceCtrlDispatcher 函数,可以在服务启动后让服务入口点函数完成初始化工作。对于 SERVICE_WIN32_OWN_PROCESS 类型的服务和程序中所有服务共同的初始化工作可以在主函数中完成,但不要超过30秒。否则必须建立另外的线程完成这些共同的初始化工作,从而保证服务程序主函数能及时地调用 StartServiceCtrlDispatcher 函数。
[cpp] view plaincopy
  1. //服务程序主函数。
  2. #include "stdafx.h"
  3. #include "Windows.h"
  4. #define SZAPPNAME      "serverSample"     //服务程序名
  5. #define SZSERVICENAME  "serviceSample"    //标识服务的内部名
  6. //内部变量
  7. bool                   bDebugServer=false;
  8. SERVICE_STATUS              ssStatus;
  9. SERVICE_STATUS_HANDLE  sshStatusHandle;
  10. DWORD                       dwErr=0;
  11. TCHAR                       szErr[256];
  12. //下面的函数由程序实现
  13. void  WINAPI  Service_Main(DWORD dwArgc, LPTSTR *lpszArgv);
  14. void  WINAPI  Service_Ctrl(DWORD dwCtrlCode);
  15. void installService();
  16. void removeService();
  17. void debugService(int argc,char** argv);
  18. bool ReportStatusToSCMgr(DWORD dwCurrentState,DWORD dwWin32ExitCode,DWORD dwWaitHint);
  19. void AddToMessageLog(LPTSTR lpszMsg);
  20. int _tmain(int argc, _TCHAR* argv[])
  21. {
  22. SERVICE_TABLE_ENTRY dispatchTable[]=
  23. {
  24. {TEXT(SZSERVICENAME),(LPSERVICE_MAIN_FUNCTION)Service_Main},
  25. { NULL,NULL}
  26. };
  27. if((argc>1)&&((*argv[1]=='-')||(argv[1]=="/")))
  28. {
  29. if(_stricmp("install",argv[1]+1)==0)
  30. {
  31. installService();
  32. }
  33. else if(_stricmp("remove",argv[1]+1)==0)
  34. {
  35. removeService();
  36. }
  37. else if(_stricmp("debug",argv[1]+1)==0)
  38. {
  39. bDebugServer=true;
  40. debugService(argc,argv);
  41. }
  42. else
  43. {        //如果未能和上面的如何参数匹配,则可能是服务控制管理程序来启动该程序。立即调用
  44. //StartServiceCtrlDispatcher 函数。
  45. printf("%s - install to install the service /n",SZAPPNAME);
  46. printf("%s - remove to remove the service /n",SZAPPNAME);
  47. printf("%s - debug to debug the service /n",SZAPPNAME);
  48. printf("/n StartServiceCtrlDispatcher being called./n");
  49. printf("This may take several seconds.Please wait./n");
  50. if(!StartServiceCtrlDispatcher(dispatchTable))
  51. AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
  52. else
  53. AddToMessageLog(TEXT("StartServiceCtrlDispatcher OK."));
  54. }
  55. exit(0);
  56. }
  57. return 0;
  58. }

 2.服务入口点函数

服务入口点函数 service_main 首先调用系统函数 RegisterServiceCtrlHandler 注册服务控制处理函数 service_ctrl,然后调用 ReportStatusToSCMgr 函数,它通过系统函数 SetServiceStatus 更新服务的状态,然后调用特定的服务初始化入口函数 ServiceStart 完成具体的初始化工作。 
[c-sharp] view plaincopy
  1. //服务入口点函数
  2. void ServiceStart(DWORD dwArgc,LPTSTR* lpszArgv);//具体服务的初始化入口函数
  3. void  WINAPI  Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
  4. {
  5. //注册服务控制处理函数
  6. sshStatusHandle=RegisterServiceCtrlHandler(TEXT(SZSERVICENAME),Service_Ctrl);
  7. //如果注册失败
  8. if(!sshStatusHandle)
  9. {
  10. goto cleanup;
  11. return;
  12. }
  13. //初始化 SERVICE_STATUS 结构中的成员
  14. ssStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS;
  15. ssStatus.dwServiceSpecificExitCode=0;
  16. //更新服务状态
  17. if(!ReportStatusToSCMgr(
  18. SERVICE_START_PENDING,//服务状态,The service is starting.
  19. NO_ERROR,            //退出码
  20. 3000))                   //等待时间
  21. goto cleanup;        //更新服务状态失败则转向 cleanup
  22. ServiceStart(dwArgc,lpszArgv);
  23. return;
  24. cleanup:
  25. //把服务状态更新为 SERVICE_STOPPED,并退出。
  26. if(sshStatusHandle)
  27. (void)ReportStatusToSCMgr(SERVICE_STOPPED,dwErr,0);
  28. }

3.控制处理程序函数
函数 Service_Ctrl 是服务的控制处理程序函数,由主函数线程的控制分发程序引用。在处理控制请求码时,应该在确定的时间间隔内更新服务状态检查点,避免发生服务不能响应的错误。
 
[c-sharp] view plaincopy
  1. //控制处理程序函数
  2. void WINAPI Service_Ctrl(DWORD dwCtrlCode)
  3. {
  4. //处理控制请求码
  5. switch(dwCtrlCode)
  6. {
  7. //先更新服务状态为 SERVICDE_STOP_PENDING,再停止服务。
  8. case SERVICE_CONTROL_STOP:
  9. ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);
  10. ServiceStop();     //由具体的服务程序实现
  11. return;
  12. //暂停服务
  13. case SERVICE_CONTROL_PAUSE:
  14. ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);
  15. ServicePause();    //由具体的服务程序实现
  16. ssStatus.dwCurrentState=SERVICE_PAUSED;
  17. return;
  18. //继续服务
  19. case SERVICE_CONTROL_CONTINUE:
  20. ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);
  21. ServiceContinue(); //由具体的服务程序实现
  22. ssStatus.dwCurrentState=SERVICE_RUNNING;
  23. return;
  24. //更新服务状态
  25. case SERVICE_CONTROL_INTERROGATE:
  26. break;
  27. //无效控制码
  28. default:
  29. break;
  30. }
  31. ReportStatusToSCMgr(ssStatus.dwCurrentState,NO_ERROR,0);
  32. }

除了系统定义的五种控制码外(还有一种是:SERVICE_CONTROL_SHUTDOWN),用户还可自定义控制码,其取值范围是128-255。用户可以通过控制面板中的服务项向特定服务程序的控制处理函数发送控制码,程序员可以调用系统函数 ControlService 直接向服务程序的控制处理函数发送控制码。其函数原型如下: 
[c-sharp] view plaincopy
  1. BOOL ControlService(
  2. SC_HANDLE hService,
  3. DWORD dwControl,
  4. LPSERVICE_STATUS lpServiceStatus
  5. );
hService :函数 OpenService or CreateService 返回的服务程序句柄。dwControl :控制码,不能是SERVICE_CONTROL_SHUTDOWN。lpServiceStatus:返回最后收到的服务状态信息。 4.安装服务程序

    每个已安装服务程序在 HKEY_LOCAL_MACHINE/SYSTE/CurrentControlSet/Services 下都有一个服务名的关键字,程序员可以调用系统函数 CreateService 安装服务程序,并指定服务类型,服务名等。这个函数创建一个服务对象,并将其增加到相关的服务控制管理器数据库中。下面是函数原型:
[cpp] view plaincopy
  1. SC_HANDLE CreateService(
  2. SC_HANDLE hSCManager, //服务控制管理程序维护的登记数据库的句柄,由系统函数OpenSCManager 返回
  3. LPCTSTR lpServiceName, //以NULL 结尾的服务名,用于创建登记数据库中的关键字
  4. LPCTSTR lpDisplayName, //以NULL 结尾的服务名,用于用户界面标识服务
  5. DWORD dwDesiredAccess, //指定服务返回类型
  6. DWORD dwServiceType, //指定服务类型
  7. DWORD dwStartType, //指定何时启动服务
  8. DWORD dwErrorControl, //指定服务启动失败的严重程度
  9. LPCTSTR lpBinaryPathName, //指定服务程序二进制文件的路径
  10. LPCTSTR lpLoadOrderGroup, //指定顺序装入的服务组名
  11. LPDWORD lpdwTagId, //忽略,NULL
  12. LPCTSTR lpDependencies, //指定启动该服务前必须先启动的服务或服务组
  13. LPCTSTR lpServiceStartName, //以NULL 结尾的字符串,指定服务帐号。如是NULL,则表示使用LocalSystem 帐号
  14. LPCTSTR lpPassword //以NULL 结尾的字符串,指定对应的口令。为NULL表示无口令。但使用LocalSystem时填NULL
  15. );

     对于一个已安装的服务程序,可以调用系统函数 OpenService 来获取服务程序的句柄下面是其函数原型:
[c-sharp] view plaincopy
  1. SC_HANDLE OpenService(
  2. SC_HANDLE hSCManager,
  3. LPCTSTR lpServiceName,
  4. DWORD dwDesiredAccess
  5. );

hSCManager :服务控制管理程序微服的登记数据库的句柄。由函数 OpenSCManager function 返回 这个句柄。 lpServiceName :将要打开的以NULL 结尾的服务程序的名字,和 CreateService  中的 lpServiceName 相对应。 dwDesiredAccess :指定服务的访问类型。服务响应请求时,首先检查访问类型。用CreateService 或OpenService 打开的服务程序句柄使用完毕后必须用CloseServiceHandle 关闭。OpenSCManager打开的服务管理数据库句柄也必须用它来关闭。
[cpp] view plaincopy
  1. //安装服务程序
  2. void installService()
  3. {
  4. SC_HANDLE schService;
  5. SC_HANDLE schSCManager;
  6. TCHAR szPath[512];
  7. //得到程序磁盘文件的路径
  8. if(GetModuleFileName(NULL,szPath,512)==0)
  9. {
  10. _tprintf(TEXT("Unable to install %s - %s /n"),
  11. TEXT(SZAPPNAME),
  12. GetLastError());//@1获取调用函数返回的最后错误码
  13. return;
  14. }
  15. //打开服务管理数据库
  16. schSCManager=OpenSCManager(
  17. NULL,    //本地计算机
  18. NULL,    //默认的数据库
  19. SC_MANAGER_ALL_ACCESS  //要求所有的访问权
  20. );
  21. if(schSCManager)
  22. {
  23. //登记服务程序
  24. schService=CreateService(
  25. schSCManager,                    //服务管理数据库句柄
  26. TEXT(SZSERVICENAME),             //服务名
  27. TEXT(SZAPPNAME),       //用于显示服务的标识
  28. SERVICE_ALL_ACCESS,              //响应所有的访问请求
  29. SERVICE_WIN32_OWN_PROCESS,       //服务类型
  30. SERVICE_DEMAND_START,            //启动类型
  31. SERVICE_ERROR_NORMAL,            //错误控制类型
  32. szPath,                              //服务程序磁盘文件的路径
  33. NULL,                                //服务不属于任何组
  34. NULL,                                //没有tag标识符
  35. NULL,              //启动服务所依赖的服务或服务组,这里仅仅是一个空字符串
  36. NULL,                                //LocalSystem 帐号
  37. NULL);
  38. if(schService)
  39. {
  40. _tprintf(TEXT("%s installed. /n"),TEXT(SZAPPNAME));
  41. CloseServiceHandle(schService);
  42. }
  43. else
  44. {
  45. _tprintf(TEXT("CreateService failed - %s /n"),GetLastError());
  46. }
  47. CloseServiceHandle(schSCManager);
  48. }
  49. else
  50. _tprintf(TEXT("OpenSCManager failed - %s /n"),GetLastError());
  51. }

主函数处理了三中命令行参数:- install,- remove,- debug,分别用于安装,删除和调试服务程序。如果不带参数运行,则认为是服务控制管理出现启动该服务程序。参数不正确则给出提示信息。

StartServiceCtrlDispatcher 函数负责把程序主线程连接到服务控制管理程序。具体描述如下:BOOL StartServiceCtrlDispatcher(  const LPSERVICE_TABLE_ENTRY lpServiceTable);lpServiceStartTable 指向 SERVICE_TABLE_ENTRY 结构类型的数组,他包含了调用进程所提供的每个服务的入口函数和字符串名。表中的最后一个元素必须为 NULL,指明入口表结束。SERVICE_TABLE_ENTRY 结构具体描述如下:

typedef struct _SERVICE_TABLE_ENTRY {  LPTSTR lpServiceName;  LPSERVICE_MAIN_FUNCTION lpServiceProc;} SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;

lpServiceName 是一个以 NULL 结尾的字符串,标识服务名。如果是 SERVICE_WIN32_OWN_PROCESS 类型的服务,这个字符串会被忽略。lpServiceProc 指向服务入口点函数。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/camforg2010/archive/2010/04/20/5505171.aspx

C++后台服务程序开发模式相关推荐

  1. 后台服务程序开发模式(一)

    后台服务程序开发模式(一) 一直感觉VC++太复杂了,但昨天看了汪蒲阳编著的因特网应用编程,其中写到后台服务程序的编写,论述的非常详细,而且逻辑清晰,看了之后感觉明白不少,故拿来与需要之人共享,并更正 ...

  2. C# Net6开发Linux守护进程(后台服务程序,类似Windows服务)案例

    C# Net6开发Linux守护进程(后台服务程序,类似Windows服务)案例 背景 C# net6开发Linux守护进程要点 背景 在使用net6开发Linux程序时,除了AspNet Core项 ...

  3. ngrok服务器搭建_C/C++ Linux 后台服务器开发高级架构师学习知识路线总结

    前言: 小编也是从事c方面10多年的工作经验.今天跟大家分享一下我总结出来的一系列 C/C Linux后台服务器开发的学习路线.从Linux开发工程师-Linux后台开发工程师-Linux高级互联网架 ...

  4. discuz 后台页面开发

    后台页面开发 创建项目 在 ./source/admincp/menu/ 目录下创建 menu_mynav.php 文件 在"管理中心-全局"中加一个项目,需要在 menu_myn ...

  5. 这可能是东半球最保姆级的后台服务器开发学习路线

    作者 | 编程指北 来源 | 编程指北(id :cs_dev) 前言 这一篇的主题是「Linux C/C++ 服务器/后台开发学习路线」. 这样的文章相信大家都见得不少了,写之前也非常忐忑,能不能和其 ...

  6. Entity Framework技术系列之2:三种开发模式实现数据访问

    前言 Entity Framework支持Database First.Model First和Code Only三种开发模式,各模式的开发流程大相径庭,开发体验完全不一样.三种开发模式各有优缺点,对 ...

  7. 基于Servlet+JSP+JavaBean开发模式的用户登录注册

    基于Servlet+JSP+JavaBean开发模式的用户登录注册 一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复 ...

  8. 前端开发模式--MV*

    前端的开发模式从经典的MVC框架,到完全独立出来controller层的MVP,再到以vue.angular.react引领的的MVVM框架. M指的是model(业务逻辑.接口数据层),V指的是vi ...

  9. AMDP + XLSX Workbench 报表开发模式

    本文介绍了我和同事通过使用AMDP + XLSX Workbench缩短报表开发周期.分离数据查询处理逻辑和前端展示工作的经验.欢迎讨论. 前言 最近接到了一套人力资源报表的开发需求,需要以EXCEL ...

最新文章

  1. CodeForces - 1401 F Reverse and Swap(线段树, 区间翻转, 区间交换,清晰易懂)
  2. Linux中的清屏命令
  3. 万豪旅享家官方商城携手神策数据,数字化礼遇更高质量的用户体验
  4. java异步调用数据库存储过程详解,java中如何调用存储过程
  5. 直播预告 | 小米人工智能部崔世起:小爱同学全双工技术实践
  6. Codeforces Round #470 (rated, Div. 2, based on VK Cup 2018 Round 1)B. Primal Sport
  7. python 文本相似度_python实现余弦相似度文本比较
  8. Ajax工作原理和原生JS的ajax封装
  9. MVC中用 BundleCollection 压缩CSS时图片路径问题
  10. matlab disp函数_从零开始的matlab学习笔记——(13)符号计算中的多项式
  11. 201671010133 2016-2017-2 《java程序设计》 初学java!
  12. Nyoj Fire Station
  13. 属于服务器端运行的程序_服务器端编程Java 引起人们的注意很大程度上始于 applet...
  14. java Apache poi 操作word生成word目录(根据word模板生成word文件)
  15. egret 里面设置MovieClip的scale缩放值时,没有效果的情况
  16. 用计算机中的知识秀恩爱,用你的专业,说最美的情话......
  17. asp.net中runat=server的含义
  18. 2022年武汉经开区首次进入规模以上工业企业奖励资金申报条件时间及资料
  19. Android禁止EditText弹出输入法
  20. (NDIS5.0)协议驱动Ndisprot源码解读

热门文章

  1. java注解式开发_JAVA语言之Spring MVC注解式开发使用详解[Java代码]
  2. Python-OpenCV 处理图像(一):基本操作
  3. 关于Spring的笔试题(一)
  4. JVM 垃圾回收算法及回收器详解
  5. 增大胸围!Mr Burning带你在家全方位虐胸!
  6. Deep Learning简明深度学习方法概述
  7. 怎样从10亿查询词找出出现频率最高的10个
  8. 程序员面试题精选100题(26)-和为n连续正数序列[算法]
  9. boost源码剖析之:Tuple Types(rev#2)
  10. 编程之美-饮料供货方法整理