mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-10-28 02:38:55 -04:00
Improve Windows installer (#674)
This commit is contained in:
1 parent
a28caf1bc9
commit
282ec1d325
1 file changed
+130
-47
@@ -52,25 +52,25 @@ Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
|
||||
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\favicon.ico"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
; Create service only if it doesn't exist (fresh install)
|
||||
Filename: "{sys}\sc.exe"; Parameters: "create ""{#MyServiceName}"" binPath= ""\""{app}\{#MyAppExeName}\"""" DisplayName= ""{#MyAppName}"" start= auto"; Tasks: installservice; Flags: runhidden; Check: not ServiceExists('{#MyServiceName}')
|
||||
Filename: "{sys}\sc.exe"; Parameters: "description ""{#MyServiceName}"" ""Cleanuparr download management service"""; Tasks: installservice; Flags: runhidden; Check: not ServiceExists('{#MyServiceName}')
|
||||
|
||||
; Start service (both fresh install and update)
|
||||
Filename: "{sys}\sc.exe"; Parameters: "start ""{#MyServiceName}"""; Tasks: installservice; Flags: runhidden
|
||||
|
||||
; Open web interface
|
||||
Filename: "http://localhost:11011"; Description: "Open Cleanuparr Web Interface"; Flags: postinstall shellexec nowait; Check: IsTaskSelected('installservice')
|
||||
|
||||
; Run directly (if not installed as service)
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "Run {#MyAppName} Application"; Flags: nowait postinstall skipifsilent; Check: not IsTaskSelected('installservice')
|
||||
|
||||
[UninstallRun]
|
||||
Filename: "{sys}\sc.exe"; Parameters: "stop ""{#MyServiceName}"""; Flags: runhidden; Check: ServiceExists('{#MyServiceName}')
|
||||
Filename: "{sys}\timeout.exe"; Parameters: "/t 5"; Flags: runhidden; Check: ServiceExists('{#MyServiceName}')
|
||||
Filename: "{sys}\sc.exe"; Parameters: "delete ""{#MyServiceName}"""; Flags: runhidden; Check: ServiceExists('{#MyServiceName}')
|
||||
|
||||
[Code]
|
||||
var
|
||||
DeleteUserData: Boolean;
|
||||
|
||||
procedure LogInstaller(const Msg: string);
|
||||
var
|
||||
LogFile, Line: string;
|
||||
begin
|
||||
LogFile := ExpandConstant('{app}\cleanuparr-installer.log');
|
||||
Line := '[' + GetDateTimeString('yyyy/mm/dd hh:nn:ss', '-', ':') + '] ' + Msg + #13#10;
|
||||
SaveStringToFile(LogFile, Line, True);
|
||||
end;
|
||||
|
||||
function ServiceExists(ServiceName: string): Boolean;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
@@ -81,23 +81,10 @@ end;
|
||||
function IsServiceRunning(ServiceName: string): Boolean;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
TempFile: string;
|
||||
StatusOutput: AnsiString;
|
||||
begin
|
||||
Result := False;
|
||||
TempFile := ExpandConstant('{tmp}\service_status.txt');
|
||||
|
||||
// Use PowerShell to get service status
|
||||
if Exec(ExpandConstant('{sys}\WindowsPowerShell\v1.0\powershell.exe'),
|
||||
'-Command "try { (Get-Service -Name ''' + ServiceName + ''' -ErrorAction Stop).Status } catch { ''NotFound'' }" > "' + TempFile + '"',
|
||||
'', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0) then
|
||||
begin
|
||||
if LoadStringFromFile(TempFile, StatusOutput) then
|
||||
begin
|
||||
Result := (Pos('Running', StatusOutput) > 0);
|
||||
end;
|
||||
DeleteFile(TempFile);
|
||||
end;
|
||||
Result := Exec(ExpandConstant('{sys}\WindowsPowerShell\v1.0\powershell.exe'),
|
||||
'-Command "if ((Get-Service -Name ''' + ServiceName + ''' -ErrorAction SilentlyContinue).Status -eq ''Running'') { exit 0 } else { exit 1 }"',
|
||||
'', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0);
|
||||
end;
|
||||
|
||||
function WaitForServiceStop(ServiceName: string; TimeoutSeconds: Integer): Boolean;
|
||||
@@ -118,24 +105,100 @@ begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
procedure StopAndDeleteService(const Name: string);
|
||||
var
|
||||
ResultCode, i: Integer;
|
||||
begin
|
||||
if not ServiceExists(Name) then
|
||||
begin
|
||||
LogInstaller('Service ' + Name + ' not present, nothing to remove');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
LogInstaller('Stopping service ' + Name);
|
||||
Exec(ExpandConstant('{sys}\sc.exe'), 'stop "' + Name + '"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
if not WaitForServiceStop(Name, 30) then
|
||||
LogInstaller('WARNING: service did not report STOPPED within 30s');
|
||||
|
||||
LogInstaller('Force-killing the service process');
|
||||
Exec(ExpandConstant('{sys}\taskkill.exe'), '/f /t /fi "SERVICES eq ' + Name + '"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
|
||||
for i := 1 to 10 do
|
||||
begin
|
||||
Exec(ExpandConstant('{sys}\sc.exe'), 'delete "' + Name + '"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
if not ServiceExists(Name) then
|
||||
begin
|
||||
LogInstaller('Service ' + Name + ' deleted (attempt ' + IntToStr(i) + ')');
|
||||
Exit;
|
||||
end;
|
||||
Sleep(1000);
|
||||
end;
|
||||
LogInstaller('WARNING: service ' + Name + ' still present after 10 delete attempts (last sc delete ResultCode=' + IntToStr(ResultCode) + ')');
|
||||
end;
|
||||
|
||||
procedure CreateOrUpdateService();
|
||||
var
|
||||
ResultCode: Integer;
|
||||
Ok: Boolean;
|
||||
begin
|
||||
if not IsTaskSelected('installservice') then
|
||||
begin
|
||||
if ServiceExists('{#MyServiceName}') then
|
||||
begin
|
||||
LogInstaller('Service task deselected on upgrade, removing existing service');
|
||||
StopAndDeleteService('{#MyServiceName}');
|
||||
end;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if ServiceExists('{#MyServiceName}') then
|
||||
begin
|
||||
LogInstaller('Refreshing existing service definition');
|
||||
Ok := Exec(ExpandConstant('{sys}\sc.exe'), ExpandConstant('config "{#MyServiceName}" binPath= "\"{app}\{#MyAppExeName}\"" start= auto'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0);
|
||||
end
|
||||
else
|
||||
begin
|
||||
LogInstaller('Creating service');
|
||||
Ok := Exec(ExpandConstant('{sys}\sc.exe'), ExpandConstant('create "{#MyServiceName}" binPath= "\"{app}\{#MyAppExeName}\"" DisplayName= "{#MyAppName}" start= auto'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0);
|
||||
end;
|
||||
|
||||
if not Ok then
|
||||
begin
|
||||
LogInstaller('ERROR: failed to create or configure the service, ResultCode=' + IntToStr(ResultCode));
|
||||
if not WizardSilent() then
|
||||
MsgBox('Failed to install the Cleanuparr service (error ' + IntToStr(ResultCode) + '). See ' + ExpandConstant('{app}\cleanuparr-installer.log') + ' for details.', mbError, MB_OK);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Exec(ExpandConstant('{sys}\sc.exe'), 'description "{#MyServiceName}" "Cleanuparr download management service"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
|
||||
LogInstaller('Starting service');
|
||||
if not (Exec(ExpandConstant('{sys}\sc.exe'), 'start "{#MyServiceName}"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and ((ResultCode = 0) or (ResultCode = 1056))) then
|
||||
begin
|
||||
LogInstaller('ERROR: failed to start the service, ResultCode=' + IntToStr(ResultCode));
|
||||
if not WizardSilent() then
|
||||
MsgBox('The Cleanuparr service was installed but failed to start (error ' + IntToStr(ResultCode) + '). See ' + ExpandConstant('{app}\cleanuparr-installer.log') + ' for details.', mbError, MB_OK);
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeSetup(): Boolean;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Result := True;
|
||||
|
||||
// If service exists and is running, stop it for the update
|
||||
|
||||
if ServiceExists('{#MyServiceName}') and IsServiceRunning('{#MyServiceName}') then
|
||||
begin
|
||||
if MsgBox('Cleanuparr service is currently running and needs to be stopped for the installation. Continue?',
|
||||
if MsgBox('Cleanuparr service is currently running and needs to be stopped for the installation. Continue?',
|
||||
mbConfirmation, MB_YESNO) = IDYES then
|
||||
begin
|
||||
LogInstaller('Stopping running service for update');
|
||||
Exec(ExpandConstant('{sys}\sc.exe'), 'stop "{#MyServiceName}"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
|
||||
|
||||
if not WaitForServiceStop('{#MyServiceName}', 30) then
|
||||
begin
|
||||
MsgBox('Warning: Service took longer than expected to stop. Installation will continue but you may need to restart the service manually.',
|
||||
mbInformation, MB_OK);
|
||||
LogInstaller('Service did not stop in time, force-killing the service process');
|
||||
Exec(ExpandConstant('{sys}\taskkill.exe'), '/f /t /fi "SERVICES eq {#MyServiceName}"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
end;
|
||||
end
|
||||
else
|
||||
@@ -146,25 +209,45 @@ begin
|
||||
end;
|
||||
|
||||
function InitializeUninstall(): Boolean;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Result := True;
|
||||
|
||||
if ServiceExists('{#MyServiceName}') then
|
||||
DeleteUserData := False;
|
||||
|
||||
if not UninstallSilent() then
|
||||
begin
|
||||
if MsgBox('Cleanuparr service will be stopped and removed. Continue with uninstallation?',
|
||||
mbConfirmation, MB_YESNO) = IDYES then
|
||||
if MsgBox('Do you want to keep Cleanuparr''s configuration and logs?' + #13#10#13#10 +
|
||||
'Click Yes to keep them, or No to delete all data.',
|
||||
mbConfirmation, MB_YESNO) = IDNO then
|
||||
begin
|
||||
if IsServiceRunning('{#MyServiceName}') then
|
||||
begin
|
||||
Exec(ExpandConstant('{sys}\sc.exe'), 'stop "{#MyServiceName}"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
WaitForServiceStop('{#MyServiceName}', 30);
|
||||
end;
|
||||
end
|
||||
DeleteUserData := True;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
begin
|
||||
if CurStep = ssPostInstall then
|
||||
CreateOrUpdateService();
|
||||
end;
|
||||
|
||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||
begin
|
||||
if CurUninstallStep = usUninstall then
|
||||
begin
|
||||
LogInstaller('=== Uninstall started ===');
|
||||
StopAndDeleteService('{#MyServiceName}');
|
||||
if RegDeleteKeyIncludingSubkeys(HKLM, 'SYSTEM\CurrentControlSet\Services\EventLog\Application\{#MyServiceName}') then
|
||||
LogInstaller('Removed EventLog source key')
|
||||
else
|
||||
LogInstaller('EventLog source key not present');
|
||||
end
|
||||
else if CurUninstallStep = usPostUninstall then
|
||||
begin
|
||||
if DeleteUserData then
|
||||
begin
|
||||
Result := False;
|
||||
LogInstaller('Deleting configuration and logs');
|
||||
DelTree(ExpandConstant('{app}\config'), True, True, True);
|
||||
DeleteFile(ExpandConstant('{app}\cleanuparr-installer.log'));
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
Reference in new issue
Block a user