Thursday, April 14, 2011

How can I restart a Windows service application written in Delphi?

I have a Windows service written in Delphi. One of the third-party resources it uses occasionally gets corrupted, and the only way I've found to fix the situation is to exit and restart the program. I can detect when the resource is corrupted from within the program, and I can tell Windows to restart the service after it stops, but I can't figure out how to have the service tell itself to stop.

The program is pretty simple. I created a service application in what seems to be the normal way. I have a subclass of TService to manage the service, while all of the functionality occurs in a separate thread. The TService subclass pretty much just manages the execution of the subthread, and it's in the subthread that I would be detecting the corruption.

For reference, here's the header info for the service and subthread.

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;

In a GUI application, I'd call Application.Terminate, but TServiceApplication doesn't seem to have that method. I can terminate the subthread, but the main thread never notices, and Windows thinks the service is still running. I can't really think of much else to try.

The program was originally created in Delphi 5, and I'm currently using Delphi 2007, in case that makes a difference.


Edit:

With mghie's code, I can stop the service, but Windows will only restart the service if it fails unexpectedly, not if it's stopped normally. What I'm going to do is make a separate service application, have the first signal the second if it has problems, and then have the second restart the first.

From stackoverflow
  • You should be able to use WMI (Windows Management Instrumentation) to restart a service, even from within the service itself. Don't know if this would cause any strange problems but it should work. Here's an article on doing WMI with Delphi.

    UPDATE: Well well, I assumed (my mistake) that there is a single WMI service restart command, such as the button you can click in the services maangement listing. Apparently not.
    You could instead write a console app that the service starts when the data is corrupted. The console app would restart the service from a separate process.

    Rob Kennedy : Don't leave us hanging! What's the WMI command to restart a service?
  • There is no problem having the service stop itself - I just tried with one of my own services, written with Delphi 4 (without using the TService class). The following routine works for me:

    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;
    

    You need to check whether it will also work from your worker thread.

0 comments:

Post a Comment