Служба Delphi XE2 не останавливается должным образом

Я создал несколько сервисов в Delphi 7, и у меня не было этой проблемы. Теперь, когда я запустил новое сервисное приложение в XE2, оно не останавливается должным образом. Я не знаю, что я делаю неправильно, или это может быть ошибка в сервисах XE2.

Процедура выполнения выглядит так:

procedure TMySvc.ServiceExecute(Sender: TService);
begin
  try
    CoInitialize(nil);
    Startup;
    try
      while not Terminated do begin
        DoSomething; //Problem persists even when nothing's here
      end;
    finally
      Cleanup;
      CoUninitialize;
    end;
  except
    on e: exception do begin
      PostLog('EXCEPTION in Execute: '+e.Message);
    end;
  end;
end;

У меня никогда не было исключения, как видите, я регистрирую любое исключение. PostLog сохраняется в файл INI, который отлично работает. Сейчас я использую компоненты ADO, поэтому использую CoInitialize() и CoUninitialize. Он подключается к БД и правильно выполняет свою работу. Проблема возникает только тогда, когда я останавливаю эту службу. Windows выдает мне следующее сообщение:

Ошибка первой остановки

Потом служба продолжается. Я должен остановить это во второй раз. Во второй раз он останавливается, но со следующим сообщением:

Ошибка второй остановки

Файл журнала показывает, что служба успешно освободилась (событие OnDestroy было зарегистрировано), но никогда не останавливалась (OnStop никогда не регистрировалась).

В приведенном выше коде у меня есть две процедуры Startup и Cleanup. Они просто создают / уничтожают и инициализируют / деинициализируют мои необходимые вещи ...

procedure TMySvc.Startup;
begin
  FUpdateThread:= TMyUpdateThread.Create;
    FUpdateThread.OnLog:= LogUpdate;
    FUpdateThread.Resume;
end;

procedure TMySvc.Cleanup;
begin
  FUpdateThread.Terminate;
end;

Как видите, у меня запущен вторичный поток. Эта служба фактически имеет множество потоков, работающих таким образом, и основной поток службы регистрирует только события из каждого потока. У каждого потока разные обязанности. Потоки сообщают правильно, и они также должным образом завершаются.

Что могло быть причиной этой остановки? Если мой опубликованный код ничего не раскрывает, я могу опубликовать дополнительный код позже - просто нужно его «преобразовать» из-за внутреннего именования и т. Д.

ИЗМЕНИТЬ

Я только что начал НОВЫЙ сервисный проект в Delphi XE2, и у меня такая же проблема. Это весь мой код ниже:

unit JDSvc;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, JDSvcMgr;

type
  TJDService = class(TService)
    procedure ServiceExecute(Sender: TService);
  private
    FAfterInstall: TServiceEvent;
  public
    function GetServiceController: TServiceController; override;
  end;

var
  JDService: TJDService;

implementation

{$R *.DFM}

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

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

procedure TJDService.ServiceExecute(Sender: TService);
begin
  while not Terminated do begin

  end;
end;

end.

person Jerry Dodge    schedule 13.02.2012    source источник
comment
Вряд ли ошибка в коде Delphi. Можете ли вы сократить это до минимального воспроизведения.   -  person David Heffernan    schedule 13.02.2012
comment
^^ +1. Кажется, ваш поток никогда не завершается, поэтому тайм-аут в SCM   -  person whosrdaddy    schedule 13.02.2012
comment
IMHO процедура TMySvc.Cleanup неизбежно создает проблемы. Вы завершаете FUpdateThread, но не знаете, когда он действительно завершается. Добавьте WaitFor или используйте объект synchro для правильного определения завершения. Дополнительную информацию можно найти здесь: eonclash.com/Tutorials/Multithreading/MartinHarvey1. 1 / Ch5.html   -  person whosrdaddy    schedule 13.02.2012
comment
Предположительно где-то вы запускаете FUpdateThread.Free, который вызовет WaitFor   -  person David Heffernan    schedule 13.02.2012
comment
Мой поток инициализирует себя с помощью FreeOnTerminate:= True;   -  person Jerry Dodge    schedule 13.02.2012
comment
@DavidHeffernan Вы уверены? Когда я смотрю на TThread.Free, я вижу традиционный TObject.Free. Также добавить WaitFor не получилось, проблема осталась.   -  person Jerry Dodge    schedule 13.02.2012
comment
@Jerry Ну да. Бесплатно - это статический метод! Могу я сослаться на свой ответ на ваш вопрос: stackoverflow.com/questions/8548843/ Дело в том, что находится в виртуальном деструкторе Destroy. И да, WaitFor называется.   -  person David Heffernan    schedule 13.02.2012
comment
Что касается FreeOnTerminate, это может легко стать проблемой. Зачем ты это делаешь? Почему вы хотите это сделать. Установите FreeOnTerminate на False и вызовите FUpdateThread.Free из Cleanup. Нет смысла звонить Terminate, поскольку это будет сделано за вас, когда вы позвоните Free.   -  person David Heffernan    schedule 13.02.2012
comment
@JerryDodge TThread.destroy вызывает waitfor. Один хороший совет: никогда не используйте freeOnterminate, контролируйте завершение потока самостоятельно, и вы больше никогда не столкнетесь с такими проблемами ...   -  person whosrdaddy    schedule 13.02.2012
comment
Я только что воспроизвел ту же проблему в НОВОМ сервисе XE2, в котором даже ничего нет. Все, что я делаю, это добавляю while not Terminated do begin .. end; в OnExecute обработчик событий. См. Добавленный код выше.   -  person Jerry Dodge    schedule 13.02.2012
comment
добавить ProcessRequests (False); в твоей петле, и ты будешь в порядке   -  person whosrdaddy    schedule 13.02.2012
comment
@whosrdaddy Вот и все! (Кстати, это ServiceThread.ProcessRequests(False);). Так это что-то новое со времен D7 или что-то, что было там, но не всплывало до сих пор?   -  person Jerry Dodge    schedule 13.02.2012
comment
@JerryDodge Я не знаю, я смотрю исходный код D5, и он примерно такой же. Обновил мой ответ, кстати. Моя другая точка зрения все еще актуальна   -  person whosrdaddy    schedule 13.02.2012
comment
@whosrdaddy Мне не показалось, что ваш комментарий относительно ProcessRequests, когда я писал свой ответ. Я пришел к такому же выводу независимо. Однако я удалил свой ответ, поскольку вы явно опередили меня.   -  person David Heffernan    schedule 13.02.2012
comment
Опыт Whosrdaddy ценен, как я видел в другом месте, и он заслуживает того, чтобы его репутация повысилась здесь. Принятый.   -  person Jerry Dodge    schedule 13.02.2012
comment
@Jerry Это был хороший вопрос, и вы хорошо ответили на запросы о дополнительной информации и небольшом примере. Заслуженные голоса за. Естественно, вы получили один отрицательный голос, но похоже, что все вопросы Delphi подходят.   -  person David Heffernan    schedule 13.02.2012
comment
В конце концов, дело было не в том, что я делал что-то неправильно, а скорее в том, что я делал что-то неправильно.   -  person Jerry Dodge    schedule 14.02.2012


Ответы (1)


посмотрите исходный код метода Execute:

procedure TServiceThread.Execute;
var
  msg: TMsg;
  Started: Boolean;
begin
  PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE); { Create message queue }
  try
    // Allow initialization of the Application object after
    // StartServiceCtrlDispatcher to prevent conflicts under
    // Windows 2003 Server when registering a class object with OLE.
    if Application.DelayInitialize then
      Application.Initialize;
    FService.Status := csStartPending;
    Started := True;
    if Assigned(FService.OnStart) then FService.OnStart(FService, Started);
    if not Started then Exit;
    try
      FService.Status := csRunning;
      if Assigned(FService.OnExecute) then
        FService.OnExecute(FService)
      else
        ProcessRequests(True);
      ProcessRequests(False);
    except
      on E: Exception do
        FService.LogMessage(Format(SServiceFailed,[SExecute, E.Message]));
    end;
  except
    on E: Exception do
      FService.LogMessage(Format(SServiceFailed,[SStart, E.Message]));
  end;
end;

как вы можете видеть, если вы не назначите метод OnExecute, Delphi будет обрабатывать запросы SCM (запуск службы, остановка, ...), пока служба не будет остановлена. Когда вы создаете цикл в Service.Execute, вы должны сами обрабатывать запросы SCM, вызывая ProcessRequests(False). Хорошая привычка - не использовать Service.execute и запускать рабочий поток в событии Service.OnStart, а завершать / освобождать его в событии Service.OnStop.

Как сказано в комментариях, еще одна проблема заключается в FUpdateThread.Terminate части. Дэвид Хеффернан попал в точку с комментарием Free / WaitFor. Убедитесь, что вы правильно завершили поток, используя объекты синхронизации.

person whosrdaddy    schedule 13.02.2012
comment
+1 Я рекомендую вам поменять местами два пункта в этом ответе. ProcessRequests - это ключ, и он должен идти первым. - person David Heffernan; 13.02.2012