windows服务程序的编写

上传人:ba****u 文档编号:174119367 上传时间:2022-12-14 格式:DOCX 页数:34 大小:25.70KB
返回 下载 相关 举报
windows服务程序的编写_第1页
第1页 / 共34页
windows服务程序的编写_第2页
第2页 / 共34页
windows服务程序的编写_第3页
第3页 / 共34页
点击查看更多>>
资源描述
摘要:几乎所有的操作系统在启动的时候都会启动一些不需要与用户交互的进程,这些进 程在Windows中就被称作服务。它由服务程序、服务控制程序(SCP, service control program)和服务控制管理器(S CM, service control manager)三个组件构成。本文针 对服务程序与服务控制程序的编写进行综合讲述。关键词:Windows,服务,VC+?1 服务介绍 几乎所有的操作系统在启动的时候都会启动一些不需要与用户交互的进程,这些进程在Windows中就被称作服务。它通常用于实现客户/服务器模式中的服务器方,如我们常见的 Web服务IIS,当操作系统在启动后它就自动被运行,不管是否有人登陆到系统只要系统开 启它就能得到运行。服务程序、服务控制程序(SCP,service control program)和服务控制管理器(SCM,service con trol manager )组成了 Windows服务。我们可以通过服务控制程序操纵服务控制管理器 来配置、启动、暂停、停止服务程序。其中服务程序和服务控制程序可以由我们自己来编 写扩展,而服务控制管理器(windowssystem32servics.exe)则是操作系统内置的一个 部件。首先我们来了解一下SCM的工作情况,然后我们介绍服务程序的编写和服务控制 时所涉及API的使用。?2服务控制管理器SCM本身也是一个服务程序(windowssystem32servics.exe),作为windows的后台服务 运行的。Winlogon在系统引导的早期会将SCM启动起来。SCM的服务入口函数首先创建一个 初始化为无信号的同步事件对象(SvcCtrlEvent_A3752DX);接下来,它开始建立一个内 部服务数据库,这个数据库要按事先规定好的一个顺序列出所有服务组,并记录与服务相 关的详细信息;当这个数据库建立完成时SCM就开始按顺序启动那些启动方式为自动的服 务,如果有服务要动行于指定用户账户中时还要调用LSASS,如果服务启动失败则会被放入 一个名为ScFailedDrivers的列表中。当这些工作都完成后,SCM将同步事件对象 SvcC trlEven t_ A3752DX置为有信号状态;并做好系统停机的准备。当系统要关机时会向Windows子系统进程Csrss发送一个消息,以便调用Csrss的停机例程。 Csrss会对所有活动的进程循环通知系统正在停机。对于除SCM以外的每一个系统进程如果 没有返回退出的响应Csrss都会等待由HKEY_USER.DEFAULTControlPanelDesktopWaitToKillAppTimeout指定的毫秒数(我的系统中是20000,也就是20秒), 然后知通下一个进程结束。当遇到SCM时也会通知SCM进程系统正在停机,但不同的是会使 用一个专用的超时间隔值(SCM在系统初始化时要向Csrss登记,于是Csrss就将SCM的进程ID 保存了下来Csrss也就是通过个ID来识别SCM的)。这个专用的超时间隔位于 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlWaitToKillServiceTimeout 中,有趣的是默认值也是20秒。这在段时间里SCM要通知所有在初始化时(服务程序自身初 始化)请求SCM通知自己系统停机的服务,SCM有一个停机处理器负责这项工作。通知下达 后SCM就等待服务退出,服务在接收到停机消息后会返回给SCM一个等待时间,SCM跟踪所有 服务返回等待时间找出其中的最大值,当这个最大值达到后,如果有一个服务或多个服务 又告诉SCM它们正在处理停机工作,那么SCM还会循环等待下去,但Csrss也再等待SCM当 Csrss等待超时后,会继续后面的停机工作最终完成停机。这就要求服务程序要在Csrss等待SCM的这段时内完成自己的停机处理工作,否则服务就没 机会在系统停机前完成自己的关闭工作了。?3 服务程序Windows服务程序其实并不神秘,它只是遵循特定规则编写的一个程序。只要遵循这个特定 的规则与服务控制管理器正确的交互,就可实现我们的服务程序。而我们只要能实现一个 简单的服务程序,设计一个能处理复杂业务的服务也并非难事,因为从结构上看两者并没 有太大的区别。只要遵循与SCM交互的规则,设计服务程序与设计普通的应用程序几乎没什 么区别。?3.1 程序结构概要服务程序的与普通应用程序一样也需要一个主函数(main()作为程序的入口,与之 不同的是作为一个服务程序它需要在主函数(main()中立即调用Start ServiceC trlDispa tcher来注册一个服务的入口函数(S erverMain(DWORD argc,LPTSTR *argv),当然这个名字可自由命名)。St ar tServiceC trlDispa tcher 函数的原型是:BOOL St ar tServiceC trlDispa tcher(LPSERVICE_TABLE_ENTRY lpServiceStartTable );它的参数是一个指向SERVICE_TABLE_ENTRY的指针;SERVICE_TABLE_ENTRY结构有两个 域;第一个域存储服务的内部名称,第二个域是服务入口函数的指针。这个函数完成后,SCM就要可以服务启动的时候调用服务的入口函数。例如:管理员在服务管理器启动一个服务,SCM就会在一个单独的线程中调用服务注册 的入口函数。这时我们在服务的这个入口函数中必须调用Regis terServiceC trlHandler完 成Handler函数的注册,这个函数用来接收和处理SCM的控制消息。下面列出Hander要处理 的控制消息和Regis terServiceC trlHandler的函数原型:VOID WINAPI Handler( DWORD fdwControl / 请求控制消息代码);控制消息宏定义说明SERVICE_CONTROL_STOP要服务停止SERVICE_CONTROL_PAUSE要服务暂停SERVICE_CONTROL_CONTINUE要服务继续SERVICE_CONTROL_INTERROGATE要服务马上报告它的状态SERVICE_CONTROL_SHUTDOWN告诉服务即将关机SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler( LPCTSTR lpServiceName,/ 服务的内部名称 LPHANDLER_FUNCTION lpHandlerProc / Handler函数的地址); Regis terServiceC trlHandler调用完成后我们就可以开始我们的业务处理的初始化工作。 初始化完成后向SCM报告服务开始运行(SERVICE_RUNNING )的消息。如果S erverMain(DWORD argc,LPTSTR *argv)函数退出服务也就停止了。下面让我总结一下实 现服务程序的步骤:(1 )在1讪()调用Start ServiceC trlDispa tcher来注册一个服务的 入口函数;(2)在ServerMain(DWORD argc,LPTSTR *argv)中调用Regis terServiceC trlHandler注册H andler函数。(3)完成业务处理程序的初始化工作, 如果初始化时间较长要实时向SCM报告当前正在启动(4)初始化完毕,报告服务正在 运行;开始业务处理工作。 ?3.2 程序实例分析(1) main()函数 int main(int argc, char* argv)SERVICE_TABLE_ENTRY serviceTable=SERVICE_NAME,(LPSERVICE_MAIN_FUNCTION)ServiceMainNULL,NULL;BOOL success;success = StartServiceCtrlDispatcher(serviceTable);if (!success)ErrorHandler(In StartServiceCtrlDispatcher,GetLastError();Start ServiceC trlDispa tcher的参数必须是一个以NULL结尾的数组指针,我们可以在一个 程序文件中注册多个服务实例,只在把所要注册的服务名和服务入口函数地址写到数组中 即可,在我们调用CreateService创建服务时要把dwServiceType参数设为共享进程 (SERVICE_WIN32_SHARE_PR0CESS);不过当要创建独立进程的服务时(dwServiceType参数 为SERVICE_WIN32_0WN_PR0CESS时)在这里就只能注册一个服务实例。(2) 服务入口函数ServerMain()VOID ServiceMain(DWORD argc,LPTSTR *argv)BOOL success;StatusHandler=RegisterServiceCtrlHandler(SERVICE_NAME,(LPHANDLER_FUNCTION)Handler);if (!serviceStatusHandler)success = ReportStatus (SERVICE_START_PENDING,NO_ERROR,0,1,5000);if (!success)return;endEvent = CreateEvent(0,TRUE,FALSE,0);if (!endEvent)success = ReportStatus (SERVICE_START_PENDING,NO_ERROR,0,2,5000);if (!success)return;/init parameter startRecvParam(argc,argv);/init parameter endsuccess = ReportStatus (SERVICE_START_PENDING,NO_ERROR,0,3,5000);if (!success)return;success = InitService();if (!success)return;success = ReportStatus (SERVICE_RUNNING,NO_ERROR,0,0,0);if (!success)return;WaitForSingleObject(endEvent,INFINITE);Regis terServiceC trlHandler完成H andler函数的注册(Handler函数的具体实现 我们在第三小节中介绍),它的第一个参数是调用CreateService创建服务时lpServiceName 指向的名服务名称,每二个参数是Handler函数的地址;函数名可以自由命名。ReportStatus 是向SCM报告服务当前状态的一个自定义函数。它内部调用Se tServiceS tat us向SCM 报告服务的当状态,此函数有两个参数第一个就是Regis terServiceC trlHandler完成时返 回的SERVICE_STATUS_HANDLE,第二个参数是一个SERVICE_STATUS变量的指针,它指示了服 务当前的状态信息;当注册完Handler函数后向SCM报告一下自己当前的状态(正在启动)。 接着创建endEven t事件对像,它是当我们收到SCM的退出控制代码时通知服务主函数退 出的,大家可看ServiceMain的最后一句。下面又是向SCM报告自己正在启动,当初始化 所花费的时间非常短时这样做并不是必须的,但如果很长就必须这样做。 RecvParam(argc,argv)使用了 ServiceMain函数的两个参数,大家可以看出ServiceMain和 main有着一样的形参;说明ServiceMain和m ain 一样可以接收配置参数,稍后我们会在服务 控制程序的编写中给大家介绍如何给服务配置参数。InitService()完成我们的业务初始化 工作并开始业务处理。最后报告服务启动完成,等待endEven t事件退出服务。下面我们再 来看一下SCM控制消息的处理。(3) SCM控制消息处理(Handler函数)VOID Handler(DWORD controlCode)DWORD currentState = 0;BOOL success;switch(controlCode)case SERVICE_CONTROL_STOP:success=ReportStatus(SERVICE_STOP_PENDING,NO_ERROR,0,1,5000);CloseTask();success=ReportStatus(SERVICE_STOPPED,NO_ERROR,0,0,0);return;case SERVICE_CONTROL_PAUSE:if (runningService&!pauseService) success=ReportStatus(SERVICE_PAUSE_PENDING,NO_ERROR,0,1,1000);pauseService=TRUE;ServicePause();currentState=SERVICE_PAUSED;break;case SERVICE_CONTROL_CONTINUE:if (runningService&pauseService)success = ReportStatus(SERVICE_CONTINUE_PENDING,NO_ERROR,0,1,1000);pauseService = FALSE;ServiceContinue();currentState=SERVICE_RUNNING;break;case SERVICE_CONTROL_INTERROGATE:/检索更新状态的时break;case SERVICE_CONTROL_SHUTDOWN:/告诉服务即将关机success=ReportStatus(SERVICE_STOP_PENDING,NO_ERROR,0,1,5000);CloseTask();return;default:break;ReportStatus(currentState,NO_ERROR,0,0,0);Handler只有一个参数就是SCM传来的控制消息代码;这里处理的了停止,暂停,继续, 更新,关机五个控制消息。但并不是这五个消息SCM都会向服务发送,要在向服务报告 状时向SCM报告自己可以响应的控制消息,只要设置SERVICE_STATUS结构中的 dwControlsAccepted域即可,它对应的值有:SERVICE_ACCEPT_STOP, SERVICE_ACCEPT_PAUSE_CONTINUE, SERVICE_ACCEPT_SHUTDOWN,当要设置多个时只要把宏 相或(丨)传给dwControlsAccepted域即可。在响应SCM控制消息时也要注意及时报告 服务当前的状态信息,否则SCM会认为服务响应超时出错了。(4)服务的安装与卸载服务程序编写完成并编译通过后,还要安装注册到操作系统中,这样它才会出现在管 理工具一服务,那个管理器里面。API给我们提供了一个函数来实现我们注册服务的功能; SC_HANDLE CreateService( SC_HANDLE hSCManager, / 服务控制管理器的句柄 LPCTSTR lpServiceName, / 指向服务的内部名称 LPCTSTR lpServiceName, / 指向服 务的显示名称 DWORD dwDesiredAccess, / 服务的访问类型 DWORD dwServiceType, /服务的类型 DWORD dwStartType, /服务的启动方式(自动,手动,禁用) DWORD dwErrorControl, / 错误控制方式 LPCTSTR lpBinaryPathName, / 服务程序的路径 LPCTSTR lpLoadOrderGroup, / 服务组的名称 LPDWORD lpdwTagId, / 服务的标 签号 LPCTSTR lpDependencies, / 服务依赖的服务或组名 LPCTSTR lpServiceStartName, / 服务的启动帐户 LPCTSTR lpPassword/ 服务启动帐户的密码);hSCManager :这是函数的第一个参数一 SCM的句柄。它要调用OpenSCManager来获 得,稍后我们会讲它怎么调用方法。lpServiceName和lpServiceName:分别的服务的名称和服务的显示名称,服务是显示名称 就服务管理器中看到的那个服务名。则是服务在SCM中注册的名称,比如调用OpenService 打开服务时就会用到它。dwDesiredAccess:标出服务同意请求的访问,可以是下面任意任值:SERVICE_ALL_ACCESSSERVICE_CHANGE_CONFIGSERVICE_ENUMERATE_DEPENDENTSSERVICE_INTERROGATESERVICE_PAUSE_CONTINUESERVICE_QUERY_CONFIGSERVICE_QUERY_STATUSSERVICE_STARTSERVICE_STOPSERVICE_USER_DEFINED_CONTROL我们可以指定一个或多个,如果有多个的话要用或符号(丨)联结起来。dwServiceType:注册服务的类型,它必须是下面的值:SERVICE_WIN32_0WN_PR0CESSSERVICE_WIN32_SHARE_PROCESSSERVICE_KERNEL_DRIVERSERVICE_FILE_SYSTEM_DRIVER 如果指定的是 SERVICE_WIN32_0WN_PR0CESS 类型的服务还可 以加上SERVICE_WIN32_OWN_PROCESS(允许用户桌面交互),我们这里介绍的服务只能注册 为 SERVICE_WIN32_0WN_PR0CESS 或 SERVICE_WIN32_SHARE_PR0CESS ;另两种类型是驱动级的 服务用的,有兴趣大家可查看相关资料。dwS tart Type:服务的启动类型 SERVICE_BOOT_START、SERVICE_SYSTEM_START、 SERVICE_AUTO_START、SERVICE_DEMAND_START、SERVICE_DISABLED。分别为前两种类型仅 对驱动程序用效,所在我们这里所说的这类服务能后三种(自动,手动,禁用)。dwErrorControl:服务的错误控制标记SERVICE_ERROR_IGNORE:忽略所有错误SERVICE_ERROR_NORMAL :正常报告服务返回的错误SERVICE_ERROR_SEVERE :当服务返回错误出现时,如果最后已知好控制集(最后已知好控 制集:是系统最后一次成功引导时使用的服务注册表配置)尚未使用,则重新引导进入最后已知好控制集,否则重新引导。SERVICE_ERROR_CRITICAL :当服务返回错误出现时,如果最后已知好控制集尚未使用,则 重新引导进入最后已知好控制集,否则蓝屏崩溃。lpBinaryPa thName:服务程序的文件路径 lpLoadOrderGroup:服务所属的组 lpdwTagId :在组中的唯一标识 lpDependencies:服务所依赖的其它组和服务 lpServiceStartName和lpPassword:服务由哪个用户启动,也即服务运行在哪个用户权限 下,分别指定用户名和密码.F再说两重要的函数:OpenSCManager和CloseServiceHandle给出它们的原型SC_HANDLE OpenSCManager( LPCTSTR lpMachineName, / 机器名,打开本机的SCM时可 为NULL LPCTSTR lpDatabaseName, / 指向SCM数据库的名字可为NULL DWORD dwDesiredAccess / 访问权限类型如:SC_MANAGER_ALL_ACCESS等);BOOL CloseServiceHandle( SC_HANDLE hSCObject / 服务控制句柄 );这果列出一段注册服务的代码供大家参考:BOOL success = FALSE;SERVICE_STATUS status;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);CloseServiceHandle(scm);return false;CloseServiceHandle(newService);CloseServiceHandle(scm);return true;删除服务时调用Dele teService ;它只有一个参数(服务句柄)。我们可分四步完 成1)、打开SCM句柄。2)、打开要删除的服务。3)、检查当前服务的状态确保服务已经 停止。 4)、删除服务并关闭所有打开的句柄。下面是一段删除服务的程序。SC_HANDLE Service,scm;SERVICE_STATUS status;BOOL success;if (pszServiceName=NULL)return false;scm = OpenSCManager(NULL,NULL,SC_MANAGER_ENUMERATE_SERVICE);if (!scm)coutOpenSCManager ERROR:GetLastError()endl;CloseServiceHandle(scm);return false;Service = OpenService(scm,pszServiceName,SERVICE_ALL_ACCESS|DELETE);CloseServiceHandle(Service);CloseServiceHandle(scm);return false;success = QueryServiceStatus(Service,&status);if (!success)coutQueryServiceStatus ERROR:GetLastError()endl;CloseServiceHandle(Service);CloseServiceHandle(scm);return false;if (status.dwCurrentState!=SERVICE_STOPPED)success = ControlService(Service,SERVICE_CONTROL_STOP,&status);if (!success)coutControlService ERROR:GetLastError()endl;CloseServiceHandle(Service);CloseServiceHandle(scm);return false;success = DeleteService(Service);CloseServiceHandle(Service);CloseServiceHandle(scm);return false;CloseServiceHandle(Service);CloseServiceHandle(scm);return true;?4 服务控制程序?4.1服务控制程序概要在上面我们了解服务程序的编写,现在我们来看一下控制服务时会用到的几个常用APIo服 务控制程序的编写与标准的Windows应用程序无异,它要用到服务管理函数,如:OpenSCManager、OpenService、QueryServiceConfig、StartService、QueryServiceStatus、ControlService等;它都在系统的advapi32.dll中实现。在使用SCM的函数时,SCP必须要 首先调用OpenSCManager函数,打开一个通向SCM的通道。调用这个函数的时候,SCP还必须 指定它想要执行的动作类型;也就是我们上一节所提到的dwDesiredAccess参数它的取值: SC_MANAGER_ALL_ACCESS、SC_MANAGER_CREATE_SERVICE、SC_MANAGER_ENUMERATE_SERVICE、 SC_MANAGER_QUERY_LOCK_STATUS、SC_MANAGER_ENUMERATE_SERVICE 、SC_MANAGER_QUERY_LOCK_STATUS、SC_MANAGER_LOCK、SC_MANAGER_CONNECT。例如:我们 要枚举当前所有的服务就必须给dwDesiredAccess参数指定SC_MANAGER_ENUMERATE_SERVICE ;同时也可以指定其它的值,当指写多个值时我们要把它 用按位或(|)符号连接起来。与此同时我们在调用OpenService时也必须告知SCM我们要对服务进行的动作;它有三个参 数,最后一个参数dwDesiredAccess指出要对服务进行的操作,这些操作的标记与 Crea teService 中的 dwDesiredAccess 参数标记值一样。当我们以 SERVICE_ALL_ACCESS 访问 权限打开服务后就可以对它进行配置(QueryServiceConfig)、控制(ControlService)、 查询状态(QueryServiceS tat us)、设置状态(Se tServiceS tat us)、删除(Dele teService) 等所有访问操作。下面我们可以来看两服务控制的实例。?4.2 枚举服务我们先来看EnumDependen tStatus函数原型:BOOL EnumServicesS tat us ( SC_HANDLE hService,/ SCM控 制句柄 DWORDdwServiceType,/要枚举服务还有驱动 DWORD dwServiceState, /要枚举什么状态的服务 LPENUM_SERVICE_STATUS lpServices,/ 存储枚举出服务的内存地址 DWORD cbBufSize,/ lpServices指向内存区大小 LPDWORD pcbBytesNeeded, /实际需要的内存大小LPDWORD lpServicesRe turned /返回枚举到服务年个数LPDWORD lpResumeHandle /指向下一个有效的入口);dwServiceState参数由:SERVICE_ACTIVE、 SERVICE_INACTIVE、SERVICE_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* szInfo1024*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;ilpDescription);/ 得到服务的启动账户名ZeroMemory(szInfo,dwSize);dwSize=1024*8;QueryServiceConfig(sh,(LPQUERY_SERVICE_CONFIG)szInfo,dwSize,&dwSize);info.LoginUser.Format(%s,(LPQUERY_SERVICE_CONFIG)szInfo)-lpServiceStartNam e);CloseServiceHandle(sh);/ 添加到信息队列中ItemAdd(&info);CloseServiceHandle(sc);return TRUE;上面程序中用到了两个查询服务当前配置的函数QueryServiceConfig2和 QueryServiceConfig。它们有所不同是QueryServiceConfig2可以通过设置第二个参数是 SERVICE_CONFIG_DESCRIPTION 还是 SERVICE_CONFIG_FAILURE_ACTIONS 来得到服务的描述信 息和失败的活动;而QueryServiceConfig则查询返回一个QUERY_SERVICE_CONFIG结构,这 个结构存储了服务的类型、启动类型、错误控制标记、服务文件所在路径、显示名等信息 详细可以查看MSDN。与这个两函数相对应还有两个配置函数ChangeServiceConfig2和 ChangeServiceConfig。它们的具体使用方法我们来看下面的这段程序。?4.3 配置服务在3.2中我们举了一个创建服务的程序片段,其中我们只是创建服务并未设置服务描述信 息,启动服务的操作。下面的程序片段给出了示例: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)coutQueryServiceStatus ERROR:GetLastError()endl;CloseServiceHandle(newService);CloseServiceHandle(scm);return false;if (status.dwCurrentState!=SERVICE_RUNNING)success = StartService(newService,NULL,NULL);if (!success)coutControlService ERROR:GetLastError()endl;CloseServiceHandle(newService);CloseServiceHandle(scm);return false;CloseServiceHandle(newService);
展开阅读全文
相关资源
相关搜索

最新文档


当前位置:首页 > 办公文档 > 解决方案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!