1
0
Fork 0
gimp/build/windows/installer/util_uninst.isi
Daniel Baumann 554424e00a
Adding upstream version 3.0.4.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-23 00:14:50 +02:00

447 lines
12 KiB
Text

#if 0
[Code]
#endif
var
asUninstInf: TArrayOfString; //uninst.inf contents (loaded at start of uninstall, executed at the end)
function SplitRegParams(const pInf: String; var oRootKey: Integer; var oKey,oValue: String): Boolean;
var sRootKey: String;
d: Integer;
begin
Result := False;
d := Pos('/',pInf);
if d = 0 then
begin
DebugMsg('SplitRegParams','Malformed line (missing /): ' + pInf);
exit;
end;
sRootKey := Copy(pInf,1,d - 1);
oKey := Copy(pInf,d + 1,Length(pInf));
if oValue <> 'nil' then
begin
d := RevPos('\',oKey);
if d = 0 then
begin
DebugMsg('SplitRegParams','Malformed line (missing \): ' + pInf);
exit;
end;
oValue := Decode(Copy(oKey,d+1,Length(oKey)));
oKey := Copy(oKey,1,d-1);
end;
DebugMsg('SplitRegParams','Root: '+sRootKey+', Key:'+oKey + ', Value:'+oValue);
case sRootKey of
'HKCR': oRootKey := HKCR;
'HKLM': oRootKey := HKLM;
'HKU': oRootKey := HKU;
'HKCU': oRootKey := HKCU;
else
begin
DebugMsg('SplitRegParams','Unrecognised root key: ' + sRootKey);
exit;
end;
end;
Result := True;
end;
procedure UninstInfRegKey(const pInf: String; const pIfEmpty: Boolean);
var sKey,sVal: String;
iRootKey: Integer;
begin
sVal := 'nil';
if not SplitRegParams(pInf,iRootKey,sKey,sVal) then
exit;
if pIfEmpty then
begin
if not RegDeleteKeyIfEmpty(iRootKey,sKey) then
DebugMsg('UninstInfRegKey','RegDeleteKeyIfEmpty failed');
end
else
begin
if not RegDeleteKeyIncludingSubkeys(iRootKey,sKey) then
DebugMsg('UninstInfRegKey','RegDeleteKeyIncludingSubkeys failed');
end;
end;
procedure UninstInfRegVal(const pInf: String);
var sKey,sVal: String;
iRootKey: Integer;
begin
if not SplitRegParams(pInf,iRootKey,sKey,sVal) then
exit;
if not RegDeleteValue(iRootKey,sKey,sVal) then
DebugMsg('UninstInfREG','RegDeleteKeyIncludingSubkeys failed');
end;
procedure UninstInfFile(const pFile: String);
begin
DebugMsg('UninstInfFile','File: '+pFile);
if not DeleteFile(pFile) then
DebugMsg('UninstInfFile','DeleteFile failed');
end;
procedure UninstInfDir(const pDir: String);
begin
DebugMsg('UninstInfDir','Dir: '+pDir);
if not RemoveDir(pDir) then
DebugMsg('UninstInfDir','RemoveDir failed');
end;
procedure CreateMessageForm(var frmMessage: TForm; const pMessage: String);
var lblMessage: TNewStaticText;
begin
frmMessage := CreateCustomForm();
with frmMessage do
begin
BorderStyle := bsDialog;
ClientWidth := ScaleX(300);
ClientHeight := ScaleY(48);
Caption := CustomMessage('UninstallingAddOnCaption');
Position := poScreenCenter;
BorderIcons := [];
end;
lblMessage := TNewStaticText.Create(frmMessage);
with lblMessage do
begin
Parent := frmMessage;
AutoSize := True;
Caption := pMessage;
Top := (frmMessage.ClientHeight - Height) div 2;
Left := (frmMessage.ClientWidth - Width) div 2;
Visible := True;
end;
frmMessage.Show();
frmMessage.Refresh();
end;
procedure UninstInfRun(const pInf: String);
var Description,Prog,Params: String;
Split, ResultCode, Ctr: Integer;
frmMessage: TForm;
begin
DebugMsg('UninstInfRun',pInf);
Split := Pos('/',pInf);
if Split <> 0 then
begin
Description := Copy(pInf, 1, Split - 1);
Prog := Copy(pInf, Split + 1, Length(pInf));
end else
begin
Prog := pInf;
Description := '';
end;
Split := Pos('/',Prog);
if Split <> 0 then
begin
Params := Copy(Prog, Split + 1, Length(Prog));
Prog := Copy(Prog, 1, Split - 1);
end else
begin
Params := '';
end;
if not UninstallSilent then //can't manipulate uninstaller messages, so create a form instead
CreateMessageForm(frmMessage,Description);
DebugMsg('UninstInfRun','Running: ' + Prog + '; Params: ' + Params);
if Exec(Prog,Params,'',SW_SHOW,ewWaitUntilTerminated,ResultCode) then
begin
DebugMsg('UninstInfRun','Exec result: ' + IntToStr(ResultCode));
Ctr := 0;
while FileExists(Prog) do //wait a few seconds for the uninstaller to be deleted - since this is done by a program
begin //running from a temporary directory, the uninstaller we ran above will exit some time before
Sleep(UNINSTALL_CHECK_TIME); //it's removed from disk
Inc(Ctr);
if Ctr = (UNINSTALL_MAX_WAIT_TIME/UNINSTALL_CHECK_TIME) then //don't wait more than 5 seconds
break;
end;
end else
DebugMsg('UninstInfRun','Exec failed: ' + IntToStr(ResultCode) + ' (' + SysErrorMessage(ResultCode) + ')');
if not UninstallSilent then
frmMessage.Free();
end;
(*
uninst.inf documentation:
- Delete Registry keys (with all subkeys):
RegKey:<RootKey>/<SubKey>
RootKey = HKCR, HKLM, HKCU, HKU
SubKey = subkey to delete (warning: this will delete all keys under subkey, so be very careful with it)
- Delete empty registry keys:
RegKeyEmpty:<RootKey>/<SubKey>
RootKey = HKCR, HKLM, HKCU, HKU
SubKey = subkey to delete if empty
- Delete values from registry:
RegVal:<RootKey>/<SubKey>/Value
RootKey = HKCR, HKLM, HKCU, HKU
SubKey = subkey to delete Value from
Value = value to delete; \ and % must be escaped as %5c and %25
- Delete files:
File:<Path>
Path = full path to file
- Delete empty directories:
Dir:<Path>
- Run program with parameters:
Run:<description>/<path>/<params>
Directives are parsed from the end of the file backwards as the first step of uninstall.
IMPORTANT: From GIMP 2.10.12 onwards (Inno Setup 6 with support for per-user install), Registry keys referring to HKCR will be
processed by the installer as the first step of install (and the entries will be removed from uninst.inf), since file associations
code was rewritten.
*)
procedure ParseUninstInf();
var i,d: Integer;
sWhat: String;
begin
for i := GetArrayLength(asUninstInf) - 1 downto 0 do
begin
DebugMsg('ParseUninstInf',asUninstInf[i]);
if (Length(asUninstInf[i]) = 0) or (asUninstInf[i][1] = '#') then //skip comments and empty lines
continue;
d := Pos(':',asUninstInf[i]);
if d = 0 then
begin
DebugMsg('ParseUninstInf','Malformed line: ' + asUninstInf[i]);
continue;
end;
sWhat := Copy(asUninstInf[i],d+1,Length(asUninstInf[i]));
case Copy(asUninstInf[i],1,d) of
'RegKey:': UninstInfRegKey(sWhat,False);
'RegKeyEmpty:': UninstInfRegKey(sWhat,True);
'RegVal:': UninstInfRegVal(sWhat);
'File:': UninstInfFile(sWhat);
'Dir:': UninstInfDir(sWhat);
'Run:': UninstInfRun(sWhat);
end;
end;
end;
procedure RestorePointU();
var
ResultCode: Integer;
begin
UninstallProgressForm.StatusLabel.Caption := CustomMessage('CreatingRestorePoint');
if not ShellExec('RunAs', 'powershell', '-Command "$job = Start-Job -ScriptBlock { Checkpoint-Computer -Description "GIMP_' + ExpandConstant('{#CUSTOM_GIMP_VERSION}') + '_uninstall" -RestorePointType APPLICATION_UNINSTALL }; Wait-Job $job -Timeout 24; if ($job.State -eq \"Running\") { Stop-Job $job -Confirm:$false }; Receive-Job $job"',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
DebugMsg('RestorePointU','Failed to create restore point. Error code: ' + IntToStr(ResultCode));
end;
end;
#define API_MAJOR=Copy(GIMP_PKGCONFIG_VERSION,1,Pos(".",GIMP_PKGCONFIG_VERSION)-1)
//Taken from https://github.com/GrayWorldFinex/InnoSetupScripts/blob/main/Src%20Code/cdx-gridberd.iss
Procedure MoveFile(SourceFile, DestFile: {#defined UNICODE ? "Ansi" : ""}String);
Begin
FileCopy(SourceFile, DestFile, False);
End;
Function ProcessFiles(S: {#defined UNICODE ? "Ansi" : ""}String): array of {#defined UNICODE ? "Ansi" : ""}String;
var
i, k: Integer;
Begin
repeat
i := Pos(',', S);
k := GetArrayLength(Result);
SetArrayLength(Result, k + 1);
If (i > 0) then begin
Result[k] := Copy(S, 1, i - 1);
Delete(S, 1, i); end
else begin
Result[k] := S;
SetLength(S, 0);
end;
until (Length(S) = 0);
End;
Procedure CopyFiles(SourceDir, DestDir, ExcludeFiles: {#defined UNICODE ? "Ansi" : ""}String);
var
FSR, DSR: TFindRec;
FindResult, Exclude: Boolean;
SourceFile, DestFile:{#defined UNICODE ? "Ansi" : ""}String;
i, k: Integer;
SS: Array of {#defined UNICODE ? "Ansi" : ""}String;
Begin
SetArrayLength(SS, 0);
SS := ProcessFiles(ExcludeFiles);
k := GetArrayLength(SS);
If not DirExists(DestDir) Then Begin
CreateDir(DestDir);
End;
FindResult := FindFirst(AddBackslash(SourceDir) + '*.*', FSR);
Try
While FindResult Do Begin
If FSR.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 Then Begin
Exclude := False;
For i := 0 To k - 1 Do Begin
If AnsiLowercase(SS[i]) = AnsiLowercase(FSR.Name) Then Begin
Exclude := True;
Break;
End;
End;
If not Exclude Then Begin
SourceFile := AddBackslash(SourceDir) + FSR.Name;
DestFile := AddBackslash(DestDir) + FSR.Name;
MoveFile(SourceFile, DestFile);
End;
End;
FindResult := FindNext(FSR);
End;
FindResult := FindFirst(AddBackslash(SourceDir) + '*.*', DSR);
While FindResult Do Begin
If ((DSR.Attributes and FILE_ATTRIBUTE_DIRECTORY) = FILE_ATTRIBUTE_DIRECTORY) and not ((DSR.Name = '.') or (DSR.Name = '..')) Then Begin
CopyFiles(AddBackslash(SourceDir) + DSR.Name, AddBackslash(DestDir) + DSR.Name, ExcludeFiles);
End;
FindResult := FindNext(DSR);
End;
Finally
FindClose(FSR);
FindClose(DSR);
End;
End;
function Time(ATimerPtr: integer): integer; external '_time32@ucrtbase.dll cdecl';
procedure CurUninstallStepChanged(CurStep: TUninstallStep);
var
gimp_directory: String;
begin
DebugMsg('CurUninstallStepChanged','');
case CurStep of
usUninstall:
begin
//Try to make restore point before unninstalling (like before installing, see RestorePoint() on *gimp3264.iss)
if IsAdminInstallMode() then begin
RestorePointU();
UninstallProgressForm.StatusLabel.Caption := FmtMessage(SetupMessage(msgStatusUninstalling),['GIMP']);
end;
LoadStringsFromFile(ExpandConstant('{app}\uninst\uninst.inf'),asUninstInf);
ParseUninstInf();
end;
usPostUninstall:
begin
//Offer option to remove %AppData%/GIMP/GIMP_APP_VERSION dir so have a future clean install
if not IsAdminInstallMode() then begin
gimp_directory := GetEnv('GIMP{#API_MAJOR}_DIRECTORY');
if gimp_directory = '' then begin
gimp_directory := ExpandConstant('{userappdata}\GIMP\{#GIMP_APP_VERSION}');
end;
if DirExists(gimp_directory + '\') then begin
if SuppressibleMsgBox(CustomMessage('UninstallConfig'), mbError, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then begin
CopyFiles(gimp_directory, ExpandConstant('{userdesktop}\gimp_{#GIMP_APP_VERSION}_backup_' + Format('%d', [Time(0)])), '');
DelTree(gimp_directory, True, True, True);
end;
end;
end;
end;
end;
end;
procedure AssociationsCleanUp();
var i, d, countNew,countUI: Integer;
asTemp, asNew: TArrayOfString;
sKey,sVal: String;
iRootKey: Integer;
begin
if FileExists(ExpandConstant('{app}\uninst\uninst.inf')) then
begin
DebugMsg('AssociationsCleanUp','Parsing old uninst.inf');
LoadStringsFromFile(ExpandConstant('{app}\uninst\uninst.inf'),asTemp);
countNew := 0;
countUI := 0;
SetArrayLength(asNew, GetArrayLength(asTemp));
SetArrayLength(asUninstInf, GetArrayLength(asTemp));
for i := 0 to GetArrayLength(asTemp) - 1 do //extract associations-related entries from uninst.inf
begin
if (Length(asTemp[i]) = 0) or (asTemp[i][1] = '#') then //comment/empty line
begin
asNew[countNew] := asTemp[i];
Inc(countNew);
continue;
end;
d := Pos(':',asTemp[i]);
if d = 0 then //something wrong, ignore
continue;
if Copy(asTemp[i],1,3) = 'Reg' then
begin
sVal := 'nil';
if not SplitRegParams(Copy(asTemp[i], d + 1, Length(asTemp[i])),iRootKey,sKey,sVal) then
continue; //malformed line, ignore
if iRootKey = HKCR then //old association, prepare for cleanup
begin
DebugMsg('AssociationsCleanUp','Preparing for cleanup: '+asTemp[i]);
asUninstInf[countUI] := asTemp[i];
Inc(countUI);
continue;
end;
end;
//something else, keep for new uninst.inf
asNew[countNew] := asTemp[i];
Inc(countNew);
end;
SetArrayLength(asNew, countNew);
SetArrayLength(asUninstInf, countUI);
SaveStringsToUTF8File(ExpandConstant('{app}\uninst\uninst.inf'), asNew, False); //replace uninst.inf with a cleaned one
ParseUninstInf(); //remove old associations
end;
end;