vc编写windows服务:C编写Windows服务程序详细介绍



服务被用于需要在后台运行应用以及实现没有用户交互任务为了学习这种控制台应用基础知识C(不是)是最佳选择本文将建立并实现个简单服务其功能是查询系统中可用物理内存数量然后将结果写入个文本文件
最后你可以用所学知识编写自己Windows服务

  当初我写第个NT服务时我到MSDN上找例子在那里我找到了篇NigelThompson写文章:“CreatingaSimpleWin32ServiceinC这篇文章附带个C例子虽然这篇文章很好地解释了服务开发过程但是我仍然感觉缺少我需要重要信息我想理解通过什么框架什么以及何时但C在这方面没有让我轻松多少面向对象思路方法固然方便但由于用类对底层Win32进行了封装它不利于学习服务基本知识这就是为什么我觉得C更加适合于编写初级服务或者实现简单后台任务服务在你对服务有了充分透彻理解的后用C编写才能游刃有余当我离开原来工作岗位不得不向另个人转移我知识时候利用我用C所写例子就非常容易解释NT服务的所以然

  服务是个运行在后台并实现勿需用户交互任务控制台WindowsNT/2000/XP提供为服务提供专门支持人们可以用服务控制面板来配置安装好服务也就是Windows2000/XP控制面板 管理工具中“服务”(或在“开始” “运行”对话框中输入services.msc/s——译者注)可以将服务配置成操作系统启动时自动启动这样你就不必每次再重启系统后还要手动启动服务

  本文将首先解释如何创建个定期查询可用物理内存并将结果写入某个文本文件服务然后指导你完成生成安装和实现服务整个过程

  第步:主和全局定义

  首先包含所需头文件例子要Win32(windows.h)和磁盘文件写入(stdio.h):

  

  #

  #

  接着定义两个常量:

  

  #SLEEP_TIME5000

  #LOGFILE\"C:\\\\MyServices\\\\memstatus.txt\"

  SLEEP_TIME指定两次连续查询可用内存的间毫秒间隔在第 2步中编写服务工作循环时候要使用该常量

  LOGFILE定义日志文件路径你将会用WriteToLog将内存查询结果输出到该文件WriteToLog定义如下:

  

  WriteToLog(char*str)

  {

   FILE*log;

   log=fopen(LOGFILE,\"a+\");

   (logNULL)

    -1;

   fprf(log,\"%s\\n\",str);

   fclose(log);

   0;

  }

  声明几个全局变量以便在多个的间共享它们值此外前向定义:

  

  SERVICE_STATUSServiceStatus;

  SERVICE_STATUS_HANDLEhStatus; [Page]

  

  voidServiceMain(argc,char**argv);

  voidControlHandler(DWORDrequest);

  InitService;

  现在准备工作已经就绪你可以开始编码了服务控制台个子集因此开始你可以定义它是入口点对于服务来说代码令人惊讶地简短它只创建分派表并启动控制分派机

  

  void

  {

   SERVICE_TABLE_ENTRYServiceTable[2];

   ServiceTable[0].lpServiceName=\"MemoryStatus\";

   ServiceTable[0].lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain;

  

   ServiceTable[1].lpServiceName=NULL;

   ServiceTable[1].lpServiceProc=NULL;

  

   //启动服务控制分派机线程

   StartServiceCtrlDispatcher(ServiceTable);

  }

  可能包含若干个服务个服务都必须列于专门分派表中(为此该定义了个ServiceTable结构)这个表中项都要在SERVICE_TABLE_ENTRY结构的中它有两个域:

  lpServiceName:指向表示服务名称指针;当定义了多个服务时那么这个域必须指定;

    lpServiceProc:指向服务主指针(服务入口点);

  分派表最后项必须是服务名和服务主NULL指针文本例子中只宿主个服务所以服务名定义是可选

  服务控制管理器(SCM:ServicesControlManager)是个管理系统所有服务进程当SCM启动某个服务时它等待某个进程主线程来StartServiceCtrlDispatcher将分派表传递给StartServiceCtrlDispatcher这将把进程主线程转换为控制分派器该分派器启动个新线程该线程运行分派表中每个服务ServiceMain(本文例子中只有个服务)分派器还监视中所有服务执行情况然后分派器将控制请求从SCM传给服务



  注意:如果StartServiceCtrlDispatcher30秒没有被便会报错为了避免这种情况我们必须在ServiceMain中(参见本文例子)或在非主单独线程中化服务分派表本文所描述服务不需要防范这样情况

  分派表中所有服务执行完的后(例如用户通过“服务”控制面板停止它们)或者发生StartServiceCtrlDispatcher返回然后主进程终止

第 2步:ServiceMain

  Listing1展示了ServiceMain代码是服务入口点它运行在个单独线程当中这个线程是由控制分派器创建ServiceMain应该尽可能早早为服务注册控制处理器这要通过RegisterServiceCtrlHadler来实现 [Page]
你要将两个参数传递给此:服务名和指向ControlHandlerfunction指针

  它指示控制分派器ControlHandler处理SCM控制请求注册完控制处理器的后获得状态句柄(hStatus)通过SetServiceStatus用hStatus向SCM报告服务状态

  Listing1展示了如何指定服务特征和其当前状态来化ServiceStatus结构ServiceStatus结构每个域都有其用途:

  dwServiceType:指示服务类型创建Win32服务赋值SERVICE_WIN32;

  dwCurrentState:指定服务当前状态服务化在这里没有完成所以这里状态为SERVICE_START_PENDING;

  dwControlsAccepted:这个域通知SCM服务接受哪个域本文例子是允许STOP和SHUTDOWN请求处理控制请求将在第 3步讨论;

  dwWin32ExitCode和dwServiceSpecicExitCode:这两个域在你终止服务并报告退出细节时很有用化服务时并不退出因此它们值为0;

  dwCheckPo和dwWaitH:这两个域表示化某个服务进程时要30秒以上本文例子服务化过程很短所以这两个域值都为0

  SetServiceStatus向SCM报告服务状态时要提供hStatus句柄和ServiceStatus结构注意ServiceStatus个全局变量所以你可以跨多个使用它ServiceMain你给结构几个域赋值它们在服务运行整个过程中都保持不变比如:dwServiceType

  在报告了服务状态的后你可以InitService来完成这个只是添加个介绍说明性串到日志文件如下面代码所示:

  

  //服务

  InitService

  {

   result;

   result=WriteToLog(\"Monitoringstarted.\");

   (result);

  }

  在ServiceMain中检查InitService返回值如果化有错(有可能写日志文件失败)则将服务状态置为终止并退出ServiceMain:

  

  error=InitService;

  (error)

  {

   //化失败终止服务

   ServiceStatus.dwCurrentState=SERVICE_STOPPED;

   ServiceStatus.dwWin32ExitCode=-1;

   SetServiceStatus(hStatus,&ServiceStatus);

   //退出ServiceMain

   ;

  }

  如果化成功则向SCM报告状态:

  

  //向SCM报告运行状态 [Page]

  ServiceStatus.dwCurrentState=SERVICE_RUNNING;

  SetServiceStatus(hStatus,&ServiceStatus);

  接着启动工作循环每 5秒钟查询个可用物理内存并将结果写入日志文件

  如Listing1所示循环直到服务状态为SERVICE_RUNNING或日志文件写入出错为止状态可能在ControlHandler响应SCM控制请求时修改

第 3步:处理控制请求

  在第 2步中你用ServiceMain注册了控制处理器控制处理器和处理各种消息窗口回调非常类似它检查SCM发送了什么请求并采取相应行动


  每次你SetServiceStatus时候必须指定服务接收STOP和SHUTDOWN请求Listing2示范了如何在ControlHandler中处理它们

  STOP请求是SCM终止服务时候发送例如如果用户在“服务”控制面板中手动终止服务SHUTDOWN请求是关闭机器时由SCM发送给所有运行中服务请求两种情况处理方式相同:

  写日志文件监视停止;

  向SCM报告SERVICE_STOPPED状态;

  由于ServiceStatus结构对于整个而言为全局量ServiceStatus中工作循环在当前状态改变或服务终止后停止其它控制请求如:PAUSE和CONTINUE在本文例子没有处理

  控制处理器必须报告服务状态即便SCM每次发送控制请求时候状态保持相同因此不管响应什么请求都要SetServiceStatus



   第 4步:安装和配置服务

  编好了将的编译成exe文件本文例子创建文件叫MemoryStatus.exe将它拷贝到C:\\MyServices文件夹为了在机器上安装这个服务需要用SC.EXE可执行文件它是Win32PlatformSDK中附带个工具(译者注:VisaulStudio.NET2003IDE环境中也有这个工具具体存放位置在:C:\\ProgramFiles\\MicrosoftVisualStudio.NET2003\\Common7\\Tools\\Bin\\winnt)使用这个实用工具可以安装和移除服务其它控制操作将通过服务控制面板来完成以下是用命令行安装MemoryStatus服务思路方法:

  

  sccreateMemoryStatusbinpath=c:\\MyServices\\MemoryStatus.exe

  发出此创建命令指定服务名和 2进制文件路径(注意binpath=和路径的间那个空格)安装成功后便可以用服务控制面板来控制这个服务用控制面板工具栏启动和终止这个服务


  MemoryStatus启动类型是手动也就是说根据需要来启动这个服务右键单击该服务然后选择上下文菜单中“属性”菜单项此时显示该服务属性窗口在这里可以修改启动类型以及其它设置你还可以从“常规”标签中启动/停止服务以下是从系统中移除服务思路方法: [Page]

  

  scdeleteMemoryStatus

  指定“delete”选项和服务名此服务将被标记为删除下次西通重启后该服务将被完全移除

  第 5步:测试服务

  从服务控制面板启动MemoryStatus服务如果化不出错表示启动成功会儿将服务停止检查下C:\\MyServices文件夹中memstatus.txt文件服务输出在我机器上输出是这样:

  

  Monitoringstarted.

  273469440

  273379328

  273133568

  273084416

  Monitoringstopped.

  为了测试MemoryStatus服务在出错情况下行为可以将memstatus.txt文件设置成只读这样服务应该无法启动

  去掉只读属性启动服务在将文件设成只读服务将停止执行此时日志文件写入失败如果你更新服务控制面板内容会发现服务状态是已经停止
Tags:  windows程序设计 windows脚本编写 windows服务程序 vc编写windows服务

延伸阅读

最新评论

发表评论