首页
归档
友情链接
关于
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结构
页面
归档
友情链接
关于
搜索到
2
篇与
的结果
2017-09-11
Windows服务框架与服务的编写
从NT内核开始,服务程序已经变为一种非常重要的系统进程,一般的驻守进程和普通的程序必须在桌面登录的情况下才能运行,而许多系统的基础程序必须在用户登录桌面之前就要运行起来,而利用服务,可以很方便的实现这种功能,而且服务程序一般不予用户进行交互,可以安静的在后台执行,合理的利用服务程序可以简化我们的系统设计,比如Windows系统的日志服务,IIS服务等等。服务程序本身是依附在某一个可执行文件之中,系统将服务安装在注册表中的HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services位置,当需要执行服务程序时,由系统的服务控制管理器在注册表中对应的位置读取服务信息,并启动对应的程序。下面从几个方面详细说明服务程序的基本框架服务程序的框架服务程序本身也是依附在exe或者dll文件中,一般一个普通的可执行文件中可以包含一个或者多个服务,但是为了代码的维护性,一般一个程序总是只包含一个服务。服务程序是由服务管理器负责调度,控制的,所以我们在编写服务程序的时候必须满足服务控制管理器的调度,必须包含:立即调用StartServiceCtrlDispatchar函数把进程的主线程连接到ServiceControlManager的主函数在进程中运行的各个服务的入口点函数ServiceMain在进程中运行的各个服务的控制处理函数HandlerServiceControlManager函数的原型如下:BOOL WINAPI StartServiceCtrlDispatcher( __in const SERVICE_TABLE_ENTRY* lpServiceTable );函数参数是一个SERVICE_TABLE_ENTRY类型的指针,这个类型的定义如下:typedef struct _SERVICE_TABLE_ENTRY { LPTSTR lpServiceName; LPSERVICE_MAIN_FUNCTION lpServiceProc; } SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;这个结构是一个服务名称和对应入口函数指针的映射。在传入的时候必须给一个该类型的数组,数组的每一项都代表一个服务与其入口函数指针的映射,同时这个数组的最后一组必须为NULL当启动服务的时候,系统会启动对应的进程,当进程代码执行到StartServiceCtrlDispatcher时,程序由服务控制管理器接管,服务控制管理器根据需要启动的服务名称,在传入的数组指针中,找到对应的入口函数,然后调用它,当对应的入口函数返回时结束服务,并将后续代码的控制权转交给对应主进程,由主进程接着执行后面的代码在入口函数中我们必须给服务一个控制管理程序,这个程序主要是用来处理服务程序接受到的各种控制消息,比如启动服务,暂停服务,停止服务等,这个函数有点类似于Windows 窗口程序中的窗口过程。这个函数由我们自己编写,然后调用函数RegisterServiceCtrlHandler(Ex) 将服务名称与对应的控制函数绑定,每当有一个控制事件发生时都会调用我们注册的函数进行处理,RegisterServiceCtrlHandler函数会返回一个句柄,作为服务的控制句柄。当我们要自己向服务控制管理器报告服务的当前状态时需要这个句柄。服务的启动过程已经安装的服务,被系统存储在注册表的HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services位置处,这个注册表项纪录了服务所依赖的exe或者dll文件,它的启动类型等信息,当我们尝试启动服务的时候,系统会在注册表的对应位置查找是否存在对应服务的表项,如果存在则启动对应的进程。当进程的代码执行到StartServiceCtrlDispatcher函数时,该进程将由服务控制管理器接管,服务控制管理器将会根据填入的SERVICE_TABLE_ENTRY,找到服务所对应的入口函数开启对应的服务线程并调用,在入口函数处会注册一个控制句柄,然后应该向服务控制管理程序报告当前状态为正在启动,然后执行服务的正式代码。(注意:由于服务的入口函数需要自己编写,所以这里提到的注册控制句柄,报告状态都应该是由程序员自己编写代码实现)Handler函数handler函数用来处理服务的控制请求,这个函数由RegisterServiceCtrlHandler(Ex)函数注册到系统,当服务控制请求到来时,由服务的主线程的控制分发线程来调用。综合上面的内容,可以看到一个服务程序应该是至少涉及到3个线程,进程的主线程,服务线程,控制分发线程,RegisterServiceCtrlHandler(Ex)的原型如下:SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerEx( __in LPCTSTR lpServiceName, __in LPHANDLER_FUNCTION_EX lpHandlerProc, __in_opt LPVOID lpContext );不带Ex的版本只有前两个参数,带Ex版本的第3个参数是一个传入到对应的控制函数中的参数。对应提供的控制管理函数的原型如下:DWORD WINAPI HandlerEx( __in DWORD dwControl, __in DWORD dwEventType, __in LPVOID lpEventData, __in LPVOID lpContext );第一个参数是一个控制码,类似于GUI程序中的消息,根据这个控制码就可以知道对应的控制消息,下面列举常见的控制码:控制码含义SERVICE_CONTROL_STOP请求服务停止SERVICE_CONTROL_PAUSE请求暂停服务SERVICE_CONTROL_CONTINUE请求恢复暂停的服务SERVICE_CONTROL_INTERROGATE请求服务立即更新它的当前状态信息给服务控制管理程序SERVICE_CONTROL_SHUTDOWN请求服务执行清理任务,因为系统正在关机.由于只有非常有限的时间用来关机,所以这个控制只应由绝对需要关机的服务使用.例如:事件登录服务需要清理维护的文件中的脏字节,或服务需要关机以便当系统在关机状态时网络连接不能进行. 如果服务关键要花时间,并发出STOP_PENDING状态信息,强烈建议这些消息包括一个等待提示使得服务控制程序知道在给系统指明服务关机完成之前要等多长时间.系统给服务控制管理器有限的时间(约20秒)完成服务关机,在这个时间后无论服务关机动作是否完成都进行系统关机第二个参数是事件类型,对于有的控制码,它可能含有子控制类型来详细描述它,就好像WM_COMMAND消息中有子控件的相关消息第三个参数是事件参数,这个参数是子控制码对应的参数第四个参数是上面带Ex的函数第三个参数传进来的内容每次Handler函数被调用时,服务必须调用SetServiceStatus函数把状态报告给服务管理器程序注意:即使状态无变化也要报告服务控制管理器在服务中一般有3类对象(在这并不是指Windows系统的内核对象,这里只是为了便于理解给出的一个分类):服务程序对象:服务本身的代码,一般是服务主要完成的功能代码服务控制对象:用来控制服务,向服务发送执行服务管理对象:用来响应对应的控制码,主要是指服务的handler函数与GUI程序相类比,服务对象就好比GUI程序本身,服务控制对象就好像我们在操作GUI程序,比如点击鼠标,而服务控制对象就像窗口的窗口过程服务管理器由SCManager对象代表。SCManager对象是持有服务对象的容器对象。SCManager对象和服务对象的句柄类型是SC_HANDLE。我们可以使用函数OpenService来在服务管理器中打开对应服务获取服务对象的句柄,或者使用函数CreateService在服务管理器中创建一个新服务并返回服务的句柄后面关于服务的控制操作请参考本人之前写的一篇关于服务控制管理器的编写的博客点击这里下面通过一个封装的Service库来说明服务程序的框架。这个简单的类的详细代码请点击这里下载该项目中主要定义了三个类,其中CFSZService类是所有服务类的基类,CServiceCtrl是服务的控制类,该类用于控制服务,这个类中的所有函数都是静态函数。另外为了测试我从CFSZService类上派生了一个类——CTestService,用来编写服务的具体代码。如果以后想要使用这个项目中的代码,可以进行如下操作:FSZService类中派生一个新类,并重载基类的RunService,在这个服务中编写具体的服务代码即可在相应位置调用DECLARE_SERVICE_TABLE_ENTRY宏,用来声明一个SERVICE_TABLE_ENTRY变量,用来绑定服务和对应的入口函数在相应位置添加代码:IMPLAMENT_SERVICE_MAIN(GetSystemInfoService, CTestService) BEGIN_SERVICE_MAP() ON_SERVICE_MAP(GetSystemInfoService, CTestService) END_SERIVCE_MAP()第一个宏用来定义了一个函数,该函数是服务的入口函数,需要传入服务名称,服务的类名称。第二个宏用来将服务名和它对应的入口函数进行绑定。在主函数处调用CFSZService::RegisterService(),在该函数里面会调用StartServiceCtrlDispatcher,一遍让服务控制管理程序来接管服务代码代码的整体说明服务基类的定义如下:class CFSZService { public: typedef CAtlMap<CString, CFSZService *> CFSZServiceMap; //服务名称和对应的服务对象 CFSZService(const CString& csSrvName); ~CFSZService(void); virtual DWORD Run(DWORD dwArgc, LPTSTR* lpszArgv); virtual BOOL OnInitService(DWORD dwArgc, LPTSTR* lpszArgv); //初始化服务 virtual DWORD RunService(); //运行服务 void SetServiceStatusHandle(SERVICE_STATUS_HANDLE); static DWORD WINAPI HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext); static BOOL RegisterService(); //服务命令处理函数 protected: virtual DWORD OnStop(); virtual DWORD OnUserControl(DWORD dwControl); virtual DWORD OnStart(); virtual DWORD OnContinue(); virtual DWORD OnPause(); virtual DWORD OnShutdown(); virtual DWORD OnInterrogate(); virtual DWORD OnShutDown(); protected://设备变更事件通知处理 SERVICE_CONTROL_DEVICEEVENT virtual DWORD OnDeviceArrival(PDEV_BROADCAST_HDR pDbh){return 0;} virtual DWORD OnDeviceRemoveComplete(PDEV_BROADCAST_HDR pDbh){return 0;} virtual DWORD OnDeviceQueryRemove(PDEV_BROADCAST_HDR pDbh){return 0;} virtual DWORD OnDeviceQueryRemoveFailed(PDEV_BROADCAST_HDR pDbh){return 0;} virtual DWORD OnDeviceRemovePending(PDEV_BROADCAST_HDR pDbh){return 0;} virtual DWORD OnCustomEvent(PDEV_BROADCAST_HDR pDbh){return 0;} protected://硬件配置文件发生变动 SERVICE_CONTROL_HARDWAREPROFILECHANGE virtual DWORD OnConfigChanged(){return 0;} virtual DWORD OnQueryChangeConfig(){return 0;} virtual DWORD OnConfigChangeCanceled(){return 0;} protected://设备电源事件 SERVICE_CONTROL_POWEREVENT virtual DWORD OnPowerSettingChange(PPOWERBROADCAST_SETTING pPs){return 0;} protected://session 发生变化 SERVICE_CONTROL_SESSIONCHANGE virtual DWORD OnWTSConsoleConnect(PWTSSESSION_NOTIFICATION pWn){return 0;} virtual DWORD OnWTSConsoleDisconnect(PWTSSESSION_NOTIFICATION pWns){return 0;} virtual DWORD OnWTSRemoteConnect(PWTSSESSION_NOTIFICATION pWns){return 0;} virtual DWORD OnWTSRemoteDisconnect(PWTSSESSION_NOTIFICATION pWns){return 0;} virtual DWORD OnWTSSessionLogon(PWTSSESSION_NOTIFICATION pWns){return 0;} virtual DWORD OnWTSSessionLogoff(PWTSSESSION_NOTIFICATION pWns){return 0;} virtual DWORD OnWTSSessionLock(PWTSSESSION_NOTIFICATION pWns){return 0;} virtual DWORD OnWTSSessionUnLock(PWTSSESSION_NOTIFICATION pWns){return 0;} virtual DWORD OnWTSSessionRemoteControl(PWTSSESSION_NOTIFICATION pWns){return 0;} protected: //内部的工具方法,设置服务为一个指定的状态 BOOL SetStatus(DWORD dwStatus,DWORD dwCheckPoint = 0,DWORD dwWaitHint = 0 ,DWORD dwExitCode = 0,DWORD dwAcceptStatus = SERVICE_CONTROL_INTERROGATE); BOOL SetStartPending(DWORD dwCheckPoint = 0,DWORD dwWaitHint = 0); //设为正在启动状态 BOOL SetContinuePending(DWORD dwCheckPoint = 0,DWORD dwWaitHint = 0); //设为正在继续运行状态 BOOL SetPausePending(DWORD dwCheckPoint = 0,DWORD dwWaitHint = 0); //设为正在暂停状态 BOOL SetPause(); //设为暂停状态 BOOL SetRunning(); //设为以启动状态 BOOL SetStopPending(DWORD dwCheckPoint = 0,DWORD dwWaitHint = 0); //设为正在停止状态 BOOL SetStop(DWORD dwExitCode = 0); //设为以停止状态 BOOL ReportStatus(DWORD, DWORD, DWORD);//向服务管理器报告当前服务状态 protected: CString m_csSrvName; //服务名称 DWORD m_dwCurrentStatus; //当前状态 SERVICE_STATUS_HANDLE m_hCtrl; //控制句柄 public: static CFSZServiceMap ms_SrvMap; };在这个基类中主要定义了3类函数,分别是:服务本身的代码函数:用来处理服务的业务,实现服务的功能服务控制管理函数:包括各种控制消息的响应函数和服务控制句柄的管理函数服务状态设置函数:主要用来设置服务的状态该项目使用Atl 和CString,一般在控制台程序中想要使用这二者只需要包含头文件:atlcoll.h、atlstr.h即可CFSZServiceMap 成员该成员是用来将服务名称和对应的类对象关联起来,这样以后根据服务名称就可以找到对应的服务类的对象指针,该类型定义如下:typedef CAtlMap<CString, CFSZService *> CFSZServiceMap;在每个类的构造函数中进行初始化:CFSZService::CFSZService(const CString& csSrvName) { m_csSrvName = csSrvName; ms_SrvMap.SetAt(m_csSrvName, this); }服务的入口函数服务的入口函数是利用宏定义的一个函数,每当需要添加一个服务的时候都需要调用宏IMPLAMENT_SERVICE_MAIN来定义一个对应的服务入口ServiceMain,该函数的定义如下:#define IMPLAMENT_SERVICE_MAIN(srvName, className)\ VOID WINAPI _ServiceMain_##className(DWORD dwArgc, LPTSTR* lpszArgv)\ {\ CFSZService *pThis = NULL;\ if(!CFSZService::ms_SrvMap.Lookup(_T(#srvName), pThis))\ {\ pThis = dynamic_cast<CFSZService*>( new className(_T(#srvName)) );\ }\ else\ {\ return;\ }\ assert(NULL != pThis);\ SERVICE_STATUS_HANDLE hss = RegisterServiceCtrlHandlerEx(_T(#srvName), CFSZService::HandlerEx, reinterpret_cast<LPVOID>(pThis));\ assert(NULL != hss);\ pThis->SetServiceStatusHandle(hss);\ pThis->Run(dwArgc, lpszArgv);\ delete dynamic_cast<##className*>(pThis);\ }上面的代码首先根据传入的类名动态创建了一个服务类(由于这里服务对象都是动态创建和销毁的,所以在其他地方不需要创建服务对象),然后调用RegisterServiceCtrlHandlerEx构造了一个服务控制句柄,然后调用类的SetServiceStatusHandle函数来将对应的服务控制句柄保存起来最后调用Run函数来运行服务的正式代码,最后当Run函数执行完毕后,服务的相应工作也做完了,这个时候删除了这个类。Run函数的定义如下:DWORD CFSZService::Run(DWORD dwArgc, LPTSTR* lpszArgv) { assert(NULL != this); if (OnInitService(dwArgc, lpszArgv)) { RunService(); } return 0; }这个函数中使用了OnInitService函数来进一步初始化服务相关信息,该函数提供了一个服务初始化的时机。比如调用相关函数进行socket的初始化或者对com环境进行初始化等等。然后调用RunService执行服务正式的代码。HandlerEx函数在前面的宏IMPLAMENT_SERVICE_MAIN中调用了RegisterServiceCtrlHandlerEx将函数HandlerEx作为服务控制码的处理函数,调用的时候将服务类对象的指针通过第四个参数传入,这样在静态函数中就可以使用服务的类成员函数,函数HandlerEx的部分代码如下: DWORD dwRet = ERROR_SUCCESS; if( NULL == lpContext ) { return ERROR_INVALID_PARAMETER; } CFSZService*pService = reinterpret_cast<CFSZService*>(lpContext); if( NULL == pService ) { return ERROR_INVALID_PARAMETER; } switch(dwControl) { case SERVICE_CONTROL_STOP: //0x00000001 停止服务器 { dwRet = pService -> OnStop(); } break; ... }在该函数中,将所有的控制吗都列举出来,针对不同的控制吗都调用的对应的处理函数,并且这些函数都是虚函数,所以在派生类中需要处理某个控制消息就重写某个对应的函数即可。最后再重新屡一下这个类在调用时的基本情况:在主函数中调用CFSZService::RegisterService();函数将之前我们通过一组BEGIN_SERVICE_MAP、ON_SERVICE_MAP、END_SERVICE_MAP组成的映射关系注册到系统的服务控制管理器中。这个函数单独调用了StartServiceCtrlDispatcher函数,一旦代码执行到这个地方,服务控制管理器会根据之前绑定的服务名称与入口函数的对应关系调用对应的入口函数入口函数是通过宏IMPLAMENT_SERVICE_MAIN定义的,在入口函数中首先动态创建了一个服务类,然后给这个服务注册服务控制句柄,并且服务控制函数为HandlerEx。接着,服务的入口函数调用对应服务的Run函数,在Run函数中调用OnInitService进行服务的初始化和调用RunService执行服务的正式代码,所以在重载类中可以重载这两个方法进行初始化和进行服务的相关操作当外部对服务进行控制时,服务控制管理器调用HandleEx函数进行相关的操作在HandleEx中会解析对应的控制事件,并调用对应的虚函数,所以如果想要处理某个消息,则重写对应的控制函数即可
2017年09月11日
4 阅读
0 评论
0 点赞
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 点赞