Подтвердить что ты не робот

Дизайн сервисов Windows Delphi

Дизайн разработки Windows Delphi

Я никогда не создавал службу Windows, но читал все, что нашел. Все статьи или примеры, с которыми я столкнулся, очень просты в реализации и ограничены по своему охвату. Не видели ничего, что выходит за рамки того или иного конкретного сценария. Итак, у меня есть вся теория, которую я, вероятно, найду, и теперь я готов погрузиться в этот проект. Мне нравится планировать свои идеи и получать отзывы о том, что думают люди. Я опишу, что мне нужно от приложения, и как я собираюсь его построить. Я был бы признателен за комментарии от тех, у кого есть опыт создания служб Windows и любых советов, которые они хотели бы поделиться.

[СЦЕНАРИЙ] Сейчас у меня есть приложение (я назову это UPDATEAPPLICATION), которое предоставляет обновления для всех наших других приложений. Чтобы запустить любое из наших приложений, вам сначала нужно запустить эту программу UPDATEAPPLICATION и передать ей параметр желаемого приложения. UPDATEAPPLICATION вызывает WebService, который возвращает XML-информацию о том, имеет ли требуемое приложение какие-либо обновления.

Если есть обновление, UPDATEAPPLICATION загружает обновление в формате EXE или ZIP и заменяет соответствующие файлы для обновления целевого приложения. После этого UPDATEAPPLICATION выполняет ShellExecute для запуска нужного приложения, а затем UPDATEAPPLICATION закрывается.

Это довольно простой процесс, который хорошо работал на протяжении многих лет. Программа UPDATEAPPLICATION является приложением Delphi, другие наши приложения смешанны: Delphi, VB6, MS Access,.NET.

[ПРОБЛЕМА] С переходом на Vista и Windows 7 безопасность резко изменилась. Из-за характера UPDATEAPPLICATION UAC не позволит программе запускаться без доступа администратора или полностью отключенного UAC. Мы находимся в процессе обновления многих наших приложений до .NET, и в течение этого процесса я хотел бы, чтобы приложения, а также UPDATEAPPLICATION были совместимы с UAC. Из того, что я исследовал, единственный способ сделать это - создать UPDATEAPPLICATION как службу Windows. Поэтому, по сути, мне нужно дублировать функциональность UPDATEAPPLICATION в архитектуре службы Windows.

[МОЙ ДИЗАЙН] Я использую DelphiXE2. Мой проект будет состоять из трех частей, чтобы сформировать единое решение: Windows Service, небольшое приложение для приложения для взаимодействия с Windows Service и мои измененные приложения, которые будут отправлять сообщения службе Windows.

  • Моя служба Windows (которую я вызову UPDATESERVICE) будет запущена в качестве службы Windows и создаст TCP-сервер для прослушивания запросов.
  • Приложение лотка (которое я вызову TRAYAPP) будет использовать TCP Client для настройки/управления UPDATESERVICE.
  • My USERAPPLICATION, при запуске, отправит TCP-сообщение в UPDATESERVICE, в котором говорится, что "ЭТО ЗАЯВЛЕНИЕ" запустилось.

[UPDATESERVICE] Будут слушать сообщения. Если он получит сообщение о запуске USERAPPLICATION, он вызовет веб-службу, чтобы узнать, есть ли обновления. Если есть, пользователь будет уведомлен о закрытии приложения и позволит UPDATESERVICE обновить приложение. UPDATESERVICE загрузит соответствующие файлы и обновит приложение.

Теперь, когда я объяснил основы того, что я пытаюсь сделать, я могу задать свои конкретные вопросы, на которые мне нужно ответить. Все это связано с тем, как я должен создать свою службу Windows. Я также планирую использовать OmniThread для управления потоками.

Когда начинается моя служба, мне нужно создать TCP-сервер.

  • Должна ли создаваться служба TCP в ее собственном потоке?
  • Если служба TCP - это собственный поток, как сохранить поток в живых? В противном случае я могу запустить службу TCP, но я не уверен, какой код я бы использовал в блоке службы TCP, чтобы поддерживать поток.
  • Какое событие служб Windows должно создать службу TCP? OnExecute? OnStart? OnCreate? В конце концов, я прочитал, что неясно, какое событие следует использовать.
  • Когда служба TCP получает сообщение, чтобы что-то делать, должна ли выполняться работа в потоке службы TCP или новый поток, созданный с основного UPDATESERVICE? Например:
    • если служба TCP получает сообщение для проверки обновления с использованием HTTP, если поток службы TCP порождает новый поток для выполнения этой работы.
    • Или, если поток службы TCP отправит сообщение в UPDATESERVICE, чтобы создать новый поток для выполнения этой работы.
    • Это даже имеет значение?
  • Возможно ли запустить/остановить/зарегистрировать/отменить регистрацию службы Windows в коде Delphi?

Это все мои вопросы. Вероятно, для этого нет правильного/неправильного ответа, а просто предпочтения, основанного на опыте. Если вы создали сервисы с Delphi, у вас, вероятно, есть вклад, который мне пригодится. Если у вас есть более надежный проект, то базовый "начать сервис и спать" и готов поделиться им - даже если я не запускаю или просто код psuedo - я уверен, что это было бы неоценимо. Спасибо, что прочитал мой длинный вопрос. Если вы можете подумать о лучшем способе этого, пожалуйста, поделитесь своими мыслями. Я добавлю, что некоторые из наших приложений могут быть загружены и запущены широкой публикой, поэтому я не имею полного контроля над ожидаемыми средами. Любые советы/комментарии/помощь будут оценены.

4b9b3361

Ответ 1

быстрые ответы:

1 & 3) Да. Как правило, не выполняйте событие службы OnExecute. Создайте собственный поток из события службы OnStart. Поток может быть прерван при получении события службы OnStop.

2) вы сохраняете свой поток живым, как это (метод выполнения):

while not Terminated do
begin
  // do something
end;

4) обычно каждое клиентское соединение будет жить на собственной ветке. (т.е. TCP-сервер порождает новый поток для каждого клиента). Используйте хорошо известный стек, например Indy или ICS. Что касается обновления HTTP, вы можете сделать это в порожденном потоке клиентского подключения.

5) да, имейте в виду, что для этого вам нужны повышенные права.

В моей карьере я сделал немало услуг, и до сих пор я всегда использую один и тот же скелет для сервисного приложения:

unit u_svc_main;

interface

uses
  // Own units
  u_globals, u_eventlog, u_MyThread, 
  // Third party units
  // Delphi units
  Windows, Messages, Registry, SysUtils, Classes, SvcMgr;

type
  TMyService = class(TService)
    procedure ServiceCreate(Sender: TObject);
    procedure ServiceAfterUninstall(Sender: TService);
    procedure ServiceAfterInstall(Sender: TService);
    procedure ServiceShutdown(Sender: TService);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
  private
    { Private declarations }
    MyThread : TMyThread;
  public
    { Public declarations }
    function GetServiceController: TServiceController; override;
  end;

var MyService : TMyService;

implementation

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  MyService.Controller(CtrlCode);
end;

function TMyService.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TMyService.ServiceCreate(Sender: TObject);
begin
  DisplayName := 'myservice';
end;

procedure TMyService.ServiceAfterInstall(Sender: TService);
var
  Reg        : TRegistry;
  ImagePath  : string;
begin
  // create needed registry entries after service installation
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    // set service description
    if Reg.OpenKey(STR_REGKEY_SVC,False) then
    begin
      ImagePath := Reg.ReadString(STR_REGVAL_IMAGEPATH);
      Reg.WriteString(STR_REGVAL_DESCRIPTION, STR_INFO_SVC_DESC);
      Reg.CloseKey;
    end;
    // set message resource for eventlog
    if Reg.OpenKey(STR_REGKEY_EVENTMSG, True) then
    begin
      Reg.WriteString(STR_REGVAL_EVENTMESSAGEFILE, ImagePath);
      Reg.WriteInteger(STR_REGVAL_TYPESSUPPORTED, 7);
      Reg.CloseKey;
    end;
    // set installdir
    if ImagePath <> '' then
      if Reg.OpenKey(STR_REGKEY_FULL,True) then
      begin
        Reg.WriteString(STR_REGVAL_INSTALLDIR, ExtractFilePath(ImagePath));
        Reg.CloseKey;
      end;
  finally
    FreeAndNil(Reg);
  end;
end;

procedure TMyService.ServiceAfterUninstall(Sender: TService);
var
  Reg : TRegistry;
begin
  Reg := TRegistry.Create;
  try
    // delete self created registry keys
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    Reg.DeleteKey(STR_REGKEY_EVENTMSG);
  finally
    FreeAndNil(Reg);
  end;
end;

procedure TMyService.ServiceShutdown(Sender: TService);
var
  Stopped : boolean;
begin
  // is called when windows shuts down
  ServiceStop(Self, Stopped);
end;

procedure TMyService.ServiceStart(Sender: TService; var Started: Boolean);
begin
  Started := False;
  try
    MyThread := TMyThread.Create;
    MyThread.Resume;
    NTEventLog.Add(Eventlog_Success, STR_INFO_SVC_STARTED);
    Started := True;
  except
    on E : Exception do
    begin
      // add event in eventlog with reason why the service couldn't start
      NTEventLog.Add(Eventlog_Error_Type, Format(STR_INFO_SVC_STARTFAIL, [E.Message]));
    end;
  end;
end;

procedure TMyService.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  try
    Stopped := True; // always stop service, even if we had exceptions, this is to prevent "stuck" service (must reboot then)
    MyThread.Terminate;
    // give MyThread 60 seconds to terminate
    if WaitForSingleObject(MyThread.ThreadEvent, 60000) = WAIT_OBJECT_0 then
    begin
      FreeAndNil(MyThread);
      NTEventLog.Add(Eventlog_Success,STR_INFO_SVC_STOPPED);
    end;
  except
    on E : Exception do
    begin
      // add event in eventlog with reason why the service couldn't stop
      NTEventLog.Add(Eventlog_Error_Type, Format(STR_INFO_SVC_STOPFAIL, [E.Message]));
    end;
  end;
end;

end.