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