1
2 type
3 TUninstallState = (
4 uiUnknown,
5 UIDone, // There IS no uninstaller, OR it was already executed during this install
6 UIOtherNeeded, // The uninstaller ('Inno Setup: App Path') points to a different Path than "WizardFolder"
7 // Uninstall for OTHER folder NEEDED
8 uiDestNeeded, // Uninstaller for "WizardFolder" found
9 // Uninstall for DESTINATION folder NEEDED
10 uiInconsistent // Path of uninstaller and lazarus to be removed, do not match
11 );
12
13 var
14 UninstallState: TUninstallState;
15 UninstallDoneState: TUninstallState; // Set only if uninstall was executed
16 UnInstallerInAppPath: Boolean; // The uninstaller is in the directory that it will remove
17
18 OldPath, // Registry 'Inno Setup: App Path'
19 OldName, // Registry 'DisplayName'
20 UnInstaller: String; // Registry 'UninstallString'
21 PathEqual: Boolean;
22
23 UninstDir: String; // The directory from which lazarus was uninstalled
24 CFGFileForUninstDir: TStringList;
25 CFGPathForUninstDir: String; // the PCP
26 CFGStateForUninstDir: TCfgFileState;
27
28 var
29 wpAskUnistall: TWizardPage;
30 wpLabel1, wpLabel2, wpLabel3, wpLabel4: TNewStaticText;
31 wpCheckBox: TNewCheckBox;
32 wpButton: TNewButton;
33
dbgsUiStatenull34 function dbgsUiState(u: TUninstallState): String;
35 begin
36 case u of
37 uiUnknown: Result := 'uiUnknown';
38 UIDone: Result := 'UIDone';
39 UIOtherNeeded: Result := 'UIOtherNeeded';
40 uiDestNeeded: Result := 'uiDestNeeded';
41 uiInconsistent: Result := 'uiInconsistent';
42 end;
43 end;
44
DidRunUninstallernull45 function DidRunUninstaller: Boolean;
46 begin
47 Result := (UninstallDoneState <> uiUnknown);
48 end;
49
50 // check if: unistall was run, and run in AFolder, and did have a lazarus.cfg file
HasSavedConfigFromUninstallnull51 function HasSavedConfigFromUninstall(AFolder: String): Boolean;
52 begin
53 Result := DidRunUninstaller and
54 (UninstDir = AFolder) and
55 (CFGFileForUninstDir <> nil) and
56 (CFGFileForUninstDir.Count > 0); // only if content
57 end;
58
GetSavedConfigFromUninstallnull59 function GetSavedConfigFromUninstall(AFolder: String): TStringList;
60 begin
61 Result := nil;
62 if HasSavedConfigFromUninstall(AFolder) then
63 Result := CFGFileForUninstDir;
64 end;
65
GetSavedPCPFromUninstallnull66 function GetSavedPCPFromUninstall(AFolder: String): String;
67 begin
68 Result := '';
69 if HasSavedConfigFromUninstall(AFolder) then
70 Result := CFGPathForUninstDir;
71 end;
72
GetSavedStateFromUninstallnull73 function GetSavedStateFromUninstall(AFolder: String): TCfgFileState;
74 begin
75 Result := csNoFile;
76 if HasSavedConfigFromUninstall(AFolder) then
77 Result := CFGStateForUninstDir;
78 end;
79
GetUninstallDatanull80 function GetUninstallData(ARegName: String): String; // Get one entry from registry e.g. 'UninstallString'
81 var
82 Path: String;
83 begin
84 Path := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\'+GetAppId('')+'_is1');
85 Result := '';
86 if not RegQueryStringValue(HKLM, Path, ARegName, Result) then
87 RegQueryStringValue(HKCU, Path, ARegName, Result);
88 end;
89
90 procedure InitializeUninstallInfo;
91 begin
92 UninstallState := uiUnknown;
93 UninstallDoneState := uiUnknown;
94 end;
95
96 procedure UpdateUninstallInfo;
97 begin
98 Log('Enter UninstallState '+dbgsUiState(UninstallState));
99 OldPath := '';
100 OldName := '';
101 UnInstaller := '';
102 PathEqual := False;
103 if UninstallState = uiDone then exit;
104
105 UnInstaller := RemoveQuotes(GetUninstallData('UninstallString'));
106 Log(' UnInstaller: '+UnInstaller);
107 if (UnInstaller <> '') and FileExists(UnInstaller) then
108 begin
109 OldPath := RemoveQuotes((GetUninstallData('Inno Setup: App Path')));
110 OldName := GetUninstallData('DisplayName');
111
112 PathEqual := (OldPath <> '') and
113 (CompareText(RemoveBackslashUnlessRoot(OldPath), RemoveBackslashUnlessRoot(WizardDirValue)) = 0);
114 Log(' OldPath: '+OldPath+' OldName: '+ OldName);
115 if PathEqual then
116 UninstallState := uiDestNeeded
117 else
118 UninstallState := uiOtherNeeded;
119
120 UnInstallerInAppPath := (CompareText(RemoveBackslashUnlessRoot(OldPath), RemoveBackslashUnlessRoot(ExtractFilePath(UnInstaller))) = 0);
121 if (not UnInstallerInAppPath) and
122 ( (CompareText(RemoveBackslashUnlessRoot(OldPath), RemoveBackslashUnlessRoot(WizardDirValue)) = 0) or
123 (CompareText(RemoveBackslashUnlessRoot(ExtractFilePath(UnInstaller)), RemoveBackslashUnlessRoot(WizardDirValue)) = 0)
124 )
125 then
126 UninstallState := uiInconsistent;
127
128 end
129 else
130 begin
131 if (IsSecondaryCheckBoxChecked) or IsSecondaryUpdate then
132 begin
133 if ForcePrimaryAppId then begin
134 Log('UpdateUninstallInfo recursion detected');
135 UninstallState := uiInconsistent;
136 exit;
137 end;
138 ForcePrimaryAppId := True;
139 Log('REDO UninstallState '+GetUninstallData('Inno Setup: App Path')+' // '+WizardDirValue);
140 if CompareText(RemoveBackslashUnlessRoot(RemoveQuotes(GetUninstallData('Inno Setup: App Path'))),
141 RemoveBackslashUnlessRoot(WizardDirValue)) = 0
142 then
143 UpdateUninstallInfo // use the plain installer
144 else
145 UninstallState := uiDone;
146 ForcePrimaryAppId := False;
147 end
148 else
149 UninstallState := uiDone;
150 end;
151
152 Log('UninstallState is now '+dbgsUiState(UninstallState)+', OldPath='+OldPath+' OldName='+OldName+' UnInstaller='+UnInstaller);
153 end;
154
155 // *** Display/Use Wizzard Page
156
157 procedure InitAskUninstall(s1, s2, s3, s4: String);
158 var
159 y: integer;
160 begin
161 wpLabel1.Caption := s1;
162 wpLabel2.Caption := s2;
163 wpLabel3.Caption := s3;
164 wpLabel4.Caption := s4;
165
166 wpLabel1.AdjustHeight;
167 wpLabel2.AdjustHeight;
168 wpLabel3.AdjustHeight;
169 wpLabel4.AdjustHeight;
170
171 wpLabel2.Top := wpLabel1.Top + wpLabel1.Height + ScaleY(5);
172 wpLabel3.Top := wpLabel2.Top + wpLabel2.Height + ScaleY(5);
173 wpLabel4.Top := wpLabel3.Top + wpLabel3.Height + ScaleY(5);
174 y := wpLabel4.Top + wpLabel4.Height + ScaleY(20);
175 if y > wpAskUnistall.SurfaceHeight - wpCheckBox.Height - wpButton.Height then
176 y := wpAskUnistall.SurfaceHeight - wpCheckBox.Height - wpButton.Height;
177 wpButton.Top := y;
178 end;
179
180 procedure UnInstUpdateGUI;
181 begin
182 UpdateUninstallInfo;
183 Log('UnInstUpdateGUI UninstallState='+dbgsUiState(UninstallState)+
184 ' IsSecondaryUpdate='+dbgsBool(IsSecondaryUpdate)+
185 ' Check='+dbgsBool(IsSecondaryCheckBoxChecked)
186 );
187
188 WizardForm.NextButton.Enabled := (UninstallState = uiDone) or (UninstallState = uiDestNeeded) or wpCheckBox.Checked;
189 wpCheckBox.Enabled := not(UninstallState = uiDone);
190 wpButton.Enabled := not(UninstallState = uiDone);
191 end;
192
193 procedure ActivateAskUninst(Sender: TWizardPage);
194 begin
195 UnInstUpdateGUI;
196 end;
197
SkipAskUninstnull198 function SkipAskUninst(Sender: TWizardPage): Boolean;
199 begin
200 Log('SkipAskUninst UninstallState='+dbgsUiState(UninstallState)+
201 ', OldPath='+OldPath+' OldName='+OldName+' UnInstaller='+UnInstaller +
202 ' IsSecondaryUpdate='+dbgsBool(IsSecondaryUpdate)+
203 ' Check='+dbgsBool(IsSecondaryCheckBoxChecked)
204 );
205 Result := UninstallState = uiDone;
206 if Result Then exit;
207
208 UnInstUpdateGUI;
209 //UpdateUninstallInfo;
210
211 // OldName, Registry 'DisplayName'
212 // OldPath, Registry 'Inno Setup: App Path'
213 // UnInstaller
214
215 case UninstallState of
216 uiDestNeeded: begin
217 wpLabel2.Font.Color := clDefault;
218 wpCheckBox.Visible := False;
219 if IsSecondaryUpdate then
220 InitAskUninstall(Format(SaveCustomMessage('OldSecondInDestFolder1', ''),
221 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
222 Format(SaveCustomMessage('OldSecondInDestFolder2', ''),
223 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
224 Format(SaveCustomMessage('OldSecondInDestFolder3', ''),
225 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
226 Format(SaveCustomMessage('OldSecondInDestFolder4', ''),
227 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]))
228 else
229 InitAskUninstall(Format(SaveCustomMessage('OldInDestFolder1', ''),
230 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
231 Format(SaveCustomMessage('OldInDestFolder2', ''), {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
232 Format(SaveCustomMessage('OldInDestFolder3', ''), {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
233 Format(SaveCustomMessage('OldInDestFolder4', ''), {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]));
234 end;
235 UIOtherNeeded: begin
236 wpLabel2.Font.Color := clRed;
237 wpLabel2.Font.Color := clRed;
238 wpCheckBox.Visible := True;
239 //if IsSecondaryUpdate then
240 // InitAskUninstall(Format(SaveCustomMessage('InOtherFolder1', ''),
241 // {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
242 // Format(SaveCustomMessage('OldSecondInOtherFolder2', ''),
243 // {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
244 // Format(SaveCustomMessage('OldSecondInOtherFolder3', ''),
245 // {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
246 // Format(SaveCustomMessage('OldSecondInOtherFolder4', ''),
247 // {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]));
248 //else
249 InitAskUninstall(Format(SaveCustomMessage('OldInOtherFolder1', ''),
250 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
251 Format(SaveCustomMessage('OldInOtherFolder2', ''),
252 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
253 Format(SaveCustomMessage('OldInOtherFolder3', ''),
254 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
255 Format(SaveCustomMessage('OldInOtherFolder4', ''),
256 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]));
257 //end;
258 end;
259 uiInconsistent: begin
260 wpLabel1.Font.Color := clRed;
261 wpLabel2.Font.Color := clRed;
262 wpCheckBox.Visible := True;
263 //if IsSecondaryUpdate then
264 // InitAskUninstall(Format(SaveCustomMessage('OldSecondInBadFolder1', ''),
265 // {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
266 // Format(SaveCustomMessage('OldSecondInBadFolder2', ''),
267 // {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
268 // Format(SaveCustomMessage('OldSecondInBadFolder3', ''),
269 // {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
270 // Format(SaveCustomMessage('OldSecondInBadFolder4', ''),
271 // {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]));
272 //else
273 InitAskUninstall(Format(SaveCustomMessage('OldInBadFolder1', ''),
274 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
275 Format(SaveCustomMessage('OldInBadFolder2', ''),
276 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
277 Format(SaveCustomMessage('OldInBadFolder3', ''),
278 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]),
279 Format(SaveCustomMessage('OldInBadFolder4', ''),
280 {}[#13#10, OldName, OldPath, UnInstaller, SecondPCP]));
281 //end;
282 end;
283 end;
284
285 end;
286
287 procedure UnInstBtnClick(Sender: TObject);
288 var
289 s, UnInstaller: String;
290 b, FolderEmpty : Boolean;
291 i: integer;
292 begin
293 UninstallDoneState := UninstallState;
294 UninstallState := uiDone;
295
296 UnInstaller := RemoveQuotes(GetUninstallData('UninstallString'));
297
298 b := (UnInstaller <> '') and FileExists(UnInstaller);
299 if b then begin
300 if PathEqual then begin
301 LoadCFGFile(OldPath, CFGFileForUninstDir);
302 CFGStateForUninstDir := ParseCFGList(CFGFileForUninstDir, CFGPathForUninstDir);
303 UninstDir := OldPath;
304 end;
305
306 if UninstallState = uiInconsistent then
307 b := Exec(UnInstaller, '/VERBOSE /NORESTART','', SW_SHOW, ewWaitUntilTerminated, i)
308 else
309 b := Exec(UnInstaller, '/SILENT /NORESTART','', SW_SHOW, ewWaitUntilTerminated, i);
310 end;
311 if not b then
312 MsgBox('Uninstall failed.', mbConfirmation, MB_OK)
313 else begin
314 if (UninstallDoneState = uiDestNeeded) then
315 begin
316 FolderEmpty := IsDirEmpty(WizardDirValue);
317 if not FolderEmpty then begin Sleep(500); FolderEmpty := IsDirEmpty(WizardDirValue); end;
318 if not FolderEmpty then begin Sleep(500); FolderEmpty := IsDirEmpty(WizardDirValue); end;
319 if not FolderEmpty then begin Sleep(500); FolderEmpty := IsDirEmpty(WizardDirValue); end;
320 if not(FolderEmpty) then begin
321 // Dir NOT empty, after uninstall
322 try
323 s := CustomMessage('FolderNotEmpty2');
324 except
325 s := 'The target folder is not empty.';
326 end;
327 MsgBox(s, mbConfirmation, MB_OK);
328 end;
329 end;
330 end;
331
332 UnInstUpdateGUI;
333 end;
334
335 procedure UnInstCheckboxClick(Sender: TObject);
336 begin
337 UnInstUpdateGUI;
338 end;
339
340 // *** Create Wizzard Page
341
342 procedure CreateUninstallWizardPage;
343 var
344 s, s2 : String;
345 begin
346 try
347 s := CustomMessage('AskUninstallTitle1');
348 s2 := CustomMessage('AskUninstallTitle2');
349 except
350 s := 'Previous Installation';
351 s2 := 'Do you want to run the uninstaller?';
352 end;
353 wpAskUnistall := CreateCustomPage(wpSelectDir, s, s2);
354 wpAskUnistall.OnShouldSkipPage := @SkipAskUninst;
355 wpAskUnistall.OnActivate := @ActivateAskUninst;
356
357 wpLabel1 := TNewStaticText.Create(wpAskUnistall);
358 wpLabel1.Parent := wpAskUnistall.Surface;
359 wpLabel1.Top := 0;
360 wpLabel1.Left := 0;
361 wpLabel1.Width := wpAskUnistall.SurfaceWidth;
362 wpLabel1.Autosize:= False;
363 wpLabel1.WordWrap := True;
364 wpLabel1.Caption := '';
365
366 wpLabel2 := TNewStaticText.Create(wpAskUnistall);
367 wpLabel2.Parent := wpAskUnistall.Surface;
368 wpLabel2.Left := 0;
369 wpLabel2.Width := wpAskUnistall.SurfaceWidth;
370 wpLabel2.Autosize:= False;
371 wpLabel2.WordWrap := True;
372 wpLabel2.Caption := '';
373
374 wpLabel3 := TNewStaticText.Create(wpAskUnistall);
375 wpLabel3.Parent := wpAskUnistall.Surface;
376 wpLabel3.Left := 0;
377 wpLabel3.Width := wpAskUnistall.SurfaceWidth;
378 wpLabel3.Autosize:= False;
379 wpLabel3.WordWrap := True;
380 wpLabel3.Caption := '';
381
382 wpLabel4 := TNewStaticText.Create(wpAskUnistall);
383 wpLabel4.Parent := wpAskUnistall.Surface;
384 wpLabel4.Left := 0;
385 wpLabel4.Width := wpAskUnistall.SurfaceWidth;
386 wpLabel4.Autosize:= False;
387 wpLabel4.WordWrap := True;
388 wpLabel4.Caption := '';
389
390 try
391 s := CustomMessage('BtnUninstall');
392 except
393 s := 'Uninstall';
394 end;
395 wpButton := TNewButton.Create(wpAskUnistall);
396 wpButton.Parent := wpAskUnistall.Surface;
397 wpButton.Width := ScaleX(80);
398 wpButton.Left := (wpAskUnistall.SurfaceWidth div 2) - ScaleX(40);
399 wpButton.Caption := s;
400 wpButton.OnClick := @UnInstBtnClick;
401
402 try
403 s := CustomMessage('ChkContinue');
404 except
405 s := 'Continue without uninstall';
406 end;
407 wpCheckBox := TNewCheckBox.Create(wpAskUnistall);
408 wpCheckBox.Parent := wpAskUnistall.Surface;
409 wpCheckBox.Top := wpAskUnistall.SurfaceHeight - wpCheckBox.Height - 1;
410 wpCheckBox.Width := wpAskUnistall.SurfaceWidth;
411 wpCheckBox.Caption := s;
412 wpCheckBox.OnClick := @UnInstCheckboxClick;
413 end;
414
415
416