首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
80 阅读
2
nvim番外之将配置的插件管理器更新为lazy
58 阅读
3
2018总结与2019规划
54 阅读
4
PDF标准详解(五)——图形状态
33 阅读
5
为 MariaDB 配置远程访问权限
30 阅读
心灵鸡汤
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
archlinux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
菜谱
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
登录
Search
标签搜索
c++
c
学习笔记
windows
文本操作术
编辑器
NeoVim
Vim
win32
VimScript
Java
emacs
linux
文本编辑器
elisp
反汇编
OLEDB
数据库编程
数据结构
内核编程
Masimaro
累计撰写
308
篇文章
累计收到
27
条评论
首页
栏目
心灵鸡汤
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
archlinux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
菜谱
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
页面
归档
友情链接
关于
搜索到
1
篇与
的结果
2016-07-16
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函数是QueryServiceConfig2BOOL 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时第三个参数为NULLControlService 控制服务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//密码 ); 在启动时需要填入一些信息系统的服务控制管理器保存这些信息,并根据其中的某些信息来启动这个服务,有的选项是必填的,比如服务名称,这个是用来唯一标识一个服务的,服务所在路径告知服务控制管理器启动哪个程序,而向依赖、密码等等信息可以不用填写。 下面是调用的例子SC_HANDLE hRet = ::CreateService(h_SCManager, lpServiceName, lpDisplayName, dwDesiredAccess, SERVICE_WIN32_OWN_PROCESS, dwStartType, SERVICE_ERROR_NORMAL, lpBinaryPathName, NULL, NULL, lpDependencies, lpServiceStartName, lpPassword);SERVICE_WIN32_OWN_PROCESS表示服务类型是win32类型拥有独立进程的服务 SERVICE_ERROR_NORMAL表示服务程序返回的错误码是系统默认的错误码 ## 删除服务 删除服务使用的函数是DeleteService,这个函数主要传入的是服务的句柄,这个句柄是由函数OpenService返回的。另外需要注意的是这个函数只对已停止的服务起作用,所以在删除之前需要将服务停止。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;
2016年07月16日
5 阅读
0 评论
0 点赞