📄 uninstall.pas
字号:
{ Hide the application window so that we don't end up with two taskbar
buttons once the second phase starts }
SetWindowPos(Application.Handle, 0, 0, 0, 0, 0, SWP_NOSIZE or
SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_HIDEWINDOW);
{ Execute the copy of itself ("second phase") }
ProcessHandle := Exec(TempFile, Format('/SECONDPHASE="%s" /FIRSTPHASEWND=$%x ',
[NewParamStr(0), Wnd]) + GetCmdTail);
{ Wait till the second phase process unexpectedly dies or is ready
for the first phase to terminate. }
repeat until ProcessMsgs or (MsgWaitForMultipleObjects(1,
ProcessHandle, False, INFINITE, QS_ALLINPUT) <> WAIT_OBJECT_0+1);
CloseHandle(ProcessHandle);
finally
DestroyWindow(Wnd);
end;
end;
procedure RunSecondPhase;
const
RemovedMsgs: array[Boolean] of TSetupMessageID =
(msgUninstalledMost, msgUninstalledAll);
var
RestartSystem: Boolean;
Wnd: HWND;
CompiledCodeData: array[0..5] of String;
Res, RemovedAll, UninstallNeedsRestart: Boolean;
begin
RestartSystem := False;
{ Create second phase window }
Wnd := CreateWindowEx(0, 'STATIC', '', 0, 0, 0, 0, 0, HWND_DESKTOP, 0,
HInstance, nil);
Longint(OldWindowProc) := SetWindowLong(Wnd, GWL_WNDPROC,
Longint(@SecondPhaseWindowProc));
try
if DebugWnd <> 0 then
SetDebugWnd(DebugWnd, True);
if EnableLogging then begin
try
if LogFilename = '' then
StartLogging('Uninstall')
else
StartLoggingWithFixedFilename(LogFilename);
except
on E: Exception do begin
E.Message := 'Error creating log file:' + SNewLine2 + E.Message;
raise;
end;
end;
end;
Log('Setup version: ' + SetupTitle + ' version ' + SetupVersion);
Log('Original Uninstall EXE: ' + UninstExeFile);
Log('Uninstall DAT: ' + UninstDataFile);
if UninstMsgFile <> '' then
Log('Detached uninstall MSG: ' + UninstMsgFile);
Log('Uninstall command line: ' + GetCmdTail);
LogFmt('Windows version: %u.%.2u.%u (NT platform: %s)', [WindowsVersion shr 24,
(WindowsVersion shr 16) and $FF, WindowsVersion and $FFFF, SYesNo[IsNT]]);
{ Open the .dat file for read access }
try
UninstLogFile := TFile.Create(UninstDataFile, fdOpenExisting, faRead, fsNone);
except
on E: EFileError do begin
SetLastError(E.ErrorCode);
RaiseLastError(FmtSetupMessage1(msgUninstallOpenError,
UninstDataFile));
end;
end;
{ Load contents of the .dat file }
UninstLog := TExtUninstallLog.Create;
UninstLog.Load(UninstLogFile, UninstDataFile);
Title := FmtSetupMessage1(msgUninstallAppFullTitle, UninstLog.AppName);
{ Check if admin privileges are needed to uninstall }
if (ufAdminInstalled in UninstLog.Flags) and not IsAdminLoggedOn then begin
AppMessageBox(PChar(SetupMessages[msgOnlyAdminCanUninstall]), PChar(Title),
MB_OK or MB_ICONEXCLAMATION);
Abort;
end;
{ Reopen the .dat file for exclusive, read/write access and keep it
open for the duration of the uninstall process to prevent a second
instance of the same uninstaller from running. }
FreeAndNil(UninstLogFile);
try
UninstLogFile := TFile.Create(UninstDataFile, fdOpenExisting, faReadWrite, fsNone);
except
on E: EFileError do begin
SetLastError(E.ErrorCode);
RaiseLastError(FmtSetupMessage1(msgUninstallOpenError,
UninstDataFile));
end;
end;
if not UninstLog.ExtractLatestRecData(utCompiledCode, SetupBinVersion, CompiledCodeData) then
InternalError('Cannot find utCompiledCode record for this version of the uninstaller');
if DebugWnd <> 0 then
CompiledCodeData[0] := DebugClientCompiledCodeText;
{ Initialize ConstLeadBytes }
if Length(CompiledCodeData[1]) <> SizeOf(UninstLeadBytes) then
InternalError('utCompiledCodeText record is invalid');
Move(Pointer(CompiledCodeData[1])^, UninstLeadBytes, SizeOf(UninstLeadBytes));
ConstLeadBytes := @UninstLeadBytes;
if CompiledCodeData[0] <> '' then begin
{ Setup some global variables which are accessible to [Code] }
InitMainNonSHFolderConsts;
TempInstallDir := CreateTempDir;
Log('Created temporary directory: ' + TempInstallDir);
if Debugging then
DebugNotifyTempDir(TempInstallDir);
LoadSHFolderDLL;
UninstallExeFilename := UninstExeFile;
UninstallExpandedAppId := UninstLog.AppId;
UninstallSilent := InitSilent or InitVerySilent;
UninstallExpandedApp := CompiledCodeData[2];
UninstallExpandedGroup := CompiledCodeData[3];
UninstallExpandedGroupName := CompiledCodeData[4];
UninstallExpandedLanguage := CompiledCodeData[5];
CodeRunner := TScriptRunner.Create();
CodeRunner.OnDllImport := CodeRunnerOnDllImport;
CodeRunner.OnDebug := CodeRunnerOnDebug;
CodeRunner.OnDebugIntermediate := CodeRunnerOnDebugIntermediate;
CodeRunner.OnException := CodeRunnerOnException;
CodeRunner.LoadScript(CompiledCodeData[0], DebugClientCompiledCodeDebugInfo);
end;
try
try
if CodeRunner <> nil then begin
try
Res := CodeRunner.RunBooleanFunction('InitializeUninstall', [''], False, True);
except
Log('InitializeUninstall raised an exception (fatal).');
raise;
end;
if not Res then begin
Log('InitializeUninstall returned False; aborting.');
Abort;
end;
end;
{ Confirm uninstall }
if not Silent and not VerySilent then begin
if MessageBoxFmt1(msgConfirmUninstall, UninstLog.AppName, PChar(Title),
MB_ICONQUESTION or MB_YESNO or MB_DEFBUTTON2) <> ID_YES then
Abort;
end;
CurUninstallStepChanged(usAppMutexCheck, False);
{ Is the app running? }
while UninstLog.CheckMutexes do
{ Yes, tell user to close it }
if MessageBoxFmt1(msgUninstallAppRunningError, UninstLog.AppName, PChar(Title),
MB_OKCANCEL or MB_ICONEXCLAMATION) <> IDOK then
Abort;
CurUninstallStepChanged(usUninstall, False);
{ Start the actual uninstall process }
RemovedAll := UninstLog.PerformUninstall(True, DeleteUninstallDataFiles);
LogFmt('Removed all? %s', [SYesNo[RemovedAll]]);
UninstallNeedsRestart := UninstLog.NeedRestart or (ufAlwaysRestart in UninstLog.Flags);
if (CodeRunner <> nil) and CodeRunner.FunctionExists('UninstallNeedRestart') then begin
if not UninstallNeedsRestart then begin
try
if CodeRunner.RunBooleanFunction('UninstallNeedRestart', [''], False, False) then begin
UninstallNeedsRestart := True;
Log('Will restart because UninstallNeedRestart returned True.');
end;
except
Log('UninstallNeedRestart raised an exception.');
ShowExceptionMsg;
end;
end
else
Log('Not calling UninstallNeedRestart because a restart has already been deemed necessary.');
end;
LogFmt('Need to restart Windows? %s', [SYesNo[UninstallNeedsRestart]]);
{ Wait for the "close timer" on ProgressForm to expire, then
destroy the window }
if Assigned(ProgressForm) then begin
while not ProgressForm.CloseTimerExpired do
Application.HandleMessage;
FreeAndNil(ProgressForm);
end;
CurUninstallStepChanged(usPostUninstall, True);
if not UninstallNeedsRestart then begin
if not Silent and not VerySilent then
MessageBoxFmt1(RemovedMsgs[RemovedAll], UninstLog.AppName,
Title, MB_ICONINFORMATION or MB_OK or MB_SETFOREGROUND);
end
else begin
if not NoRestart then begin
if VerySilent or
(MessageBoxFmt1(msgUninstalledAndNeedsRestart, UninstLog.AppName,
Title, MB_ICONINFORMATION or MB_YESNO or MB_SETFOREGROUND) = ID_YES) then
RestartSystem := True;
end;
if not RestartSystem then
Log('Will not restart Windows automatically.');
end;
CurUninstallStepChanged(usDone, True);
except
{ Show any pending exception here *before* DeinitializeUninstall
is called, which could display an exception message of its own }
ShowExceptionMsg;
Abort;
end;
finally
if CodeRunner <> nil then begin
try
CodeRunner.RunProcedure('DeinitializeUninstall', [''], False);
except
Log('DeinitializeUninstall raised an exception.');
ShowExceptionMsg;
end;
end;
end;
finally
CodeRunner.Free;
UnloadSHFolderDLL;
if TempInstallDir <> '' then begin
if Debugging then
DebugNotifyTempDir('');
if not DelTree(TempInstallDir, True, True, True, nil, nil, nil) then
Log('Failed to remove temporary directory: ' + TempInstallDir);
end;
EndDebug;
UninstLog.Free;
FreeAndNil(UninstLogFile);
DestroyWindow(Wnd);
end;
if RestartSystem then begin
Log('Restarting Windows.');
RestartComputer;
end;
end;
procedure RunUninstaller;
var
F: TFile;
UninstallerMsgTail: TUninstallerMsgTail;
begin
{ Set default title; it's set again below after the messages are read }
Application.Title := 'Uninstall';
{ This is needed for D3+: Must force the application window visible since
we aren't displaying any forms }
ShowWindow(Application.Handle, SW_SHOW);
try
InitializeCommonVars;
SetCurrentDir(GetSystemDir);
UninstExeFile := NewParamStr(0);
ProcessCommandLine;
{ Initialize messages }
F := TFile.Create(UninstExeFile, fdOpenExisting, faRead, fsRead);
try
F.Seek(F.Size.Lo - SizeOf(UninstallerMsgTail));
F.ReadBuffer(UninstallerMsgTail, SizeOf(UninstallerMsgTail));
if UninstallerMsgTail.ID <> UninstallerMsgTailID then begin
{ No valid UninstallerMsgTail record found at the end of the EXE;
load messages from an external .msg file. }
UninstMsgFile := PathChangeExt(UninstExeFile, '.msg');
LoadSetupMessagesAndOptions(UninstMsgFile, 0);
end
else
LoadSetupMessagesAndOptions(UninstExeFile, UninstallerMsgTail.Offset);
finally
F.Free;
end;
SetMessageBoxCaption(mbInformation, PChar(SetupMessages[msgInformationTitle]));
SetMessageBoxCaption(mbConfirmation, PChar(SetupMessages[msgConfirmTitle]));
SetMessageBoxCaption(mbError, PChar(SetupMessages[msgErrorTitle]));
SetMessageBoxCaption(mbCriticalError, PChar(SetupMessages[msgErrorTitle]));
Application.Title := SetupMessages[msgUninstallAppTitle];
{ Initialize dialog font name & size it will use }
InitializeDialogFont;
{ Verify that uninstall data file exists }
UninstDataFile := PathChangeExt(UninstExeFile, '.dat');
if not NewFileExists(UninstDataFile) then begin
MessageBoxFmt1(msgUninstallNotFound, UninstDataFile,
SetupMessages[msgUninstallAppTitle], MB_ICONSTOP or MB_OK);
Abort;
end;
if not SecondPhase then
RunFirstPhase
else
RunSecondPhase;
except
ShowExceptionMsg;
end;
Halt(ExitCode);
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -