Переустановка службы Windows Inno Setup

Вопрос или проблема

Мне нужно создать установщик для службы Windows. Эта служба должна запускаться сразу после установки и также быть включенной сразу после входа в систему.

В настоящее время служба Windows устанавливается и запускается правильно сразу после установки. Однако мне нужно, чтобы она переустанавливалась, если пользователь снова запускает установщик для службы.

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

    [Setup]
AppName=my_service
AppVersion=1.1.0.1
DefaultDirName=C:\ws-my_service\driver   
DisableDirPage=yes
DisableProgramGroupPage=yes         
OutputDir=Output3
OutputBaseFilename=my_service
Compression=lzma2
SolidCompression=true

[Files]
Source: "C:\Users\Home\Desktop\my_service\my_service\bin\Release\net8.0\win-x64\publish\*"; DestDir: "{app}"; Flags: ignoreversion

[Run]
Filename: "{sys}\sc.exe"; Parameters: "create my_service binPath= ""{app}\my_service.exe"" start= auto"; Flags: runhidden waituntilterminated; Check: not IsServiceInstalled('ws_agent_admin');
Filename: "{sys}\sc.exe"; Parameters: "start my_service"; Flags: runhidden; Check: not IsServiceRunning('my_service');

[UninstallRun]
Filename: "{sys}\sc.exe"; Parameters: "stop my_service"; Flags: runhidden waituntilterminated; Check: IsServiceRunning('my_service'); RunOnceId: "StopService"
Filename: "{sys}\sc.exe"; Parameters: "delete my_service"; Flags: runhidden waituntilterminated; Check: IsServiceInstalled('my_service'); RunOnceId: "DeleteService"

[Code]
var
  TempLogFile: String;

function RunCommandAndLogOutput(Command, Params: String): Boolean;
var
  ErrorCode: Integer;
  Output: TStringList;
begin
  Result := Exec(Command, Params + ' > "' + TempLogFile + '" 2>&1', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
  
  Output := TStringList.Create;
  try
    if FileExists(TempLogFile) then
    begin
      Output.LoadFromFile(TempLogFile);
      Log('Вывод команды "' + Command + ' ' + Params + '":');
      Log(Output.Text);
      DeleteFile(TempLogFile);
    end;
  finally
    Output.Free;
  end;
end;

function IsServiceInstalled(ServiceName: String): Boolean;
var
  ResultCode: Integer;
begin
  Log('Проверка, установлен ли сервис ' + ServiceName + '...');
  Result := Exec('{sys}\sc.exe', 'query ' + ServiceName, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0);
end;

function IsServiceRunning(ServiceName: String): Boolean;
var
  ResultCode: Integer;
begin
  Log('Проверка, работает ли сервис ' + ServiceName + '...');
  Result := Exec('{sys}\sc.exe', 'query ' + ServiceName, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0);
end;

function StopService(ServiceName: String): Boolean;
var
  Attempts: Integer;
begin
  Log('Попытка остановить сервис ' + ServiceName + '...');
  Result := RunCommandAndLogOutput('{sys}\sc.exe', 'stop ' + ServiceName);
  
  Attempts := 0;
  while (IsServiceRunning(ServiceName)) and (Attempts < 10) do // Ждем максимум 10 попыток (10*500мс)
  begin
    Log('Сервис ' + ServiceName + ' все еще работает. Ждем его остановки...');
    Sleep(500);
    Inc(Attempts);
  end;

  if IsServiceRunning(ServiceName) then
    Log('Не удалось остановить сервис ' + ServiceName + ' после ожидания.');
end;

function RemoveService(ServiceName: String): Boolean;
begin
  Log('Попытка удалить сервис ' + ServiceName + '...');
  Result := RunCommandAndLogOutput('{sys}\sc.exe', 'delete ' + ServiceName);
end;

function InitializeSetup(): Boolean;
begin
  TempLogFile := ExpandConstant('{tmp}\sc_output.log');
  Log('InitializeSetup: Проверка, нужно ли останавливать и удалять сервис.');
  
  if IsServiceInstalled('my_service') then
  begin
    if StopService('my_service') then
      RemoveService('my_service');
  end;
  
  Result := True;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep = ssPostInstall then
  begin
    Log('CurStepChanged: Запуск сервиса my_service после установки...');
    RunCommandAndLogOutput('{sys}\sc.exe', 'start my_service');
  end;
end;

в чем может быть проблема? как я могу это исправить?

Ответ или решение

Чтобы успешно переустановить Windows-сервис с использованием Inno Setup, вам нужно учесть несколько ключевых аспектов. Давайте разберем вашу задачу и предложим решения, ссылаясь на предоставленный вами код.

Проблема

Вы испытываете трудности с остановкой и удалением существующего сервиса перед его переустановкой. Ошибки, которые вы получаете, могут быть вызваны тем, что сервис все еще работает или не был корректно остановлен перед удалением.

Решения

1. Убедитесь, что сервис действительно остановлен

Прежде чем пытаться удалить сервис, убедитесь, что он полностью остановлен. Используйте циклы ожидания или увеличьте время ожидания между попытками остановки и удаления сервиса. В вашем коде уже реализована логика ожидания (максимум 10 попыток по 500 мс). Однако это может быть недостаточно, особенно для сервиса, который может не реагировать на команды немедленно.

2. Усовершенствуйте логику остановки сервиса

Можно модифицировать функцию StopService таким образом, чтобы она не только пыталась остановить сервис, но и проверяла, что он действительно остановился, прежде чем продолжать выполнение. Используйте следующий вариант:

function StopService(ServiceName: String): Boolean;
var
  Attempts: Integer;
begin
  Log('Attempting to stop service ' + ServiceName + '...');
  Result := RunCommandAndLogOutput('{sys}\sc.exe', 'stop ' + ServiceName);

  Attempts := 0;
  while (IsServiceRunning(ServiceName)) and (Attempts < 20) do // Увеличиваем количество попыток
  begin
    Log('Service ' + ServiceName + ' is still running. Waiting for it to stop...');
    Sleep(1000); // Увеличиваем время ожидания
    Inc(Attempts);
  end;

  if IsServiceRunning(ServiceName) then
  begin
    Log('Failed to stop service ' + ServiceName + ' after waiting.');
    Result := False;
  end;
end;

3. Добавьте обработку ошибок

В вашем коде отсутствуют несколько обработок ошибок, которые важны для протоколирования и отладки. При попытке остановить или удалить сервис стоит использовать Exec с проверкой результата выполнения.

4. Упрощение процесса

Оптимизируйте порядок ваших команд в InitializeSetup. Например, укажите сначала остановить сервис, а затем удалять его:

function InitializeSetup(): Boolean;
begin
  TempLogFile := ExpandConstant('{tmp}\sc_output.log');
  Log('InitializeSetup: Checking if service needs to be stopped and removed.');

  if IsServiceInstalled('my_service') then
  begin
    if StopService('my_service') then
    begin
      if RemoveService('my_service') then
        Log('Service successfully removed.');
    end;
  end;

  Result := True;
end;

Заключение

Соблюдение предложенных рекомендаций поможет вам успешно переустанавливать Windows-сервис через Inno Setup. Убедитесь, что все функции правильно логируют свои действия, что значительно облегчит вам поиск причины неполадок. Управляйте ожиданием при остановке и удалении сервиса, и вы избежите текущих проблем.

Оцените материал
Добавить комментарий

Капча загружается...