学用Delphi之2:痛苦的NT Service Control App

 

 一个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,所以我们就要让其它操作等待一下,直到停止动作完成。
posted @ 2009-02-17 08:32  dainiao01  阅读(411)  评论(0编辑  收藏  举报