📄 undo.pas
字号:
unit Undo;
{
Inno Setup
Copyright (C) 1997-2004 Jordan Russell
Portions by Martijn Laan
For conditions of distribution and use, see LICENSE.TXT.
Uninstallation Procedures
$jrsoftware: issrc/Projects/Undo.pas,v 1.64 2004/10/27 22:21:31 jr Exp $
}
{ Note: This unit is shared by both the 'Setup' and 'Uninst' projects }
interface
{$I VERSION.INC}
uses
Windows, SysUtils, FileClass;
const
HighestSupportedVersion = 33;
{ Each time the format of the uninstall log changes (usually a new entry type
is added), I increment HighestSupportedVersion and the version info in
Uninst.res to match (51.x). Do NOT do this yourself; doing so could cause
incompatibilies with future Inno Setup releases. It's recommended that you
use the "utUserDefined" log entry type if you wish to implement your own
custom uninstall log entries; see below for more information. }
type
TUninstallRecTyp = type Word;
const
{ Values for TUninstallRecTyp.
If you wish to define your own custom uninstall entry type, you should use
"utUserDefined". (Do NOT define your own ut* constants; this could cause
incompatibilies with future Inno Setup releases.) The first field in a
utUserDefined record must be a string which specifies a unique name for
the record type. Example:
UninstLog.Add(utUserDefined, ['MyRecordType', ... ], 0);
}
utUserDefined = $01;
utStartInstall = $10;
utEndInstall = $11;
utCompiledCode = $20;
utRun = $80;
utDeleteDirOrFiles = $81;
utDeleteFile = $82;
utDeleteGroupOrItem = $83;
utIniDeleteEntry = $84;
utIniDeleteSection = $85;
utRegDeleteEntireKey = $86;
utRegClearValue = $87;
utRegDeleteKeyIfEmpty = $88;
utRegDeleteValue = $89;
utDecrementSharedCount = $8A;
utRefreshFileAssoc = $8B;
utMutexCheck = $8C;
ValidUninstallRecTypes: array[0..13] of TUninstallRecTyp = (
utUserDefined, utStartInstall, utEndInstall, utRun, utDeleteDirOrFiles,
utDeleteFile, utDeleteGroupOrItem, utIniDeleteEntry, utIniDeleteSection,
utRegDeleteEntireKey, utRegClearValue, utRegDeleteKeyIfEmpty,
utRegDeleteValue, utDecrementSharedCount);
{ Flags on ExtraData }
utRun_NoWait = 1;
utRun_WaitUntilIdle = 2;
utRun_ShellExec = 4;
utRun_RunMinimized = 8;
utRun_RunMaximized = 16;
utRun_SkipIfDoesntExist = 32;
utRun_RunHidden = 64;
utRun_ShellExecRespectWaitFlags = 128;
utDeleteFile_ExistedBeforeInstall = 1;
utDeleteFile_Extra = 2;
utDeleteFile_IsFont = 4;
utDeleteFile_SharedFile = 8;
utDeleteFile_RegisteredServer = 16;
utDeleteFile_CallChangeNotify = 32;
utDeleteFile_RegisteredTypeLib = 64;
utDeleteFile_RestartDelete = 128;
utDeleteFile_RemoveReadOnly = 256;
utDeleteFile_NoSharedFilePrompt = 512;
utDeleteDirOrFiles_Extra = 1;
utDeleteDirOrFiles_IsDir = 2;
utDeleteDirOrFiles_DeleteFiles = 4;
utDeleteDirOrFiles_DeleteSubdirsAlso = 8;
utDeleteDirOrFiles_CallChangeNotify = 16;
utIniDeleteSection_OnlyIfEmpty = 1;
const
UninstallRecNonDataSize =
(SizeOf(Pointer) * 2) + SizeOf(Longint) + SizeOf(Cardinal) + SizeOf(TUninstallRecTyp);
type
PUninstallRec = ^TUninstallRec;
TUninstallRec = packed record
{if this is modified, you must to update UninstallRecNonDataSize above}
Prev, Next: PUninstallRec;
ExtraData: Longint;
DataSize: Cardinal;
Typ: TUninstallRecTyp;
Data: array[0..$6FFFFFFF] of Char;
end;
TDeleteUninstallDataFilesProc = procedure;
TUninstallLogFlags = set of (ufAdminInstalled, ufDontCheckRecCRCs,
ufModernStyle, ufAlwaysRestart, ufChangesEnvironment);
TUninstallLog = class
private
FList, FLastList: PUninstallRec;
FCount: Integer;
function Delete(const Rec: PUninstallRec): PUninstallRec;
procedure InternalAdd(const Typ: TUninstallRecTyp; const Data: Pointer;
const Size: Cardinal; const ExtraData: Longint);
protected
procedure HandleException; virtual; abstract;
function ShouldRemoveSharedFile(const Filename: String): Boolean; virtual;
procedure StatusUpdate(StartingCount, CurCount: Integer); virtual;
public
AppId, AppName: String;
NeedRestart: Boolean;
Flags: TUninstallLogFlags;
Version: Integer;
constructor Create;
destructor Destroy; override;
procedure Add(const Typ: TUninstallRecTyp; const Data: array of String;
const ExtraData: Longint);
function CheckMutexes: Boolean;
procedure Clear;
class function ExtractRecData(const Rec: PUninstallRec;
var Data: array of String): Integer;
function ExtractLatestRecData(const Typ: TUninstallRecTyp;
const ExtraData: Longint; var Data: array of String): Boolean;
procedure Load(const F: TFile; const Filename: String);
function PerformUninstall(const CallFromUninstaller: Boolean;
const DeleteUninstallDataFilesProc: TDeleteUninstallDataFilesProc): Boolean;
procedure Save(const Filename: String;
const Append, UpdateUninstallLogAppName: Boolean);
function Test(const Filename, AAppId: String): Boolean;
property List: PUninstallRec read FList;
property LastList: PUninstallRec read FLastList;
end;
implementation
uses
Messages, ShlObj,
PathFunc, CmnFunc2, Struct, Msgs, MsgIDs, InstFunc, InstFnc2, Compress,
Logging;
type
{ Note: TUninstallLogHeader should stay <= 512 bytes in size, so that it
fits into a single disk sector and can be written atomically }
TUninstallLogHeader = packed record
ID: TUninstallLogID;
AppId: array[0..127] of Char;
AppName: array[0..127] of Char;
Version, NumRecs: Integer;
EndOffset: Cardinal;
Flags: Longint;
Reserved: array[0..26] of Longint; { reserved for future use }
CRC: Longint;
end;
TUninstallCrcHeader = packed record
Size, NotSize: Cardinal;
CRC: Longint;
end;
TUninstallFileRec = packed record
Typ: TUninstallRecTyp;
ExtraData: Longint;
DataSize: Cardinal;
end;
{ Misc. uninstallation functions }
function ListContainsPathOrSubdir(const List: TSimpleStringList;
const Path: String): Boolean;
{ Returns True if List contains Path or a subdirectory of Path }
var
SlashPath: String;
SlashPathLen, I: Integer;
begin
SlashPath := AddBackslash(Path);
SlashPathLen := Length(SlashPath);
if SlashPathLen > 0 then begin { ...sanity check }
for I := 0 to List.Count-1 do begin
if List[I] = Path then begin
Result := True;
Exit;
end;
if (Length(List[I]) > SlashPathLen) and
CompareMem(Pointer(List[I]), Pointer(SlashPath), SlashPathLen * SizeOf(Char)) then begin
Result := True;
Exit;
end;
end;
end;
Result := False;
end;
function DeleteDir(const DirName: String;
const DirsNotRemoved, RestartDeleteDirList: TSimpleStringList): Boolean;
var
Attribs, LastError: DWORD;
begin
Attribs := GetFileAttributes(PChar(DirName));
{ Does the directory exist? }
if (Attribs <> $FFFFFFFF) and
(Attribs and FILE_ATTRIBUTE_DIRECTORY <> 0) then begin
LogFmt('Deleting directory: %s', [DirName]);
{ If the directory has the read-only attribute, strip it first }
if Attribs and FILE_ATTRIBUTE_READONLY <> 0 then begin
if IsDirEmpty(DirName) then begin
if SetFileAttributes(PChar(DirName), Attribs and not FILE_ATTRIBUTE_READONLY) then
Log('Stripped read-only attribute.')
else
Log('Failed to strip read-only attribute.');
end
else
Log('Not stripping read-only attribute because the directory ' +
'does not appear to be empty.');
end;
Result := RemoveDirectory(PChar(DirName));
if not Result then begin
LastError := GetLastError;
if Assigned(DirsNotRemoved) then begin
LogFmt('Failed to delete directory (%d). Will retry later.', [LastError]);
DirsNotRemoved.AddIfDoesntExist(DirName);
end
else if Assigned(RestartDeleteDirList) and
ListContainsPathOrSubdir(RestartDeleteDirList, DirName) and
(Win32Platform = VER_PLATFORM_WIN32_NT) then begin
{ Note: This code is NT-only; I don't think it's possible to
restart-delete directories on Windows 9x. }
LogFmt('Failed to delete directory (%d). Will delete on restart (if empty).',
[LastError]);
if not MoveFileEx(PChar(DirName), nil, MOVEFILE_DELAY_UNTIL_REBOOT) then
LogFmt('MoveFileEx failed (%d).', [GetLastError]);
end
else
LogFmt('Failed to delete directory (%d).', [LastError]);
end;
end
else
Result := True;
end;
function IsSectionEmpty(const Section, Filename: PChar): Boolean;
var
Test: array[0..255] of Char;
begin
Test[0] := #0;
if Filename^ <> #0 then
GetPrivateProfileString(Section, nil, '', Test, SizeOf(Test), Filename)
else
GetProfileString(Section, nil, '', Test, SizeOf(Test));
Result := Test[0] = #0;
end;
{ TUninstallLog }
constructor TUninstallLog.Create;
begin
inherited Create;
Clear;
end;
destructor TUninstallLog.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TUninstallLog.InternalAdd(const Typ: TUninstallRecTyp;
const Data: Pointer; const Size: Cardinal; const ExtraData: Longint);
{ Adds a new entry to the uninstall list }
var
NewRec: PUninstallRec;
begin
NewRec := AllocMem(UninstallRecNonDataSize + Size);
NewRec^.ExtraData := ExtraData;
NewRec^.Typ := Typ;
NewRec^.DataSize := Size;
Move(Data^, NewRec^.Data, Size);
if List = nil then begin
FList := NewRec;
FLastList := List;
end
else begin
LastList^.Next := NewRec;
NewRec^.Prev := LastList;
FLastList := NewRec;
end;
Inc(FCount);
end;
procedure TUninstallLog.Add(const Typ: TUninstallRecTyp; const Data: array of String;
const ExtraData: Longint);
var
I, L: Integer;
S, X: String;
begin
for I := 0 to High(Data) do begin
L := Length(Data[I]);
if L < $FD then
S := S + Char(L)
else if L <= $FFFF then begin
SetLength(X, SizeOf(Byte) + SizeOf(Word));
X[1] := #$FD;
Word((@X[2])^) := Word(L);
S := S + X;
end
else begin
SetLength(X, SizeOf(Byte) + SizeOf(Integer));
X[1] := #$FE;
Integer((@X[2])^) := Integer(L);
S := S + X;
end;
S := S + Data[I];
end;
S := S + #$FF;
InternalAdd(Typ, PChar(S), Length(S), ExtraData);
if Version < HighestSupportedVersion then
Version := HighestSupportedVersion;
end;
function TUninstallLog.Delete(const Rec: PUninstallRec): PUninstallRec;
{ Removes Rec from the linked list, then frees it. Returns (what was) the
previous record, or nil if there is none. }
begin
Result := Rec.Prev;
if Assigned(Rec.Prev) then
Rec.Prev.Next := Rec.Next;
if Assigned(Rec.Next) then
Rec.Next.Prev := Rec.Prev;
if FList = Rec then
FList := Rec.Next;
if FLastList = Rec then
FLastList := Rec.Prev;
Dec(FCount);
FreeMem(Rec);
end;
procedure TUninstallLog.Clear;
{ Frees all entries in the uninstall list and clears AppName/AppDir }
begin
while FLastList <> nil do
Delete(FLastList);
FCount := 0;
AppId := '';
AppName := '';
Flags := [];
end;
type
PDeleteDirData = ^TDeleteDirData;
TDeleteDirData = record
DirsNotRemoved: TSimpleStringList;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -