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;
windows服务管理操作的更多相关文章
- windows 服务管理器使用系统内置帐户时密码的输入
windows 服务管理器使用系统内置帐户时在选择帐户如network services后不需要输入密码,直接确认即可,系统会自动附加密码.
- C#对Windows服务的操作
一.安装服务: private void InstallService(IDictionary stateSaver, string filepath) { try { System.ServiceP ...
- c# Windows服务管理
.NET Framework中提供的类库可以很方便的实现对windows服务的安装.卸载.启动.停止.获取运行状态等功能.这些类都在System.ServiceProcess命名空间下. 所以,在开始 ...
- 关于windows服务的操作
/// <summary> /// 判断是否安装了某个服务 /// </summary> /// <param name="serviceName"& ...
- Windows服务管理
按键:win+R 输入:services.msc “服务和应用程序”界面选项打开 * sc命令的使用:create(创建) delete(删除)等 * service可执行文件路径的修改:win+R ...
- windows服务管理TopShelf
http://docs.topshelf-project.com/en/latest/index.html 我曾经把名称错为Topself
- C#开发可以可视化操作的windows服务
使用C#开发自定义windows服务是一件十分简单的事.那么什么时候,我们需要自己开发windows服务呢,就是当我们需要计算机定期或者一 直执行我们开发的某些程序的时候.我经常看到许多人开发的win ...
- 【C#】开发可以可视化操作的windows服务
使用C#开发自定义windows服务是一件十分简单的事.那么什么时候,我们需要自己开发windows服务呢,就是当我们需要计算机定期或者一直执行我们开发的某些程序的时候.这里我以一个WCF的监听服务为 ...
- C#开发人员能够可视化操作windows服务
使用C#开发自己的定义windows服务是一个很简单的事.因此,当.我们需要发展自己windows它的服务.这是当我们需要有定期的计算机或运行某些程序的时候,我们开发.在这里,我有WCF监听案例,因为 ...
随机推荐
- 关于系统首页绘制问题(ext布局+c#后台加入数据)经html输出流输出响应client
关于系统首页绘制问题,业务需求 TODO 绘制系统首页(Main.aspx) 採用的技术:functioncharts+jquery+ext布局+c#+html 解说篇:1,服务端aspx,2,服务端 ...
- HDU 4162 Shape Number(字符串,最小表示法)
HDU 4162 题意: 给一个数字串(length <= 300,000),数字由0~7构成,求出一阶差分码,然后输出与该差分码循环同构的最小字典序差分码. 思路: 第一步是将差分码求出:s[ ...
- ImageLoader配置(凝视)
/** * 配置ImageLoader */ private void configImageLoader() { File discCacheDir = StorageUtils.getOwnCac ...
- 搭建angular1 gulp项目(上传到gitup)
(安装好相关的前端环境) 1.新建一个文件夹,名字为angular-gulp,dos命令切换到该目录,输入npm init,继续添上你需要的信息,ok之后目录中多了package.json(管理项目所 ...
- es6+require混合开发,兼容es6 module,import,export之 加载css及公用date-main
大家好!上篇文章已经介绍了搭建文件夹,以及加载js文件.现在讲一下加载css ,以及对baseUrl的理解 1.对项目结构的认知 一个项目的结构是根据项目的架构来决定的,当然也可以做到更智能,但是意义 ...
- IDEA引MAVEN项目jar包依赖导入问题解决
Intellj 自动载入Mave依赖的功能很好用,但有时候会碰到问题,导致pom文件修改却没有触发自动重新载入的动作,此时需要手动强制更新依赖. 如下: 1手动删除Project Settings里面 ...
- Python 项目实践一(外星人入侵小游戏)第五篇
接着上节的继续学习,在本章中,我们将结束游戏<外星人入侵>的开发.我们将添加一个Play按钮,用于根据需要启动游戏以及在游戏结束后重启游戏.我们还将修改这个游戏,使其在玩家的等级提高时加快 ...
- OpenCASCADE构造一般曲面
OpenCASCADE构造一般曲面 eryar@163.com Abstract. 本文主要介绍常见的曲面如一般柱面(拉伸曲面).旋转面在OpenCASCADE中的构造方法,由此思考一般放样算法的实现 ...
- HTML5 给图形绘制阴影(绘制五角星示例)
几个属性 shadowOffsetX:阴影的横向位移量. shadowOffsetY:阴影的纵向位移量. shadowColor:阴影的颜色. shadowBlur:阴影的模糊范围. 属性说明 sha ...
- 理解Kubernetes(1):手工搭建Kubernetes测试环境
系列文章: 1. 手工搭建环境 1. 基础环境准备 准备 3个Ubuntu节点,操作系统版本为 16.04,并做好以下配置: 系统升级 设置 /etc/hosts 文件,保持一致 设置从 0 节点上无 ...