Как перезапустить приложение-службу Windows, написанное на Delphi? - PullRequest
2 голосов
/ 20 марта 2009

У меня есть служба Windows, написанная на Delphi. Один из сторонних ресурсов, которые он использует, иногда повреждается, и единственный способ исправить ситуацию - это выйти и перезапустить программу. Я могу определить, когда ресурс поврежден изнутри программы, и могу сказать 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;

В приложении с графическим интерфейсом я бы вызвал Application.Terminate, но TServiceApplication, похоже, не имеет такого метода. Я могу прекратить подпоток, но основной поток никогда не замечает, и Windows считает, что служба все еще работает. Я не могу придумать, что еще можно попробовать.

Программа изначально была создана в Delphi 5, и в настоящее время я использую Delphi 2007, на случай, если что-то изменится.


Edit:

С помощью кода mghie я могу остановить службу, но Windows перезапустит службу только в случае неожиданного сбоя, а не в случае его нормальной остановки. Я собираюсь создать отдельное приложение-службу, дать первый сигнал второму, если у него возникнут проблемы, а затем второй перезапустить первый.

Ответы [ 3 ]

9 голосов
/ 20 марта 2009

Нет проблем с самой остановкой службы - я только что попробовал с одной из моих собственных служб, написанных на 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 голос
/ 20 марта 2009

Вы должны иметь возможность использовать WMI (инструментарий управления Windows) для перезапуска службы, даже из самой службы. Не знаю, если это вызовет какие-то странные проблемы, но это должно работать. Вот статья о создании WMI с Delphi.

ОБНОВЛЕНИЕ: Хорошо, я предположил (моя ошибка), что есть одна команда перезапуска службы WMI, например кнопка, которую вы можете нажать в списке управления службами. Видимо нет.
Вместо этого вы могли бы написать консольное приложение, которое запускается при повреждении данных. Консольное приложение перезапустит службу из отдельного процесса.

0 голосов
/ 21 декабря 2010

Я нашел более простую альтернативу, которая должна работать. Вы можете добавить метод к своему подклассу TService:

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

Я использую ту же настройку, что и OP - в частности, потомок TService, который просто запускает рабочий поток, а затем ничего не делает, только обрабатывает сообщения Windows. К сожалению, вы не можете вызвать Controller из рабочего потока, потому что он защищен. Конечно, простой способ это создать публичный метод, как я показал выше. Чтобы использовать это из рабочего потока, вам понадобится ссылка на объект TService, доступный из рабочего потока. Я делаю это, передавая мой объект TService в рабочий поток в конструкторе потока.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...