你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 图形图象处理与游戏编程
6.4 Windows服务编写综述(下)
 

cout<<"OpenSCManager ERROR:"<<GetLastError()<<endl;

        CloseServiceHandle(scm);

        return false;

    }

    Service = OpenService(scm,pszServiceName,SERVICE_ALL_ACCESS|DELETE);

    if (!Service){

        cout<<"OpenService ERROR:"<<GetLastError()<<endl;

        CloseServiceHandle(Service);

        CloseServiceHandle(scm);

        return false;

    }

    success = QueryServiceStatus(Service,&status);

    if (!success){

        cout<<"QueryServiceStatus ERROR:"<<GetLastError()<<endl;

        CloseServiceHandle(Service);

        CloseServiceHandle(scm);

        return false;

    }

    if (status.dwCurrentState!=SERVICE_STOPPED)

    {

        success = ControlService(Service,SERVICE_CONTROL_STOP,&status);

        if (!success){

            cout<<"ControlService ERROR:"<<GetLastError()<<endl;

            CloseServiceHandle(Service);

            CloseServiceHandle(scm);

            return false;

        }

    }

    success = DeleteService(Service);

    if (!success){

        cout<<"DeleteService ERROR:"<<GetLastError()<<endl;

        CloseServiceHandle(Service);

        CloseServiceHandle(scm);

        return false;

    }

    CloseServiceHandle(Service);

    CloseServiceHandle(scm);

    return true;

四、服务控制程序

1.服务控制程序概要

在上面了解了服务程序的编写,现在再来看一下控制服务时会用到的几个常用API。服务控制程序的编写与标准的Windows应用程序无异,它要用到服务管理函数,如:OpenSCManagerOpenServiceQueryServiceConfigStartServiceQueryServiceStatusControlService等;它都在系统的advapi32.dll中实现。在使用SCM的函数时,SCP必须要首先调用OpenSCManager函数,打开一个通向SCM的通道。调用这个函数的时候,SCP还必须指定它想要执行的动作类型;也就上面所提到的dwDesiredAccess 参数取值:SC_MANAGER_ALL_ACCESSSC_MANAGER_CREATE_SERVICESC_MANAGER_ENUMERATE_SERVICE SC_MANAGER_QUERY_LOCK_STATUSSC_MANAGER_ENUMERATE_SERVICE SC_MANAGER_QUERY_LOCK_STATUS SC_MANAGER_LOCKSC_MANAGER_CONNECT。例如:要枚举当前所有的服务就必须给dwDesiredAccess参数指定SC_MANAGER_ENUMERATE_SERVICE;同时也可以指定其他的值,当指定多个值时要把它用按位或(|)符号连接起来。

与此同时在调用OpenService时也必须告知SCM要对服务进行的动作;它有三个参数,最后一个参数dwDesiredAccess指出要对服务进行的操作,这些操作的标记与CreateService中的dwDesiredAccess参数标记值一样。当以SERVICE_ALL_ACCESS访问权限打开服务后就可以对它进行配置(QueryServiceConfig)、控制(ControlService)、查询状态(QueryServiceStatus)、设置状态(SetServiceStatus)、删除(DeleteService)等所有访问操作。

2.枚举服务

先来看EnumDependentStatus函数原型:

BOOL EnumServicesStatus (

  SC_HANDLE hService,      // SCM控制句柄

DWORD dwServiceType,    //要枚举服务还有驱动

  DWORD dwServiceState,    // 要枚举什么状态的服务

  LPENUM_SERVICE_STATUS lpServices,// 存储枚举出服务的内存地址

  DWORD cbBufSize,         // lpServices指向内存区大小

  LPDWORD pcbBytesNeeded,  //实际需要的内存大小

  LPDWORD lpServicesReturned //返回枚举到服务年个数

LPDWORD lpResumeHandle  //指向下一个有效的入口

);

dwServiceState参数由:SERVICE_ACTIVESERVICE_INACTIVESERVICE_STATE_ALL三种值分别枚举当前活动、不活动、全部的服务。函数会返回一个ENUM_SERVICE_STATUS的数组,ENUM_SERVICE_STATUS有三个域分别指出服务的服务名、显示名和当前状态一个指针;可以根据服务名打开枚举出的服务,以得到它更加详细的信息。下面具体看一段程序:

//清空服务信息队列

DeletItemAll();

LPENUM_SERVICE_STATUS st=NULL;

st=NULL;

DWORD ret=0;

DWORD size=0;

ServiceInfo info;

SC_HANDLE sc=OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);

SC_HANDLE sh;

char* szInfo[1024*8];

DWORD dwSize=1024*8;

CString str;

//第一次调用来得到需要多大的内存区

EnumServicesStatus(sc,SERVICE_WIN32,SERVICE_STATE_ALL,st,size,&size,&ret,NULL);

//申请需要的内存

st=(LPENUM_SERVICE_STATUS)LocalAlloc(LPTR,size);

EnumServicesStatus(sc,SERVICE_WIN32,SERVICE_STATE_ALL,st,size,&size,&ret,NULL);

//开始记录枚举出服务的信息

for(DWORD i=0;i<ret;i++){

    dwSize=1024*8;

    ZeroMemory(szInfo,dwSize);

     info.Name.Format("%s",st[i].lpDisplayName);

    info.serviceNmae.Format("%s",st[i].lpServiceName);

    info.State.Format("%d",st[i].ServiceStatus.dwCurrentState);

    sh=OpenService(sc,st[i].lpServiceName,SERVICE_ALL_ACCESS);

    //得到服务描述信息

    QueryServiceConfig2(sh,SERVICE_CONFIG_DESCRIPTION,(LPBYTE)szInfo,dwSize,&dwSize);

    info.Desc.Format("%s",((LPSERVICE_DESCRIPTION)szInfo)->lpDescription);

    //得到服务的启动账户名

    ZeroMemory(szInfo,dwSize);

    dwSize=1024*8;

    QueryServiceConfig(sh,(LPQUERY_SERVICE_CONFIG)szInfo,dwSize,&dwSize);

    info.LoginUser.Format("%s",((LPQUERY_SERVICE_CONFIG)szInfo)->lpServiceStartName);

    CloseServiceHandle(sh);

    //添加到信息队列中

    ItemAdd(&info);

}

CloseServiceHandle(sc);

return TRUE;

上面程序中用到了两个查询服务当前配置的函数QueryServiceConfig2QueryServiceConfig。它们有所不同是QueryServiceConfig2可以通过设置第二个参数是SERVICE_CONFIG_DESCRIPTION还是SERVICE_CONFIG_FAILURE_ACTIONS来得到服务的描述信息和失败的活动;而QueryServiceConfig则查询返回一个QUERY_SERVICE_CONFIG结构,这个结构存储了服务的类型、启动类型、错误控制标记、服务文件所在路径、显示名等信息,详细情况可以查看MSDN。与这个两函数相对应还有两个配置函数ChangeServiceConfig2ChangeServiceConfig

3.配置服务

上面举了一个创建服务的程序片段,其中只是创建服务并未设置服务描述信息,启动服务的操作。下面的程序片段给出了示例:

bool RegterService(char* pszServiceName,

                   char* pszDisplayName,

                   char* pszServicePath,

                   char* pszDescription)

{

    SC_HANDLE newService,scm;

    BOOL success = FALSE;

    SERVICE_STATUS status;

    SERVICE_DESCRIPTION description;

 

    if (pszDisplayName==NULL&&pszServiceName==NULL&&pszServicePath==NULL)

    {

        return false;

    }

    description.lpDescription=pszDescription;

    scm = OpenSCManager(NULL,NULL,SC_MANAGER_ENUMERATE_SERVICE|SC_MANAGER_CREATE_SERVICE);

    if (!scm){

        OUT_DEBUG("OpenSCManager ERROR!");

        return false;

    }

    newService = CreateService(scm,pszServiceName,pszDisplayName,

        SERVICE_ALL_ACCESS|SERVICE_STOP,SERVICE_WIN32_OWN_PROCESS,

        SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,pszServicePath,

        0,0,0,0,0);

    if (!newService){

        OUT_DEBUG("CreateService ERROR!");

        CloseServiceHandle(scm);

        return false;

    }

    if (description.lpDescription!=NULL)

    {

        success=ChangeServiceConfig2(newService,

                    SERVICE_CONFIG_DESCRIPTION,

                    &description);

    }

    success = QueryServiceStatus(newService,&status);

    if (!success){

        cout<<"QueryServiceStatus ERROR:"<<GetLastError()<<endl;

        CloseServiceHandle(newService);

        CloseServiceHandle(scm);

        return false;

    }

    if (status.dwCurrentState!=SERVICE_RUNNING)

    {

        success = StartService(newService,NULL,NULL);

        if (!success){

            cout<<"ControlService ERROR:"<<GetLastError()<<endl;

            CloseServiceHandle(newService);

            CloseServiceHandle(scm);

            return false;

        }

    }

    CloseServiceHandle(newService);

    CloseServiceHandle(scm);

    return true;

}

ChangeServiceConfig函数可以配置更多关于服务的信息,下面列出其原型:

BOOL ChangeServiceConfig(

  SC_HANDLE hService     // 打开服务时返回的句柄

  DWORD dwServiceType,   // 服务的类型

  DWORD dwStartType,     // 何时启动服务

  DWORD dwErrorControl,  // 错误控制代码

  LPCTSTR lpBinaryPathName,  // 服务的路径

  LPCTSTR lpLoadOrderGroup,  // 服务所属的组

  LPDWORD lpdwTagId,     // 服务的标记

  LPCTSTR lpDependencies,    // 依赖的其他服务和组

  LPCTSTR lpServiceStartName,// 服务的启动用户

  LPCTSTR lpPassword,   //服务启动用户的密码

  LPCTSTR lpDisplayName      // 服务的显示名

);

大家可以看到ChangeServiceConfigCreateServiceee 有着相似的参数,它们的使用方法也十分相似可以参照CreateServiceee函数调用。它主要在服务安装完成后,需要对服务的配置进行修改时调用,除了lpDisplayName改变时需要服务停止才能生效外,其他都可以运行时动态改变;更详细信息可查阅MSDN

4.控制服务

有时要根据实际情况启动、暂停、停止一个服务。在上面的程序示例里面就一个启动服务的调用。这里再简单介绍一下这个函数:

BOOL StartService(

  SC_HANDLE hService,            // 打开服务时返回的句柄

  DWORD dwNumServiceArgs,        // 服务程序参数的个数

  LPCTSTR *lpServiceArgVectors   // 存放服务程序参数的数组

);

当服务程序需要配置启动参数时就需要使用StartService后面的两个参数,服务程序的入口函数也就可以接到相关的参数了。函数调用成功时返回非零,当返回零时调用失败;更详细的出错信息可以调用GetLastError获得。

启动后如果还要控制其暂停、继续、停止的话,还需要另一个函数调用来完成。在上面删除服务的例示程序片段中调用了ControlService函数来停止服务,下面介绍一下它的详细信息:

BOOL ControlService(

  SC_HANDLE hService,  // 打开服务时返回的句柄

  DWORD dwControl,     // 控制代码

  LPSERVICE_STATUS lpServiceStatus // 服务的状态

);

调用此函数会把控制代码发给指定服务程序的Handler处理函数同时返回服务的状态,服务程序得到相应的控制代码后根据协议要执行相应的操作;控制代码就是Handler规定响应的所有代码。不能启动和停止服务安全描述符不允许的服务程序。默认的安全描述符只允许LocalSystem Administrators Power Users来启动和停止服务程序。服务的安全描述符用SetServiceObjectSecurity来设置(更详细信息的信息可查阅MSDN)。

五、结语

这里从总体上讲述了SCM的工作流程序、服务程序编写的方法及控制服务所用到的一些函数。文中只列出了一部分函数的信息,更详细的信息大家可以查阅MSDN。希望本文讲述的内容能帮助大家理解服务程序的编写与控制。

  推荐精品文章

·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089