#if 0 [Code] #endif 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 = 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 = HKCR, HKLM, HKCU, HKU SubKey = subkey to delete if empty - Delete values from registry: RegVal://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 = full path to file - Delete empty directories: Dir: - Run program with parameters: Run:// Directives are parsed from the end of the file backwards as the first step of uninstall. IMPORTANT: From PIKA 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 CurUninstallStepChanged(CurStep: TUninstallStep); begin DebugMsg('CurUninstallStepChanged',''); case CurStep of usUninstall: begin LoadStringsFromFile(ExpandConstant('{app}\uninst\uninst.inf'),asUninstInf); ParseUninstInf(); end; //usPostUninstall: 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;