Windows服务学习一

来源:互联网 发布:国家网络应急中心待遇 编辑:程序博客网 时间:2024/05/16 03:51

 关于Windows服务编程的学习,在网上搜索还是很多的。

恩,个人最近总是不在状态啊,看代码看着就睡着了,只好自己写篇博客,来加深下印象了,所有资料都是从网上收集的,个人整合了一下。

 

一、什么是Windows Service

Windows Service 是一种可随 Windows 操作系统启动而启动的,在后台运行的,通常不和用户产生交互的程序。它无法通过双击来运行,类似于 Unix 守护进程(daemon processes),当用户注销时它也不会停止。

Windows 服务由三部分组成:1.一个服务可执行文件;2.一个服务控制程序(SCP);3.服务控制管理器(SCM),负责在 HKLM/SYSTEM/CurrentControlSet/Services 下创建服务键值。用户可通过 SCP 控制服务的启动、停止、暂停等,SCP 会通过 SCM 调用服务程序。

Window 标准的exe可执行程序通常有一个用户界面,Console或GUI,通常由用户来启动或停止


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ojlovecd/archive/2008/05/07/2412564.aspx

 

二、如何编写Windows Service

我们编写的服务,其实就是要编写在上面所说的Windows服务三部分组成之一的第一点 服务可执行文件,服务控制程序和服务控制管理器是Windows本身就有的,我们要实现的就是将我们编写的服务可执行文件编写安装到Windows内并向服务控制管理器注册服务,也方便服务控制程序能够对我们的服务进行操作。所以其实是很简单的。

下面分步讲解完成过程:

1、要创建一个服务我们首先就要告诉服务控制管理器:“我们要创建一个服务”,它会告诉你是不是可以创建一个服务。如何告诉呢,很简单,如下:

//打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

参数说明:

其中第一个参数是要在那个电脑上创建服务啊,如果是本机的话,直接使用NULL就可以了,否则该参数是一个'/0'结尾的字符串,并且前缀必须是“//”。

第二个参数是要打开服务控制管理器的名称,一般设为SERVICES_ACTIVE_DATABASE或是NULL均可。

第三个是设置访问权限了,SC_MANAGER_ALL_ACCESS是设置可以访问所有,具体的此处不在罗列。

当然该函数的返回值是我们调用函数的结果是一个SC_HANDLE类型,一看就知道是服务控制器的句柄了。

我们要调用了,当让是对我们有好处的了,不然我闲着没事干了,我们就是要用它的返回值作为下一个函数的句柄,如下:

//创建服务 

SC_HANDLE hService = ::CreateService(  

hSCM, //服务控制器句柄了

szServiceName, //创建的服务名称     最长256个字符 无大小写之分   不能包含/和/字符                      

szServiceName, //显示的名称      最长256个字符 无大小写之分

 SERVICE_ALL_ACCESS, //访问权限

SERVICE_WIN32_OWN_PROCESS,//服务类型  

SERVICE_DEMAND_START, //指出服务何时开始

SERVICE_ERROR_NORMAL, //指出启动出错后如何处理      

szFilePath, //生成可执行文件路径 如果路径包含空格 要用引号

NULL,//如果服务属于组,则制定顺序装入服务组名   

NULL, //用来接收标志值,在组中使用的

_T(""),//制定启动该服务前要启动的服务或服务组

NULL,//以NULL结尾的字符串,指定服务帐号,如果为NULL则是LocalSystem帐号  

NULL); //以NULL结尾的字符串,制定对应的口令,LocalSystem时为NULL

 

//创建服务后关掉句柄

::CloseServiceHandle(hService);   

::CloseServiceHandle(hSCM);

上面的过程是创建了一个服务,其实就是常说的安装了一个服务了。有创建自然就有删除,如果说是安装那也就得有卸载吧,下面就是卸载了。

2、服务的删除(卸载): 

//打开服务控制管理器 ,同上
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
//打开服务,参数表示停止或删除
 SC_HANDLE = OpenService(hSCM, szServiceName, SERVICE_STOP | DELETE);
//停止服务
 ControlService(hService, SERVICE_CONTROL_STOP, &status);
//删除服务
 DeleteService(hService);
就这样简单,服务就被删除了,代码不是很难理解。上面说了服务的创建和删除,但是我们的服务到底做了写什么,如和让它去做我们让它做的事呢。请看下一点.
3、服务的编写过程:
服务程序是控制台程序的一个子集。因此,开始你可以定义一个 main 函数,它是程序的入口点。对于服务程序来说,main 的代码令人惊讶地简短,因为它只创建分派表并启动控制分派机。可以在Main函数中实现对服务的安装和卸载。现在我共看过两个版本的创建服务方式,其中一个是在Main函数中不实现安装和卸载,而是通过调用VC提供的工具SC来安装和卸载的。代码如下:
   void main() //main函数相当的简单啊
{
    SERVICE_TABLE_ENTRY ServiceTable[2];//服务表入口,该结果的最后必须以NULL结尾
    ServiceTable[0].lpServiceName = "MemoryStatus";//服务名称
    ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
    //一个程序可以包含如干个服务,每个服务都必须列于专门的分派表中,此处只有一个服务。
    // 最后一个成员的服务名和服务处理函数必须为NULL
    ServiceTable[1].lpServiceName = NULL;
    ServiceTable[1].lpServiceProc = NULL;

    // 启动服务的控制分派机线程,该函数在30秒钟没有调用会报错,为此我们在此不能够做更多的工作
    //必须把初始化服务分派表的工作放在线程中或ServiceMain函数中去完成。
    StartServiceCtrlDispatcher(ServiceTable);
}

上面提到了一个函数ServiceMain,该函数就是服务的入口点。它运行在一个单独的线程当中,这个线程是由控制分派器创建的(当然我们没有看到CreateThread类似的函数)。ServiceMain应该早早地为服务注册服务控制处理器,该注册是通过函数RegisterServiceCtrlHandler来实现,我们只需要传递给该函数两个参数(服务名和控制处理函数的地址)即可。注册完成后返回服务状态句柄,我们就可以通过该函数实现对服务状态的操作了。如下:

SERVICE_STATUS          ServiceStatus;//服务状态
SERVICE_STATUS_HANDLE   hStatus; //服务状态句柄

ServiceStatus.dwServiceType =
      SERVICE_WIN32; //设置为WIN32服务
   ServiceStatus.dwCurrentState =
      SERVICE_START_PENDING; //开始时并不允许
   ServiceStatus.dwControlsAccepted   = 
      SERVICE_ACCEPT_STOP | //要使用SERVICE_ACCEPT_STOP 否则我们不能控制服务
      SERVICE_ACCEPT_SHUTDOWN;
   ServiceStatus.dwWin32ExitCode = 0; //在终止服务时使用,此处设为0
   ServiceStatus.dwServiceSpecificExitCode = 0; //在终止服务时使用
   ServiceStatus.dwCheckPoint = 0; //这两个域表示初始化某个服务进程时要30秒以上。本文例
   ServiceStatus.dwWaitHint = 0; //子服务的初始化过程很短,所以这两个域的值都为 0。 
 
   hStatus = RegisterServiceCtrlHandler(
      "MemoryStatus", //为服务名为MemoryStatus的服务注册控制器函数为ControlHander
      (LPHANDLER_FUNCTION)ControlHandler);
   if (hStatus == (SERVICE_STATUS_HANDLE)0) //判断是否注册成功
   {
      // Registering Control Handler failed
      return;
   }   

//上面没有错后就设置服务状态为运行状态并调用SetServiceStatus向SCM报告

   ServiceStatus.dwCurrentState =
      SERVICE_RUNNING;
   SetServiceStatus (hStatus, &ServiceStatus);

接着在ServiceMain函数中完成上面的工作后,我们就可以开始执行我们自己的工作了。

具体的请参看如下两篇文章,很谢谢他们的分享,本文相当的于是看这两篇文章后的笔记吧。

http://www.vckbase.com/document/viewdoc/?id=1474

http://blog.csdn.net/xiaohuaidan717/archive/2006/12/08/1434508.aspx