Skip to content

Commit 3fa39c7

Browse files
committed
feat(installer): Safely uninstall v1 installs on v2 upgrade. Prevent upgrades from 32-bit to 64-bit.
This change makes transitioning from v1 to v2 much easier. The new v2 installer will now detect if v1 is installed, and safely uninstall it. It can optionally preserve the existing SyncTrayzor config (including syncthing.exe), which is the default. I can't be bothered to add the same logic for 32-bit installs (which is more complicated anyway). Just prevent the installer from running if someone is currently on the 32-bit v1 version. They will have to uninstall and reinstall manually.
1 parent ae47bb6 commit 3fa39c7

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed

installer/common.iss

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#define AppPublisher "SyncTrayzor"
99
#define AppURL "https://github.com/GermanCoding/SyncTrayzor"
1010
#define AppDataFolder "SyncTrayzor"
11+
#define V1AppData "{userappdata}\SyncTrayzor"
12+
#define LegacyX86AppId "{c9bab27b-d754-4b62-ad8c-3509e1cac15c}"
13+
#define BackupStamp "_v1_backup"
1114
#define SyncthingFolder "Syncthing"
1215

1316
[Setup]
@@ -70,6 +73,20 @@ Name: "{autodesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: deskto
7073
Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall; Parameters: {code:SyncTrayzorStartFlags}; Check: ShouldStartSyncTrayzor
7174

7275
[Code]
76+
// -------------------------------------------------------------------
77+
// GLOBALS
78+
// -------------------------------------------------------------------
79+
var
80+
OldVersionDetected: Boolean;
81+
OldVersion : string;
82+
OldUninstString : string;
83+
OldInstallDir : string;
84+
BackupDir : string;
85+
86+
InfoPage : TWizardPage;
87+
PgLabel : TNewStaticText;
88+
PgCheckbox : TNewCheckBox;
89+
7390
procedure BumpInstallCount;
7491
var
7592
FileContents: AnsiString;
@@ -213,6 +230,162 @@ begin
213230
end;
214231
end;
215232
233+
// -------------------------------------------------------------------
234+
// v1 DETECTION -----------------------------------------------------
235+
// -------------------------------------------------------------------
236+
function DetectV1(): Boolean;
237+
var
238+
RootKey, UninstPath: string;
239+
OldPackedVersion: Int64;
240+
begin
241+
Result := False;
242+
243+
RootKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\';
244+
UninstPath := RootKey + '{#AppId}' + '_is1';
245+
246+
if RegQueryStringValue(HKLM, UninstPath, 'DisplayVersion', OldVersion) then
247+
begin
248+
if not StrToVersion(OldVersion, OldPackedVersion) then
249+
begin
250+
Log('Invalid version format: ' + OldVersion);
251+
Result := False;
252+
exit;
253+
end;
254+
255+
Result := ComparePackedVersion(OldPackedVersion, PackVersionComponents(2,0,0,0)) < 0;
256+
RegQueryStringValue(HKLM, UninstPath, 'QuietUninstallString', OldUninstString);
257+
RegQueryStringValue(HKLM, UninstPath, 'InstallLocation', OldInstallDir);
258+
end;
259+
end;
260+
261+
function EnsureNoLegacyX86(): Boolean;
262+
var
263+
RootKey, UninstPath, OldInstallDirX86: string;
264+
begin
265+
Result := True;
266+
267+
RootKey := 'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\';
268+
UninstPath := RootKey + '{#LegacyX86AppId}' + '_is1';
269+
270+
if RegQueryStringValue(HKLM, UninstPath, 'InstallLocation', OldInstallDirX86) then
271+
begin
272+
SuppressibleMsgBox('Found an old 32-bit version of SyncTrayzor already installed. You must uninstall it before installing the 64-bit version. Installed at: '+OldInstallDirX86, mbError, MB_OK, IDOK);
273+
Result := False;
274+
end;
275+
end;
276+
277+
// -------------------------------------------------------------------
278+
// APPDATA BACKUP ----------------------------------------------------
279+
// -------------------------------------------------------------------
280+
procedure RecursiveCopy(const SrcDir, DstDir: string);
281+
var
282+
FindRec: TFindRec;
283+
begin
284+
if DirExists(SrcDir) then
285+
begin
286+
ForceDirectories(DstDir);
287+
if FindFirst(AddBackslash(SrcDir) + '*', FindRec) then
288+
begin
289+
try
290+
repeat
291+
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
292+
begin
293+
if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
294+
RecursiveCopy(AddBackslash(SrcDir) + FindRec.Name,
295+
AddBackslash(DstDir) + FindRec.Name)
296+
else
297+
CopyFile(AddBackslash(SrcDir) + FindRec.Name,
298+
AddBackslash(DstDir) + FindRec.Name, False);
299+
end;
300+
until not FindNext(FindRec);
301+
finally
302+
FindClose(FindRec);
303+
end;
304+
end;
305+
end;
306+
end;
307+
308+
procedure BackupAppData();
309+
begin
310+
BackupDir := ExpandConstant('{tmp}') + '\' + '{#AppId}' + '{#BackupStamp}';
311+
RecursiveCopy(ExpandConstant('{#V1AppData}'), BackupDir);
312+
end;
313+
314+
procedure RestoreAppData();
315+
begin
316+
if DirExists(BackupDir) then
317+
RecursiveCopy(BackupDir, ExpandConstant('{#V1AppData}'));
318+
end;
319+
320+
// -------------------------------------------------------------------
321+
// WIZARD EVENT HOOKS ------------------------------------------------
322+
// -------------------------------------------------------------------
323+
function InitializeSetup(): Boolean;
324+
begin
325+
Result := True;
326+
if not EnsureNoLegacyX86() then
327+
begin
328+
Result := False;
329+
end;
330+
end;
331+
332+
procedure InitializeWizard();
333+
begin
334+
OldVersionDetected := DetectV1();
335+
Log('SyncTrayzor v1 installed: ' + IntToStr(Integer(OldVersionDetected)));
336+
337+
if OldVersionDetected then
338+
begin
339+
// Extra page inserted between Welcome and License
340+
InfoPage := CreateCustomPage(wpWelcome, '{#AppName} upgrade',
341+
'A previous version of SyncTrayzor (' + OldVersion + ') was found. ' +
342+
'It will be removed automatically before the new version is installed.');
343+
344+
PgLabel := TNewStaticText.Create(InfoPage);
345+
PgLabel.Parent := InfoPage.Surface;
346+
PgLabel.Caption :=
347+
'• Your settings will be preserved unless you tick the box below.'#13#10 +
348+
'• If you decide to start fresh, SyncTrayzor will upgrade you to syncthing v2.'#13#10 +
349+
'• Please wait – the uninstaller may take a moment.';
350+
351+
PgCheckbox := TNewCheckBox.Create(InfoPage);
352+
PgCheckbox.Parent := InfoPage.Surface;
353+
PgCheckbox.Top := PgLabel.Top + PgLabel.Height + 8;
354+
PgCheckbox.Width := InfoPage.SurfaceWidth - 2 * PgCheckbox.Left;
355+
PgCheckbox.Caption := 'Start fresh - DELETE previous SyncTrayzor configuration and old syncthing.exe!';
356+
PgCheckbox.Checked := False;
357+
end;
358+
end;
359+
360+
function NextButtonClick(CurPageID: Integer): Boolean;
361+
var
362+
ResultCode: Integer;
363+
begin
364+
Result := True; // allow page switch unless we veto
365+
366+
if Assigned(InfoPage) and (CurPageID = InfoPage.ID) and OldVersionDetected then
367+
begin
368+
// 1) Backup
369+
if not PgCheckbox.Checked then
370+
BackupAppData();
371+
372+
// 2) Run uninstaller
373+
if (Exec('>', OldUninstString, '', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode)) then
374+
begin
375+
// 3) Restore
376+
if not PgCheckbox.Checked then
377+
RestoreAppData();
378+
end
379+
else
380+
begin
381+
MsgBox('Could not launch the previous uninstaller (' +
382+
OldUninstString + '): ' + SysErrorMessage(ResultCode) + '.'#13#10'Setup cannot continue.',
383+
mbError, MB_OK);
384+
Result := False; // stay on page
385+
end;
386+
end;
387+
end;
388+
216389
[UninstallRun]
217390
Filename: "{app}\SyncTrayzor.exe"; Parameters: "--shutdown"; RunOnceId: "ShutdownSyncTrayzor"; Flags: skipifdoesntexist
218391

0 commit comments

Comments
 (0)