【问题标题】:How to start a windows service in Delphi for Windows 8如何在 Delphi for Windows 8 中启动 Windows 服务
【发布时间】:2015-05-28 12:17:37
【问题描述】:

我需要使用 Delphi Windows 应用程序启动服务。它在 Windows 7 中运行良好,但在 Windows 8.1 中无法运行。我使用了以下代码

function ServiceStart(sMachine,sService : string ) : boolean;
var
  schm,schs   : SC_Handle;
  ss     : TServiceStatus;
  psTemp : PChar;
  dwChkP : DWord;
begin
  ss.dwCurrentState := 0;
  schm := OpenSCManager(PChar(sMachine),Nil,SC_MANAGER_CONNECT);
  if(schm > 0)then
  begin
    schs := OpenService(schm,PChar(sService),SERVICE_START or SERVICE_QUERY_STATUS);
    if(schs > 0)then
    begin
      psTemp := Nil;
      if(StartService(schs,0,psTemp))then
      begin
        if(QueryServiceStatus(schs,ss))then
        begin
          while(SERVICE_RUNNING <> ss.dwCurrentState)do
          begin
            dwChkP := ss.dwCheckPoint;
            Sleep(ss.dwWaitHint);
            if(not QueryServiceStatus(schs,ss))then
            begin
              break;
            end;
            if(ss.dwCheckPoint < dwChkP)then
            begin
              break;
            end;
          end;
        end;
      end;
      CloseServiceHandle(schs);
    end;
    CloseServiceHandle(schm);
  end;


  Result := SERVICE_RUNNING = ss.dwCurrentState;
end;


procedure TForm1.BBSerStatusClick(Sender: TObject);
begin
  ServiceStart('','SERVTEST');
end;

注意:SERVTEST 是服务应用程序。 谁能帮我?

【问题讨论】:

  • 那么会发生什么。哪个部分失败了?错误代码是什么?告诉我们你的调试。请记住,Stack Overflow 不能替代调试。
  • 我假设你的程序正在运行提升
  • 以上代码在windows 7上运行良好,但在windows 8中,代码只是安装服务而不是启动服务。
  • 如果您尝试从服务管理中启动它,您是否会启动服务?你应该先检查一下。
  • StartService 的文档清楚地表明 如果函数失败,则返回值为 0。要获取扩展错误信息,请调用 GetLastError 我没有看到致电GetLastError 了解为什么StartService 不起作用。显然,它提供的扩展错误信息在这里会很有用,你不觉得吗?

标签: delphi


【解决方案1】:

我看到您正在使用从here 复制的代码。

if(schm &gt; 0)thenif(schs &gt; 0)then 应改为 if(schm &lt;&gt; 0)thenif(schs &lt;&gt; 0) then。这种情况下唯一的失败值是 0(一些 API 使用 INVALID_HANDLE_VALUE 代替,但 SCM API 不使用)。 任何其他值都是有效句柄。句柄并不是真正的整数(尽管 Delphi 如此声明它们),因此您不应将它们视为整数。它们是任意值,不应被解释,它们应按原样使用。如果您没有返回实际的失败值(在本例中为 0),则无论实际返回的值如何,调用都是成功的。

ss.dwCurrentState 的处理也有点偏差。而不是在ss.dwCurrentState不是SERVICE_RUNNING时循环,而是在ss.dwCurrentStateSERVICE_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;

【讨论】:

  • SC_HANDLE 是签名还是未签名?
  • 在 C 中,Win32 API 将 SC_HANDLE 声明为 struct SC_HANDLE__ *void*,具体取决于是否定义了 STRICT。在 Delphi 中,Win32 API 句柄,如SC_HANDLE,被简单地声明为THandle,即CardinalNativeUInt,具体取决于平台。
  • 在这种情况下,&gt;0 在语义上与&lt;&gt;0 相同。我绝对同意 &lt;&gt;0 在美学上更好,但这并不能解释任何行为变化。
  • 雷米,如果可以的话,我会接受你的回答,但我只能给你+1。我测试了你的第一个 ServiceStart 例程,它运行良好。谢谢 !您是否认为,如果我提出一个新问题,您可以发布如何停止服务的答案?我在 op 复制他的示例的链接中看到也是一个 Stop 过程,但是如果您说它有错误,我不相信该代码。 :)
  • @MarusNebunu:只需使用与上面类似的代码,将“开始”部分替换为相应的“停止”部分(SERVICE_START -> SERVICE_STOPStartService() -> ControlService(SERVICE_CONTROL_STOP),@ 987654358@ -> SERVICE_STOP_PENDING, SERVICE_RUNNING -> SERVICE_STOPPED 等)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-23
相关资源
最近更新 更多