我看到您使用了从这里复制的代码。
if(schm > 0)then
和if(schs > 0)then
应该改为if(schm <> 0)then
和if(schs <> 0) then
。在这种情况下,唯一的失败值是0(一些API使用INVALID_HANDLE_VALUE
,但SCM API不使用)。任何其他值都是有效的句柄。句柄实际上并不是整数(尽管Delphi将它们声明为整数),因此您不应将它们视为整数。它们是任意值,不应被解释,而应按原样使用。如果未返回实际的失败值(在本例中为0),则调用无论实际返回什么值都成功。
ss.dwCurrentState
的处理也有点问题。不要在ss.dwCurrentState
不是SERVICE_RUNNING
时循环,而应在ss.dwCurrentState
是SERVICE_START_PENDING
时循环。如果出现问题,服务从未进入SERVICE_RUNNING
状态,则循环将持续运行,除非QueryServiceStatus()
本身失败。我不建议依赖ss.dwCheckPoint
,因为并不是所有服务都正确实现它(事实上,Delphi自己的TService
没有 - 请参见QC#1006 TService.ReportStatus报告了错误的CheckPoint)。
尝试以下内容。它区分SCM API故障和服务启动故障,但还进行了额外的错误检查以处理某些实际上并非致命错误的错误:
function ServiceStart(sMachine, sService : string) : Boolean;
var
schm, schs : SC_HANDLE;
ss : TServiceStatus;
begin
schm := OpenSCManager(PChar(sMachine), nil, SC_MANAGER_CONNECT);
if (schm = 0) then RaiseLastOSError;
try
schs := OpenService(schm, PChar(sService), SERVICE_START or SERVICE_QUERY_STATUS);
if (schs = 0) then RaiseLastOSError;
try
if not StartService(schs, 0, nil) then
begin
Result := ERROR_SERVICE_ALREADY_RUNNING = GetLastError();
if not Result then RaiseLastOSError;
Exit;
end;
repeat
if not QueryServiceStatus(schs, ss) then
begin
if (ERROR_SERVICE_NOT_ACTIVE <> GetLastError()) then RaiseLastOSError;
Result := False;
Exit;
end;
if (SERVICE_START_PENDING <> ss.dwCurrentState) then Break;
Sleep(ss.dwWaitHint);
until False;
Result := SERVICE_RUNNING = ss.dwCurrentState;
finally
CloseServiceHandle(schs);
end;
finally
CloseServiceHandle(schm);
end;
end;
或者,这里是微软示例的一个(修改过的)版本,还包括在启动服务之前处理服务处于SERVICE_STOP_PENDING
状态的情况(我删除了基于dwCheckPoint
处理的超时逻辑):
启动服务:
function ServiceStart(sMachine, sService : string) : Boolean;
var
schSCManager,
schService : SC_HANDLE;
ssStatus : TServiceStatus;
begin
schSCManager := OpenSCManager(PChar(sMachine), nil, SC_MANAGER_CONNECT);
if (schSCManager = 0) then RaiseLastOSError;
try
schService := OpenService(schSCManager, PChar(sService), SERVICE_START or SERVICE_QUERY_STATUS);
if (schService = 0) then RaiseLastOSError;
try
if not QueryServiceStatus(schService, ssStatus) then
begin
if (ERROR_SERVICE_NOT_ACTIVE <> GetLastError()) then RaiseLastOSError;
ssStatus.dwCurrentState := SERVICE_STOPPED;
end;
if (ssStatus.dwCurrentState <> SERVICE_STOPPED) and ssStatus.dwCurrentState <> SERVICE_STOP_PENDING) then
begin
Result := True;
Exit;
end;
while (ssStatus.dwCurrentState = SERVICE_STOP_PENDING) do
begin
dwWaitTime := ssStatus.dwWaitHint div 10;
if (dwWaitTime < 1000) then
dwWaitTime := 1000
else if (dwWaitTime > 10000) then
dwWaitTime := 10000;
Sleep(dwWaitTime);
if not QueryServiceStatus(schService, ssStatus) then
begin
if (ERROR_SERVICE_NOT_ACTIVE <> GetLastError()) then RaiseLastOSError;
Break;
end;
end;
if not StartService(schService, 0, nil) then RaiseLastOSError;
if not QueryServiceStatus(schService, ssStatus) then
begin
if (ERROR_SERVICE_NOT_ACTIVE <> GetLastError()) then RaiseLastOSError;
ssStatus.dwCurrentState := SERVICE_STOPPED;
end;
while (ssStatus.dwCurrentState = SERVICE_START_PENDING) do
begin
dwWaitTime := ssStatus.dwWaitHint div 10;
if (dwWaitTime < 1000) then
dwWaitTime := 1000
else if (dwWaitTime > 10000) then
dwWaitTime := 10000;
Sleep(dwWaitTime);
if not QueryServiceStatus(schService, ssStatus) then
begin
if (ERROR_SERVICE_NOT_ACTIVE <> GetLastError()) then RaiseLastOSError;
ssStatus.dwCurrentState := SERVICE_STOPPED;
Break;
end;
end;
Result := (ssStatus.dwCurrentState = SERVICE_RUNNING);
finally
CloseServiceHandle(schService);
end;
finally
CloseServiceHandle(schSCManager);
end;
end;
StartService
的文档明确说明 如果函数失败,返回值为0。要获取扩展错误信息,请调用GetLastError
。 我没有看到调用GetLastError
来查看为什么StartService
没有工作。显然,它提供的 扩展错误信息 在这里会很有用,你不觉得吗? - Ken White