windows服务管理操作

服务程序是windows上重要的一类程序,它们虽然不与用户进行界面交互,但是它们对于系统有着重要的意义。windows上为了管理服务程序提供了一个特别的程序:服务控制管理程序,系统上关于服务控制管理的API基本上都与这个程序打交道。下面通过对服务程序的操作来说明这些API函数

获取系统服务的信息

在windows系统中有专门用来存储服务信息的数据库,而获取系统服务信息主要是通过在这样的数据库中查找。所用到的函数主要有:

OpenSCManager:打开数据库

SC_HANDLE WINAPI OpenSCManager(
  __in          LPCTSTR lpMachineName,
  __in          LPCTSTR lpDatabaseName,
  __in          DWORD dwDesiredAccess
);

这个函数主要用来连接特定计算机上的服务控制管理器,并打开服务控制管理器的数据库。

函数的参数有:

lpMachineName:主机名称

lpDatabaseName:主机中服务数据库的名称

dwDesiredAccess:以何种权限打开服务程序

前两个参数都可以为NULL,如果第一个参数为NULL,则表示在本机上获取,第二个参数为NULL表示从注册表中获取,第三个参数的主要传入如下值:

SC_MANAGER_ALL_ACCESS (0xF003F) :默认拥有所有权限

SC_MANAGER_CREATE_SERVICE (0x0002):具有创建服务的权限

SC_MANAGER_CONNECT (0x0001):连接的权利

SC_MANAGER_ENUMERATE_SERVICE (0x0004) 枚举里面信息的权限

后面的就不再一一说明了,详细信息请看MSDN的记录。在程序中为了方便一般采用SC_MANAGER_ALL_ACCESS 参数

函数如果调用成功,则会返回一个操作数据库的句柄,以后的关于服务的操作都已这个参数作为第一个参数。

EnumServicesStatus:枚举系统服务

BOOL WINAPI EnumServicesStatus(
  __in          SC_HANDLE hSCManager,
  __in          DWORD dwServiceType,
  __in          DWORD dwServiceState,
  __out         LPENUM_SERVICE_STATUS lpServices,
  __in          DWORD cbBufSize,
  __out         LPDWORD pcbBytesNeeded,
  __out         LPDWORD lpServicesReturned,
  __in_out      LPDWORD lpResumeHandle
);

hSCManager:服务数据库句柄

dwServiceType:枚举服务的类型,主要有:SERVICE_DRIVER(驱动类型服务)、SERVICE_WIN32(win32类型的服务)

dwServiceState:表示枚举哪中状态的服务,主要有:SERVICE_ACTIVE(已启动的服务)、SERVICE_INACTIVE(未启动的服务)、SERVICE_STATE_ALL(所有服务)

lpServices:这个参数主要是作为一个缓冲区,用来返回服务信息,类型ENUM_SERVICE_STATUS主要存储的是服务名称、显示名称以及一个SERVICE_STATUS 结构体,该结构体的原型如下:

typedef struct _SERVICE_STATUS{
    DWORD dwServiceType; //服务类型
    DWORD dwControlsAccepted;//当前状态
    DWORD dwServiceSpecificExitCode;
    DWORD dwCheckPoint;
    DWORD dwWaitHint;
} SERVICE_STATUS,  *LPSERVICE_STATUS;

cbBufSize:缓冲区的大小

pcbBytesNeeded:实际需要缓冲区的大小

lpServicesReturned:服务的返回值

lpResumeHandle:额外的句柄

每一个ENUM_SERVICE_STATUS结构体保存的是一个服务的信息,但是我们事先并不知道有多少个服务,因此不知道该定义多大的服务信息数组,但是windows考虑到了这一点,当函数调用失败时利用GetLastError返回ERROR_MORE_DATA时表示提供的缓冲区不够,这个时候参数pcbBytesNeeded会返回正确的大小,所以使用这个函数一般会经过两个调用第一次lpServices = NULL, cbBufSize = 0,这个时候函数出错并返回所需要的实际大小,然后根据大小动态分陪一个内存缓冲区或者提供一个数组,并传入实际大小,以获取所有服务的信息。下面提供一个具体的例子:

    SC_HANDLE scHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if(NULL == scHandle)
    {
        return FALSE;
    }
    LPENUM_SERVICE_STATUS pServices = NULL;
    DWORD dwByteNeed = 0;
    DWORD dwServiceReturn = 0;
    LPDWORD lpResumeHandle = NULL;
    //第一次调用,将缓冲区设置为NULL并将缓冲区大小设置为0
    BOOL  bRet = ::EnumServicesStatus(scHandle, SERVICE_WIN32, SERVICE_STATE_ALL, pServices, 0, &dwByteNeed, &dwServiceReturn, lpResumeHandle);
    if(!bRet)
    {
    //如果是因为缓冲区大小不够
        if(ERROR_MORE_DATA == GetLastError())
        {
        //保存缓冲区的真实大小
            DWORD dwRealNeed = dwByteNeed;
            //多分配一个是为了保存字符串末尾的0
            pServices = (LPENUM_SERVICE_STATUS)new char[dwRealNeed + 1];
            ASSERT(NULL != pServices);
            ZeroMemory(pServices, dwRealNeed + 1);
            bRet = ::EnumServicesStatus(scHandle, SERVICE_WIN32, SERVICE_STATE_ALL, pServices, dwRealNeed + 1, &dwByteNeed, &dwServiceReturn, lpResumeHandle);
            //通过上述代码可以获取到服务的相关信息
        }
    }

获取服务的主程序所在路径、启动类型以及依赖项

上述代码只能获取到系统服务的部分信息,比如服务的名称,显示名称,等等至于其他的信息需要调用另外的API函数获取

OpenService获取具体服务的句柄

SC_HANDLE WINAPI OpenService(
  __in          SC_HANDLE hSCManager, //服务数据库的句柄
  __in          LPCTSTR lpServiceName,//服务的名称
  __in          DWORD dwDesiredAccess//以何种权限打开,为了方便一般填入SERVICE_ALL_ACCESS所有权限
);

QueryServiceConfig 查询系统服务信息

BOOL WINAPI QueryServiceConfig(
  __in          SC_HANDLE hService,
  __out         LPQUERY_SERVICE_CONFIG lpServiceConfig,
  __in          DWORD cbBufSize,
  __out         LPDWORD pcbBytesNeeded
);

这个函数的第二个参数是一个结构体指针这个结构体的定义如下:

typedef struct _QUERY_SERVICE_CONFIG {
    DWORD dwServiceType;  //服务类型
    DWORD dwStartType;  //启动类型
    DWORD dwErrorControl;//错误码,服务执行出错时返回,操作系统根据这个错误码来做相应的处理
    LPTSTR lpBinaryPathName;//主程序所在路径
    LPTSTR lpLoadOrderGroup;
    DWORD dwTagId;
    LPTSTR lpDependencies; //依赖项
    LPTSTR lpServiceStartName;
    LPTSTR lpDisplayName; //显示名称
} QUERY_SERVICE_CONFIG,  *LPQUERY_SERVICE_CONFIG;

这个函数的调用方式与EnumServicesStatus相同,也是通过两次调用,第一次获得所需的空间大小,这个大小通过第四个参数返回。

下面的代码展示了如何调用这两个函数

//第一个参数是通过OpenSCManager函数获取得到的
SC_HANDLE h_SCService = OpenService(h_SCHandle, pSrvItem->strSrvName, SERVICE_ALL_ACCESS);
    if(NULL == h_SCService)
    {
        CloseServiceHandle(h_SCHandle);
        return FALSE;
    }

    LPQUERY_SERVICE_CONFIG pSrvConfig = NULL;
    DWORD dwBuffSize = 0;
    DWORD dwBuffNeed = 0;
    BOOL bRet = QueryServiceConfig(h_SCService, pSrvConfig, dwBuffSize, &dwBuffNeed);
    if(!bRet)
    {
        if(ERROR_INSUFFICIENT_BUFFER == GetLastError())
        {
            DWORD dwRealNeed = dwBuffNeed;
            pSrvConfig = (LPQUERY_SERVICE_CONFIG)new char[dwRealNeed + 1];
            ASSERT(NULL != pSrvConfig);
            bRet = QueryServiceConfig(h_SCService, pSrvConfig, dwRealNeed, &dwBuffNeed);
        }
    }

获取服务的描述信息

描述信息一般是有服务开发人员提供,以便解释服务程序的作用等等信息,这些信息在注入服务时由系统记录,并呈现给用户。获取系统服务主要使用的API函数是QueryServiceConfig2

BOOL WINAPI QueryServiceConfig2(
  __in          SC_HANDLE hService,
  __in          DWORD dwInfoLevel,//将获取何种信息在这我们需要填写SERVICE_CONFIG_DESCRIPTION表示获取描述信息
  __out         LPBYTE lpBuffer,
  __in          DWORD cbBufSize,
  __out         LPDWORD pcbBytesNeeded
);

这个函数不想上面的QueryServiceConfig一次可以获取服务的多项信息,它是根据第二个参数指定需要获取哪项信息,然后返回到第3个参数提供的缓冲区中,这个缓冲区是一个BYTE类型的指针,调用者需要根据具体的情况进行类型转化。同样这个函数需要进行两次调用。

BOOL bRet = QueryServiceConfig2(h_SCService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpByte, cbBufSize, &dwBuffNeed);
    if(!bRet)
    {
        if(ERROR_INSUFFICIENT_BUFFER == GetLastError())
        {
            DWORD dwRealNeed = dwBuffNeed;
            //LPSERVICE_DESCRIPTION结构体中只保存了一个字符串指针lpDescription
            lpByte = (LPSERVICE_DESCRIPTION)new char[dwRealNeed + 1];
            ASSERT(NULL != lpByte);
            bRet = QueryServiceConfig2(h_SCService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpByte, dwRealNeed, &dwBuffNeed);
            if(!bRet)
            {
                delete[] lpByte;
                goto __ERROR_RET;
            }

            pSrvItem->strSrvDescrible = lpByte->lpDescription;
            delete[] lpByte;
            CloseServiceHandle(h_SCService);
            CloseServiceHandle(h_SCHandle);
            return TRUE;
        }
    }
__ERROR_RET:
    CloseServiceHandle(h_SCService);
    CloseServiceHandle(h_SCHandle);
    return FALSE;

服务的控制

服务控制主要控制服务的启动,暂停,恢复,停止等等。

StartService启动服务

BOOL WINAPI StartService(
  __in          SC_HANDLE hService,
  __in          DWORD dwNumServiceArgs,//启动参数的个数
  __in          LPCTSTR* lpServiceArgVectors//参数列表指针
);

这个函数有点类似于main函数,main函数可以传递命令行参数给程序,以便实现程序与用户的交互,这里同样可以传递参数,以便服务完成特定的功能,当第二个参数为0时第三个参数为NULL

ControlService 控制服务

BOOL WINAPI ControlService(
  __in          SC_HANDLE hService,
  __in          DWORD dwControl,//新状态
  __out         LPSERVICE_STATUS lpServiceStatus//服务的原始状态
);

这个函数用来完成除了启动之外的服务控制,其中第二个参数是服务的状态,它可使用的参数主要有如下几个:

取值 含义
SERVICE_CONTROL_CONTINUE 继续运行
SERVICE_CONTROL_PAUSE 暂停
SERVICE_CONTROL_STOP 停止

其余的部分不是很常用,故不在这里一一列举

下面是一个具体的例子,由于要考虑当前的状态以及服务是否支持这种状态,因此这部分的代码多了许多判断的部分

SERVICE_STATUS ServiceStatus = {0};
//获取当前的状态
    BOOL bRet = QueryServiceStatus(h_SCService, &ServiceStatus);
    ASSERT(bRet);
    if(ServiceStatus.dwCurrentState == dwNewStatus)
    {
        goto __RETURN;
    }
//如果当前服务正处在启动和暂停之中,但是没有完成这个动作时不允许改变服务状态
    if(ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING || SERVICE_PAUSE_PENDING ==ServiceStatus.dwCurrentState ||
        SERVICE_START_PENDING == ServiceStatus.dwCurrentState || SERVICE_STOP_PENDING == ServiceStatus.dwCurrentState)
    {
        bRet = FALSE;
        goto __RETURN;
    }
//如果服务处在暂停状态,则只允许继续运行和停止
    if(SERVICE_PAUSED == ServiceStatus.dwCurrentState)
    {
        if(SERVICE_CONTROL_CONTINUE == dwNewStatus || SERVICE_CONTROL_STOP == dwNewStatus)
        {
            bRet = ControlService(h_SCService, dwNewStatus, &ServiceStatus);
            goto __RETURN;
        }else
        {
            bRet = FALSE;
            goto __RETURN;
        }
    }
//如果服务正在运行,则运行暂停和停止
    if(SERVICE_RUNNING == ServiceStatus.dwCurrentState)
    {
        if(SERVICE_CONTROL_PAUSE == dwNewStatus || SERVICE_CONTROL_STOP == dwNewStatus)
        {
            bRet = ControlService(h_SCService, dwNewStatus, &ServiceStatus);
            goto __RETURN;
        }else
        {
            bRet = FALSE;
            goto __RETURN;
        }
    }
//如果服务处于停止状态,则允许运行操作
    if(SERVICE_STOPPED == ServiceStatus.dwCurrentState)
    {
        if(SERVICE_RUNNING == dwNewStatus)
        {
            bRet = StartService(h_SCService, 0, NULL);
            goto __RETURN;
        }else
        {
            bRet = FALSE;
            goto __RETURN;
        }
    }
__RETURN:
    if(bRet)
    {
        pIter->pNode->dwCurrentStatus = dwNewStatus;
    }
    CloseServiceHandle(h_SCService);
    CloseServiceHandle(h_SCHandle);
    return bRet;

当前服务的状态可以使用EnumServicesStatus函数获取,但是这个函数是获取系统中记录的所有的服务程序,在这样的情况下,我们只需要获取一个服务的状态,调用这个函数总有杀鸡用牛刀的意思,所以采用的是另外一个函数QueryServiceStatus,服务程序的主要状态有如下几种:

取值 状态描述
SERVICE_RUNNING 已启动
SERVICE_STOPPED 已停止
SERVICE_PAUSED 已暂停
SERVICE_CONTINUE_PENDING 正在恢复
SERVICE_PAUSE_PENDING 正在暂停
SERVICE_START_PENDING 正在启动
SERVICE_STOP_PENDING 正在停止

其中前几个是完成时,也就是完成了从一个状态到另一个的转化,而后面几个是进行时,正在这两种状态之间

获取服务的可控类型

在控制服务时不光要考虑服务程序当前的状态,还要考虑它是否支持这种状态,比如有的服务就不支持暂停和恢复操作

QueryServiceStatus 查询服务的状态信息

这个函数主要传递一个SERVICE_STATUS结构体的指针。这个结构体的定义如下:

typedef struct _SERVICE_STATUS {
    DWORD dwServiceType;//服务类型
    DWORD dwCurrentState; //当前状态
    DWORD dwControlsAccepted;//允许的操作
    DWORD dwWin32ExitCode;
    DWORD dwServiceSpecificExitCode;
    DWORD dwCheckPoint;
    DWORD dwWaitHint;
} SERVICE_STATUS,  *LPSERVICE_STATUS;

改变服务的启动类型

服务的启动类型主要有开机启动、手动启动、以及禁止启动等项。改变启动类型的函数主要是:ChangeServiceConfig。函数原型如下:

BOOL WINAPI ChangeServiceConfig(
  __in          SC_HANDLE hService,
  __in          DWORD dwServiceType,//服务类型
  __in          DWORD dwStartType,//启动类型
  __in          DWORD dwErrorControl,
  __in          LPCTSTR lpBinaryPathName,//服务的主程序路径
  __in          LPCTSTR lpLoadOrderGroup,
  __out         LPDWORD lpdwTagId,
  __in          LPCTSTR lpDependencies,//依赖项
  __in          LPCTSTR lpServiceStartName,//服务名称
  __in          LPCTSTR lpPassword,//服务密码,主要用于控制服务
  __in          LPCTSTR lpDisplayName//服务的显示名称
);

函数中传递的都是服务的新信息,如果希望改变则填入相应的值,如果不想改变则对于DWORD类型的成员来说填入SERVICE_NO_CHANGE,对于指针类型的只需要填入NULL即可。

创建服务

创建服务主要使用函数CreateService,该函数的原型如下:

SC_HANDLE WINAPI CreateService(
  __in          SC_HANDLE hSCManager,
  __in          LPCTSTR lpServiceName,//服务名称
  __in          LPCTSTR lpDisplayName,//显示名称
  __in          DWORD dwDesiredAccess,//服务权限
  __in          DWORD dwServiceType,//服务类型
  __in          DWORD dwStartType,//服务启动类型
  __in          DWORD dwErrorControl,
  __in          LPCTSTR lpBinaryPathName,//主程序路径
  __in          LPCTSTR lpLoadOrderGroup,
  __out         LPDWORD lpdwTagId,
  __in          LPCTSTR lpDependencies,//依赖项
  __in          LPCTSTR lpServiceStartName,启动名称
  __in          LPCTSTR lpPassword//密码
);
在启动时需要填入一些信息系统的服务控制管理器保存这些信息,并根据其中的某些信息来启动这个服务,有的选项是必填的,比如服务名称,这个是用来唯一标识一个服务的,服务所在路径告知服务控制管理器启动哪个程序,而向依赖、密码等等信息可以不用填写。
下面是调用的例子
```cppp
    SC_HANDLE hRet = ::CreateService(h_SCManager, lpServiceName, lpDisplayName,     dwDesiredAccess, SERVICE_WIN32_OWN_PROCESS, dwStartType, SERVICE_ERROR_NORMAL,
        lpBinaryPathName, NULL, NULL, lpDependencies, lpServiceStartName, lpPassword);

<div class="se-preview-section-delimiter"></div>

SERVICE_WIN32_OWN_PROCESS表示服务类型是win32类型拥有独立进程的服务

SERVICE_ERROR_NORMAL表示服务程序返回的错误码是系统默认的错误码

删除服务

删除服务使用的函数是DeleteService,这个函数主要传入的是服务的句柄,这个句柄是由函数OpenService返回的。另外需要注意的是这个函数只对已停止的服务起作用,所以在删除之前需要将服务停止。

“`

SERVICE_WIN32_OWN_PROCESS表示服务类型是win32类型拥有独立进程的服务
SERVICE_ERROR_NORMAL表示服务程序返回的错误码是系统默认的错误码
## 删除服务
删除服务使用的函数是DeleteService,这个函数主要传入的是服务的句柄,这个句柄是由函数OpenService返回的。另外需要注意的是这个函数只对已停止的服务起作用,所以在删除之前需要将服务停止。
```cpp
BOOL bRet = FALSE;
    DWORD dwSrvAcceptCtrl = GetSrvCtrlAccept(pIter->pNode->strSrvName);
    if(0 == (dwSrvAcceptCtrl & SERVICE_ACCEPT_STOP))            //服务不能被删除
    {
        goto __RETURN;
    }
    //停止服务,函数CtrlService是我之前封装的用于控制服务。
    bRet = CtrlService(pIter, SERVICE_CONTROL_STOP);
    if(!bRet)
    {
        goto __RETURN;
    }

    bRet = ::DeleteService(h_SCService);
    if (bRet)
    {
        DeleteItem(pIter->pNode);
    }
__RETURN:
    CloseServiceHandle(h_SCService);
    CloseServiceHandle(h_SCManager);
    return bRet;
时间: 2024-10-10 08:33:52

windows服务管理操作的相关文章

关于windows服务的操作

/// <summary> /// 判断是否安装了某个服务 /// </summary> /// <param name="serviceName"></param> /// <returns></returns> public static bool ISWindowsServiceInstalled(string serviceName) { try { ServiceController[] services

C#开发可以可视化操作的windows服务

使用C#开发自定义windows服务是一件十分简单的事.那么什么时候,我们需要自己开发windows服务呢,就是当我们需要计算机定期或者一直执行我们开发的某些程序的时候.这里我以一个WCF的监听服务为例,因为我是做一个局域聊天室,需要服务器端监听终端,所以我就开发了一个服务,以便控制此监听服务.然而,我们开发的windows服务,默认情况下是无法可视化的操作的,这里我就额外的开发一个工具来对此服务进行操作,效果图如下: 开发步骤: 1."新建项目"--"Window服务&qu

(转)为C# Windows服务添加安装程序

本文转载自:http://kamiff.iteye.com/blog/507129 最近一直在搞Windows服务,也有了不少经验,感觉权限方面确定比一般程序要受限很多,但方便性也很多.像后台运行不阻塞系统,不用用户登录之类.哈 哈,扯远了,今天讲一下那个怎么给Windows服务做个安装包.为什么做安装包?当然是方便了,不用每次调用InstallUtil,还有,就是看上去 正规些. 不多说了,先来看看怎么做吧.首先,当然是创建一个Windows服务的项目.这个大家应该都知道怎么做(这都不明白的留

RDIFramework.NET框架SOA解决方案(集Windows服务、WinForm形式与IIS形式发布)-分布式应用

RDIFramework.NET框架SOA解决方案(集Windows服务.WinForm形式与IIS形式发布)-分布式应用 RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,给用户和开发者最佳的.Net框架部署方案.该框架以SOA范式作为指导思想,作为异质系统整合与互操作性.分布式应用提供了可行的解决方案. 1.SOA平台简介 1.1.概述 SOA(service-oriented architecture,也叫面向服务的体系结构或面向服务架构)是指为了解决在Inte

C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(上)

译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(上)),不对的地方欢迎指出与交流. 章节出自<Professional C# 6 and .NET Core 1.0>.水平有限,各位阅读时仔细分辨,唯望莫误人子弟. 附英文版原文:Professional C# 6 and .NET Core 1.0 - Chapter 39 Windows Services --------------------------------

为C# Windows服务添加安装程序

最近一直在搞Windows服务,也有了不少经验,感觉权限方面确定比一般程序要受限很多,但方便性也很多.像后台运行不阻塞系统,不用用户登录之类.哈哈,扯远了,今天讲一下那个怎么给Windows服务做个安装包.为什么做安装包?当然是方便了,不用每次调用InstallUtil,还有,就是看上去正规些. 不多说了,先来看看怎么做吧.首先,当然是创建一个Windows服务的项目.这个大家应该都知道怎么做(这都不明白的留言问我),然后要给服务“添加安装程序”,如图1所示:(这一步和自己用InstallUti

MongoDB安装为Windows服务方法与注意事项

本文出处:http://blog.csdn.net/chaijunkun/article/details/7227967,转载请注明.由于本人不定期会整理相关博文,会对相应内容作出完善.因此强烈建议在原始出处查看此文. MongoDB作为一个基于分布式文件存储的数据库,近两年大受追捧.数据灵活的存取方式和高效的处理使得它广泛用于互联网应用. 最近本人开始在Windows 32位平台下研究MongoDB的使用,为了方便,本人更喜欢将其安装为Windows的系统服务.这样就不用每次开一个mongod

C# Windows服务开发和安装

Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面.这使服务非常适合在服务器上使用,或任何时候,为了不影响在同一台计算机上工作的其他用户,需要长时间运行功能时使用.还可以在不同于登录用户的特定用户帐户或默认计算机帐户的安全上下文中运行服务. Windows服务管理器 在Windows系统中,你可以通过在命令行中输入service

如何在网页启动Windows服务

由于公司有许多windows服务进行业务的处理,所谓对服务的维护也是一个比较头痛的问题,因为自己也不知道服务什么时候自动停了,而 且更主要的原因是服务都是由运维部门在维护管理,开发这边没有直接操作服务的权限,所以利用网页监控服务就很重要了(在服务中也可以增加邮件提醒,比如服 务报错或者断掉). 如何获取电脑上的所有服务: 使用.Net框架类库中自带的组件ServiceController可以实现对 Windows服务的操作. 1.首先要在你的项目添加引用System.ServiceProcess