-
-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Fix Windows installer cleanup #8576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0e00f5d
919deae
bb76913
6574e0c
b8c190b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -269,6 +269,8 @@ ArchitecturesAllowed=x64compatible arm64 | |
| ArchitecturesInstallIn64BitMode=x64compatible arm64 | ||
| SetupLogging=yes | ||
| UninstallLogging=yes | ||
| CloseApplications=yes | ||
| RestartApplications=no | ||
|
|
||
| [Languages] | ||
| {% for locale in LOCALES %} | ||
|
|
@@ -291,10 +293,6 @@ Name: "{autoprograms}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}" | |
| Name: "{autodesktop}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}"; Tasks: desktopicon | ||
|
|
||
| [Run] | ||
| ; Stop/delete any existing service | ||
| Filename: "{sys}\sc.exe"; Parameters: "stop ""{#SvcName}"""; Flags: runhidden | ||
| Filename: "{sys}\sc.exe"; Parameters: "delete ""{#SvcName}"""; Flags: runhidden | ||
|
|
||
| ; Create service | ||
| Filename: "{sys}\sc.exe"; \ | ||
| Parameters: "create ""{#SvcName}"" binPath= ""{code:ServiceExecutablePath}"" start= delayed-auto DisplayName= ""{#SvcDisplayName}"""; \ | ||
|
|
@@ -318,6 +316,124 @@ Filename: "{sys}\sc.exe"; Parameters: "delete ""{#SvcName}"""; Flags: runhidden | |
| Type: filesandordirs; Name: "{#ProgramDataDir}" | ||
|
|
||
| [Code] | ||
| const | ||
| ServiceStopTimeoutMs = 20000; | ||
| ServicePollIntervalMs = 250; | ||
| UninstallRegSubKey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#SetupSetting("AppId")}_is1'; | ||
|
|
||
| function ExtractExecutablePath(const CommandLine: String): String; | ||
| var | ||
| S: String; | ||
| EndQuote: Integer; | ||
| FirstSpace: Integer; | ||
| begin | ||
| S := Trim(CommandLine); | ||
| if S = '' then begin | ||
| Result := ''; | ||
| exit; | ||
| end; | ||
|
|
||
| if S[1] = '"' then begin | ||
| Delete(S, 1, 1); | ||
| EndQuote := Pos('"', S); | ||
| if EndQuote > 0 then begin | ||
| Result := Copy(S, 1, EndQuote - 1); | ||
| end else begin | ||
| Result := S; | ||
| end; | ||
| exit; | ||
| end; | ||
|
|
||
| FirstSpace := Pos(' ', S); | ||
| if FirstSpace > 0 then begin | ||
| Result := Copy(S, 1, FirstSpace - 1); | ||
| end else begin | ||
| Result := S; | ||
| end; | ||
|
Comment on lines
+324
to
+352
|
||
| end; | ||
|
|
||
| procedure RemoveStaleUninstallEntry(const RootKey: Integer; const RootName: String); | ||
| var | ||
| UninstallString: String; | ||
| UninstallExePath: String; | ||
| begin | ||
| if not RegQueryStringValue(RootKey, UninstallRegSubKey, 'UninstallString', UninstallString) then begin | ||
| exit; | ||
| end; | ||
|
|
||
| UninstallExePath := ExtractExecutablePath(UninstallString); | ||
| if (UninstallExePath = '') or FileExists(UninstallExePath) then begin | ||
| exit; | ||
| end; | ||
|
|
||
| Log( | ||
| 'Removing stale uninstall entry at root=' + RootName + | ||
| ' key=' + UninstallRegSubKey + | ||
| ' (missing uninstaller: ' + UninstallExePath + ')' | ||
| ); | ||
| if not RegDeleteKeyIncludingSubkeys(RootKey, UninstallRegSubKey) then begin | ||
| Log('Failed to remove stale uninstall entry'); | ||
| end; | ||
| end; | ||
|
|
||
| function ExecSc(const Parameters: String; var ExitCode: Integer): Boolean; | ||
| begin | ||
| Result := Exec( | ||
| ExpandConstant('{sys}\sc.exe'), | ||
| Parameters, | ||
| '', | ||
| SW_HIDE, | ||
| ewWaitUntilTerminated, | ||
| ExitCode | ||
| ); | ||
| if Result then begin | ||
| Log('sc.exe ' + Parameters + ' (exit=' + IntToStr(ExitCode) + ')'); | ||
| end else begin | ||
| Log('failed to launch sc.exe ' + Parameters); | ||
| end; | ||
| end; | ||
|
|
||
| procedure StopAndDeleteService; | ||
| var | ||
| ExitCode: Integer; | ||
| begin | ||
| ExecSc('stop "{#SvcName}"', ExitCode); | ||
| ExecSc('delete "{#SvcName}"', ExitCode); | ||
| end; | ||
|
|
||
| function WaitForServiceDelete(const TimeoutMs: Integer): Boolean; | ||
| var | ||
| ExitCode: Integer; | ||
| ElapsedMs: Integer; | ||
| begin | ||
| ElapsedMs := 0; | ||
| while ElapsedMs <= TimeoutMs do begin | ||
| if ExecSc('query "{#SvcName}"', ExitCode) then begin | ||
| // SERVICE_DOES_NOT_EXIST | ||
| if ExitCode = 1060 then begin | ||
| Result := True; | ||
| exit; | ||
| end; | ||
| end; | ||
| Sleep(ServicePollIntervalMs); | ||
| ElapsedMs := ElapsedMs + ServicePollIntervalMs; | ||
| end; | ||
| Result := False; | ||
| end; | ||
|
|
||
| procedure CurStepChanged(CurStep: TSetupStep); | ||
| begin | ||
| if CurStep <> ssInstall then begin | ||
| exit; | ||
| end; | ||
|
|
||
| Log('Pre-install service cleanup started'); | ||
| StopAndDeleteService; | ||
| if not WaitForServiceDelete(ServiceStopTimeoutMs) then begin | ||
| Log('Timed out waiting for service deletion; continuing install'); | ||
| end; | ||
| end; | ||
|
|
||
| function ServiceExecutablePath(_Param: String): String; | ||
| var | ||
| Arm64ServicePath: String; | ||
|
|
@@ -331,6 +447,9 @@ end; | |
|
|
||
| function InitializeSetup: Boolean; | ||
| begin | ||
| RemoveStaleUninstallEntry(HKLM, 'HKLM'); | ||
| RemoveStaleUninstallEntry(HKCU, 'HKCU'); | ||
|
|
||
| Dependency_AddWebView2; | ||
| Dependency_AddVC2015To2022; | ||
| Result := True; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ServiceStopTimeoutMsis used as the timeout for waiting on service deletion (WaitForServiceDelete), not for waiting on the service to stop. Renaming this constant (or splitting stop vs delete timeouts) would make the intent clearer and reduce confusion for future changes.