如何重新启动用Delphi编写的Windows服务应用程序?

时间:2009-03-20 19:22:20

标签: delphi winapi windows-services

我有一个用Delphi编写的Windows服务。它偶尔使用的第三方资源之一会被破坏,我发现修复这种情况的唯一方法就是退出并重启程序。我可以检测到程序中资源何时被破坏,我可以告诉Windows在服务停止后重新启动服务,但我无法弄清楚如何让服务告诉自己停止服务。

程序非常简单。我用似乎正常的方式创建了一个服务应用程序。我有一个TService的子类来管理服务,而所有的功能都发生在一个单独的线程中。 TService子类几乎只管理子线程的执行,并且在子线程中我将检测到损坏。

供参考,这是服务和子线程的标题信息。

type
  TScannerThread = class(TThread)
   private     
    Scanner    : TScanner;
    DefaultDir : String;
    ImageDir   : String;
    procedure CheckScanner;
   public      
    Parent     : TComponent;
    procedure Execute; override;
  end;         

  TCardScanSvc   = class(TService)
    procedure ServiceCreate(Sender: TObject);
    procedure ServiceExecute(Sender: TService);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServicePause(Sender: TService; var Paused: Boolean);
    procedure ServiceContinue(Sender: TService; var Continued: Boolean);
   private        
    ScannerThread : TScannerThread;
   public         
    function GetServiceController: TServiceController; override;
  end;            

var
  CardScanSvc : TCardScanSvc;

在GUI应用程序中,我调用Application.Terminate,但TServiceApplication似乎没有该方法。我可以终止子线程,但主线程从未注意到,Windows认为该服务仍在运行。我真的想不起其他的事情。

该程序最初是在Delphi 5中创建的,我目前正在使用Delphi 2007,以防万一。


编辑:

使用mghie的代码,我可以停止服务,但Windows只会在意外失败时重启服务,而不是正常停止服务。我要做的是创建一个单独的服务应用程序,如果有问题则将第一个信号作为第二个信号,然后让第二个信号重新启动第一个。

3 个答案:

答案 0 :(得分:9)

服务停止本身没有问题 - 我只是尝试使用自己的一个服务,用Delphi 4编写(不使用 TService 类)。以下例程对我有用:

procedure TTestService.StopService;
var
  Scm, Svc: SC_Handle;
  Status: SERVICE_STATUS;
begin
  Scm := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  if Scm <> 0 then begin
    Svc := OpenService(Scm, PChar(ServiceName), SERVICE_ALL_ACCESS);
    if Svc <> 0 then begin
      ControlService(Svc, SERVICE_CONTROL_STOP, Status);
      // handle Status....
      CloseServiceHandle(Svc);
    end;
    CloseServiceHandle(Scm);
  end;
end;

您需要检查它是否也可以从您的工作线程中运行。

答案 1 :(得分:1)

您应该能够使用WMI(Windows Management Instrumentation)重新启动服务,即使是在服务本身内也是如此。不知道这是否会导致任何奇怪的问题,但它应该工作。 Here's一篇关于使用Delphi进行WMI的文章。

更新:好吧,我假设(我的错误)有一个WMI服务重启命令,例如您可以在服务管理列表中单击的按钮。显然不是。
您可以改为在数据损坏时编写服务启动的控制台应用程序。控制台应用程序将从单独的进程重新启动该服务。

答案 2 :(得分:-1)

我找到了一个更简单的替代方案。您可以向TService子类添加方法:

procedure TSomeService.StopService;
begin
  Controller(SERVICE_CONTROL_STOP);
end;

我使用与OP相同的设置 - 特别是TService后代只是启动一个工作线程,然后除了处理Windows消息之外什么都不做。不幸的是,您无法从工作线程调用Controller,因为它受到保护。当然,围绕它的简单方法是创建一个公共方法,如上所示。要从工作线程中使用它,您将需要对可从工作线程内访问的TService对象的引用。我这样做是通过将我的TService对象传递给线程构造函数中的工作线程。