我看到您正在使用从here 复制的代码。
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 reports incorrect 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
// NOTE: if you use a version of Delphi that incorrectly declares
// StartService() with a 'var' lpServiceArgVectors parameter, you
// can't pass a nil value directly in the 3rd parameter, you would
// have to pass it indirectly as either PPChar(nil)^ or PChar(nil^)
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 处理):
Starting a Service:
function ServiceStart(sMachine, sService : string) : Boolean;
var
schSCManager,
schService : SC_HANDLE;
ssStatus : TServiceStatus;
begin
// Get a handle to the SCM database.
schSCManager := OpenSCManager(PChar(sMachine), nil, SC_MANAGER_CONNECT);
if (schSCManager = 0) then RaiseLastOSError;
try
// Get a handle to the service.
schService := OpenService(schSCManager, PChar(sService), SERVICE_START or SERVICE_QUERY_STATUS);
if (schService = 0) then RaiseLastOSError;
try
// Check the status in case the service is not stopped.
if not QueryServiceStatus(schService, ssStatus) then
begin
if (ERROR_SERVICE_NOT_ACTIVE <> GetLastError()) then RaiseLastOSError;
ssStatus.dwCurrentState := SERVICE_STOPPED;
end;
// Check if the service is already running
if (ssStatus.dwCurrentState <> SERVICE_STOPPED) and ssStatus.dwCurrentState <> SERVICE_STOP_PENDING) then
begin
Result := True;
Exit;
end;
// Wait for the service to stop before attempting to start it.
while (ssStatus.dwCurrentState = SERVICE_STOP_PENDING) do
begin
// Do not wait longer than the wait hint. A good interval is
// one-tenth of the wait hint but not less than 1 second
// and not more than 10 seconds.
dwWaitTime := ssStatus.dwWaitHint div 10;
if (dwWaitTime < 1000) then
dwWaitTime := 1000
else if (dwWaitTime > 10000) then
dwWaitTime := 10000;
Sleep(dwWaitTime);
// Check the status until the service is no longer stop pending.
if not QueryServiceStatus(schService, ssStatus) then
begin
if (ERROR_SERVICE_NOT_ACTIVE <> GetLastError()) then RaiseLastOSError;
Break;
end;
end;
// Attempt to start the service.
// NOTE: if you use a version of Delphi that incorrectly declares
// StartService() with a 'var' lpServiceArgVectors parameter, you
// can't pass a nil value directly in the 3rd parameter, you would
// have to pass it indirectly as either PPChar(nil)^ or PChar(nil^)
if not StartService(schService, 0, nil) then RaiseLastOSError;
// Check the status until the service is no longer start pending.
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
// Do not wait longer than the wait hint. A good interval is
// one-tenth the wait hint, but no less than 1 second and no
// more than 10 seconds.
dwWaitTime := ssStatus.dwWaitHint div 10;
if (dwWaitTime < 1000) then
dwWaitTime := 1000
else if (dwWaitTime > 10000) then
dwWaitTime := 10000;
Sleep(dwWaitTime);
// Check the status again.
if not QueryServiceStatus(schService, ssStatus) then
begin
if (ERROR_SERVICE_NOT_ACTIVE <> GetLastError()) then RaiseLastOSError;
ssStatus.dwCurrentState := SERVICE_STOPPED;
Break;
end;
end;
// Determine whether the service is running.
Result := (ssStatus.dwCurrentState = SERVICE_RUNNING);
finally
CloseServiceHandle(schService);
end;
finally
CloseServiceHandle(schSCManager);
end;
end;