尚未学会走的时候,我就已经开始爬电线杆了。第一个成型的软件是一个NT服务程序的控制程序。为了写好它,白头发眼看就要钻出头皮了。现在写出来,发泄一下。
我原先准备写个NT服务的^_^,不过线程类没搞懂,所以转而写一个控制服务的程序。在Delphi中,选择新建一个“Service Application”程序,然后编译一下,就是一个NT服务程序的骨架了。简单是简单,不过想让它丰满起来就难了,而且一些小问题掌握不到,它的控制程序可就不那么容易写了。 一个NT服务的控制程序,意思就是说要用它来控制一个NT服务的安装、启动、停止、卸载这四个状态,它的过程是: 1、先要打开系统的服务管理器(取得管理NT服务的权限); 2、打开需要控制的NT服务(获得控制权); 3、对该服务进行控制。 做法是: 一、使用 OpenSCManager打开本地默认的服务管理器。(用到这些API的时候,要先在USES中加入“winsvc” OpenSCManager的声明是: SC_HANDLE OpenSCManager( LPCTSTR lpMachineName, // 计算机名。我做的只是控制本地的服务,所以这里用Nil。 LPCTSTR lpDatabaseName, // 服务管理器名。打开默认的就用Nil,我不知道是不是还有别的 DWORD dwDesiredAccess // 你要获得什么样的权限(这个要查手册) ); 先声明: var schOpenSCManager : SC_HANDLE begin schOpenSCManager := OpenSCManager(Nil,Nil,SERVICE_ALL_ACCESS); // 最后面那个参数是控制权限,意思是完全控制,当然还有其它的,查查书. end; 这样,打开本地服务管理器的工作就完成了. 二、检查我们要操作的服务是否存在,返回一个HANDLE来确认。这里要用到 OpenService(),它的原形是: SC_HANDLE OpenService( SC_HANDLE hSCManager, //打开服务管理器后返回的HANDLE,也就是 schOpenSCManager LPCTSTR lpServiceName, //服务名称,这个很重要,不能写混了。看下文。 DWORD dwDesiredAccess //想获得什么权限。最好是SERVICE_ALL_ACCESS了。^_^ ); 三、要是OpenService()返回的值小于或等于0,我们就可以认为我们需要控制的服务没有安装。要是大于0,那就要用另一个API函数来确定它现在是什么状态。 BOOL QueryServiceStatus( SC_HANDLE hService,// 这个写的是在上一步 OpenService 时返回的值,意思就是说指定特定的服务 LPSERVICE_STATUS lpServiceStatus // QueryServiceStatus() 返回的这个服务的状态 ); 我是这样写的: var sscStatus : TServiceStatus; begin QueryServiceStatus(schOpenService,sscStatus); //此时,就可以得到服务当前的状态了 Case sscStatus.dwcurretStatus of // 把当前状态写成Case语句,其实随便写,只要通过就行了。 SERVICE_STOPPED : // 你的代码 SERVICE_RUNNING : // 你的代码 SERVICE_PASUED : // 你的代码 //还有几种状态,查书看看吧。 end; end; 四、确定是什么状态就好办了。如果schOpenService <= 0,那我们就需要安装。得用到 CreateService();在SDK文档中是这样定义的: SC_HANDLE CreateService( SC_HANDLE hSCManager, // 上文提到的 schOpenSCManager LPCTSTR lpServiceName, // 重要:跟OpenService()中的lpServiceName的意思是一样的,都是你做服务程序时给服务起的名字。做服务的时候有一个是:DisplayName,这是显示在服务程序器中的名字;还有一个 Name :如果你建立服务程序时没改,那这里就得用默认的名字(默认的名字是service1);第三个是 ServiceStartName : 这个我没搞懂,也没用。 LPCTSTR lpDisplayName, //就是上文说的显示在服务管理器中的名字 DWORD dwDesiredAccess, //权限 DWORD dwServiceType, //类型,共有四个。具体意思我说不清楚。 SERVICE_WIN32_OWN_PROCESS是有自己进程的win32 服务。要跟你写服务程序时选择的一样。 DWORD dwStartType, // 是自动的啊,还是BOOT啊什么的,要跟你写服务程序时选择的一样。 DWORD dwErrorControl, //出现错误怎么办?最好用:SERVICE_ERROR_IGNORE,忽略它。其它的可选项请看SDK的说明。 LPCTSTR lpBinaryPathName,// 这个参数把我浑了至少三个小时。后来看大富翁,才知道Pchar(服务的完整路径加服务的名字)就可以了。 LPCTSTR lpLoadOrderGroup,// 后面这五个我看了别人的介绍,但不是很清楚,都是用的NIL, LPDWORD lpdwTagId, LPCTSTR lpDependencies, LPCTSTR lpServiceStartName, LPCTSTR lpPassword ); 用了这个CreatService()后,你需要控制的服务就加入服务管理器里了。打开服务管理器应该就可以看到了。如果上面的那个ServiceName跟你建立服务里的不一样的话,你也可以把服务安装到服务管理器里,但启动、停止、删除的控制都不行了,用 service /uninstall也删不掉的,只有到注册表里删了。 五、安装好了之后,服务并没有启动,(如果你写服务的时候把 starttype选成Auto 或 boot的话,重启就自动启动了)。现在没有重启,要把它启动起来,要用到 StartService(). BOOL StartService( SC_HANDLE hService, // 从OpenService() 或 CreatService() 返回的值,我这里用的是 schOpenService,也就是OpenService()返回的。 DWORD dwNumServiceArgs, //SDK中的说明是:[in] Number of strings in the lpServiceArgVectors array. If lpServiceArgVectors is NULL, this parameter can be zero. 因为看的不是很懂,既然人家说可以zero的,我就写0了。 LPCTSTR* lpServiceArgVectors //SDK当中关于这个参数的说明偶看不懂,说什么Argv[0]作为默认的服务名字什么的。我是按照人家的程序写的。 ); 我的写法是: var argv : Pchar; begin argv := Nil; StartService(schOpenService,0,argv); end; 如果上面所说的你把Createservice()函数里的ServiceName的值写的和你写服务时的不一样,StartService 后你就会发现这个服务在服务管理器里的状态是“启动”,记住,不是“已启动”。当你关掉这个服务控制程序再运行时,会发现这个服务没安装(这是我遇到的问题。我是不是很笨?) 六、删除服务 DeleteService() 函数 BOOL DeleteService( SC_HANDLE hService // OpenService() 或 CreateService() 返回的值. ); DeleteService(schOpenService); 这一句就可以把已经安装的服务删除啦。 七、停止服务。并没有StopService()这个函数。需要用 ControlService()这个函数来停止一个服务。 BOOL ControlService( SC_HANDLE hService, //OpenService 或 CreateService() 的返回值 DWORD dwControl, //你向服务管理器发出的控制命令,这里是要停止这个服务,所以就是:SERVICE_CONTROL_STOP LPSERVICE_STATUS lpServiceStatus // 这个服务当前的状态 ); 八、需要注意的除了ServiceName这个可能出错的问题外,还要注意你OpenService()服务里的权限问题,我们使用OpenService()时是为了下一步的操作,如果下一步的操作是要删除这个服务,那个在OpenService()时就要给出删除的权限,所以能给ALL_ACCESS时就尽量给了。 服务的状态还有几个后面是_PENDING的,意思应该是某个工作尚未完成吧。比如说停止了一个服务,但我们用QueryServiceStatus()得到的状态是:SERVICE_STOP_PENDING,所以我们就要让其它操作等待一下,直到停止动作完成。 |
|