⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uninstall.pas

📁 源代码
💻 PAS
📖 第 1 页 / 共 2 页
字号:
unit Uninstall;

{
  Inno Setup
  Copyright (C) 1997-2004 Jordan Russell
  Portions by Martijn Laan
  For conditions of distribution and use, see LICENSE.TXT.

  Uninstaller

  $jrsoftware: issrc/Projects/Uninstall.pas,v 1.29 2004/12/23 19:54:19 jr Exp $

  NOTE: This unit was derived from Uninst.dpr rev 1.38.
}

interface

procedure RunUninstaller;

implementation

uses
  Windows, SysUtils, Messages, Forms, PathFunc, CmnFunc, CmnFunc2, Undo, Msgs,
  MsgIDs, InstFunc, Struct, UninstProgressForm, UninstSharedFileForm,
  FileClass, ScriptRunner, DebugClient, SetupTypes, Logging,
  Main;

type
  TExtUninstallLog = class(TUninstallLog)
  private
    FNoSharedFileDlgs: Boolean;
    FRemoveSharedFiles: Boolean;
  protected
    procedure HandleException; override;
    function ShouldRemoveSharedFile(const Filename: String): Boolean; override;
    procedure StatusUpdate(StartingCount, CurCount: Integer); override;
  end;

const
  WM_KillFirstPhase = WM_USER + 333;

var
  ExitCode: Integer = 1;
  UninstExeFile, UninstDataFile, UninstMsgFile: String;
  UninstLogFile: TFile;
  UninstLog: TExtUninstallLog = nil;
  Title: String;
  SecondPhase, EnableLogging, Silent, VerySilent, NoRestart: Boolean;
  LogFilename: String;
  FirstPhaseWnd, DebugWnd: HWND;
  OldWindowProc: Pointer;
  ProgressForm: TUninstProgressForm;
  UninstLeadBytes: set of Char;

procedure ShowExceptionMsg;
var
  Msg: String;
begin
  if ExceptObject is EAbort then
    Exit;
  Msg := GetExceptMessage;
  Log('Exception message:' + SNewLine + Msg);
  AppMessageBox(PChar(Msg), Pointer(SetupMessages[msgErrorTitle]),
    MB_OK or MB_ICONSTOP);
    { ^ use a Pointer cast instead of a PChar cast so that it will use "nil"
      if SetupMessages[msgErrorTitle] is empty due to the messages not being
      loaded yet. MessageBox displays 'Error' as the caption if the lpCaption
      parameter is nil. }
end;

procedure TExtUninstallLog.HandleException;
begin
  ShowExceptionMsg;
end;

function TExtUninstallLog.ShouldRemoveSharedFile(const Filename: String): Boolean;
const
  SToAll: array[Boolean] of String = ('', ' to All');
begin
  if Silent or VerySilent then
    Result := True
  else begin
    if not FNoSharedFileDlgs then begin
      { FNoSharedFileDlgs will be set to True if a "...to All" button is clicked }
      FRemoveSharedFiles := ExecuteRemoveSharedFileDlg(Filename,
        FNoSharedFileDlgs);
      LogFmt('Remove shared file %s? User chose %s%s', [Filename, SYesNo[FRemoveSharedFiles], SToAll[FNoSharedFileDlgs]]);
    end;
    Result := FRemoveSharedFiles;
  end;
end;

procedure TExtUninstallLog.StatusUpdate(StartingCount, CurCount: Integer);
begin
  if not VerySilent then begin
    if ProgressForm = nil then begin
      ProgressForm := CreateUninstProgressForm(Title, UninstLog.AppName);
      Application.BringToFront;
    end;
    { Only update the progress bar if it's at the beginning or end, or if
      125 msec has passed since the last update (so that updating the progress
      bar doesn't slow down the actual uninstallation process). }
    if ProgressForm.UpdateTimerExpired or (StartingCount = CurCount) or
       (CurCount = 0) then begin
      ProgressForm.UpdateTimerExpired := False;
      ProgressForm.UpdateProgress(StartingCount - CurCount, StartingCount);
    end;
  end;
  Application.ProcessMessages;
end;

function MessageBoxFmt1(const ID: TSetupMessageID; const Arg1: String;
  const Title: String; const Flags: UINT): Integer;
begin
  Result := AppMessageBox(PChar(FmtSetupMessage1(ID, Arg1)), PChar(Title), Flags);
end;

procedure RaiseLastError(const S: String);
var
  ErrorCode: DWORD;
begin
  ErrorCode := GetLastError;
  raise Exception.Create(FmtSetupMessage(msgLastErrorMessage,
    [S, IntToStr(ErrorCode), Win32ErrorString(ErrorCode)]));
end;

function Exec(const Filename: String; const Parms: String): THandle;
var
  CmdLine: String;
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  CmdLine := AddQuotes(Filename) + ' ' + Parms;

  FillChar(StartupInfo, SizeOf(StartupInfo), 0);
  StartupInfo.cb := SizeOf(StartupInfo);
  if not CreateProcess(nil, PChar(CmdLine), nil, nil, False, 0, nil, nil,
     StartupInfo, ProcessInfo) then
    RaiseLastError(SetupMessages[msgLdrCannotExecTemp]);
  CloseHandle(ProcessInfo.hThread);
  Result := ProcessInfo.hProcess;
end;

function ProcessMsgs: Boolean;
var
  Msg: TMsg;
begin
  Result := False;
  while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
    if Msg.Message = WM_QUIT then begin
      Result := True;
      Break;
    end;
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
end;

function FirstPhaseWindowProc(Wnd: HWND; Msg: UINT; wParam: WPARAM;
  lParam: LPARAM): LRESULT; stdcall;
begin
  Result := 0;
  case Msg of
    WM_QUERYENDSESSION: ;  { Return zero to deny any shutdown requests }
    WM_KillFirstPhase: begin
        PostQuitMessage(0);
        { If we got WM_KillFirstPhase, the second phase must have been
          successful (up until now, at least). Set an exit code of 0. }
        ExitCode := 0;
      end;
  else
    Result := CallWindowProc(OldWindowProc, Wnd, Msg, wParam, lParam);
  end;
end;

function SecondPhaseWindowProc(Wnd: HWND; Msg: UINT; wParam: WPARAM;
  lParam: LPARAM): LRESULT; stdcall;
begin
  if Msg = WM_QUERYENDSESSION then
    { Return zero to deny any shutdown requests }
    Result := 0
  else
    Result := CallWindowProc(OldWindowProc, Wnd, Msg, wParam, lParam);
end;

procedure DeleteUninstallDataFiles;
var
  ProcessID: DWORD;
  Process: THandle;
begin
  Log('Deleting Uninstall data files.');

  { Truncate the .dat file to zero bytes just before relinquishing exclusive
    access to it }
  try
    UninstLogFile.Seek(0);
    UninstLogFile.Truncate;
  except
    { ignore any exceptions, just in case }
  end;
  FreeAndNil(UninstLogFile);

  { Delete the .dat and .msg files }
  DeleteFile(UninstDataFile);
  if UninstMsgFile <> '' then
    DeleteFile(UninstMsgFile);

  { Tell the first phase to terminate, then delete its .exe }
  if FirstPhaseWnd <> 0 then begin
    ProcessID := 0;
    GetWindowThreadProcessId(FirstPhaseWnd, @ProcessID);
    Process := OpenProcess(STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE, False,
      ProcessID);
    SendMessage(FirstPhaseWnd, WM_KillFirstPhase, 0, 0);
    WaitForSingleObject(Process, INFINITE);
    CloseHandle(Process);
    { Sleep for a bit to allow pre-Windows 2000 Add/Remove Programs to finish
      bringing itself to the foreground before we take it back below. Also
      helps the DelayDeleteFile call succeed on the first try. }
    Sleep(500);
  end;
  ExitCode := 0;
  DelayDeleteFile(UninstExeFile, 12);
  if Debugging then
    DebugNotifyUninstExe('');
  { Pre-Windows 2000 Add/Remove Programs will try to bring itself to the
    foreground after the first phase terminates. Take it back. }
  Application.BringToFront;
end;

procedure InitializeDialogFont;
begin
  LangOptions.DialogFontName := UninstLangOptions.DialogFontName;
  LangOptions.DialogFontSize := UninstLangOptions.DialogFontSize;
end;

procedure ProcessCommandLine;
var
  C, I: Integer;
  S: String;
begin
  C := NewParamCount;
  I := 1;
  while I <= C do begin
    S := NewParamStr(I);
    if CompareText(S, '/Log') = 0 then begin
      EnableLogging := True;
      LogFilename := '';
    end
    else
    if CompareText(Copy(S, 1, 5), '/Log=') = 0 then begin
      EnableLogging := True;
      LogFilename := Copy(S, 6, Maxint);
    end
    else
    if CompareText(Copy(S, 1, 13), '/SECONDPHASE=') = 0 then begin
      SecondPhase := True;
      UninstExeFile := Copy(S, 14, Maxint);
    end
    else if CompareText(Copy(S, 1, 15), '/FIRSTPHASEWND=') = 0 then
      FirstPhaseWnd := StrToInt(Copy(S, 16, Maxint))
    else if CompareText(S, '/SILENT') = 0 then
      Silent := True
    else if CompareText(S, '/VERYSILENT') = 0 then
      VerySilent := True
    else if CompareText(S, '/NoRestart') = 0 then
      NoRestart := True
    else if CompareText(Copy(S, 1, 10), '/DEBUGWND=') = 0 then
      DebugWnd := StrToInt(Copy(S, 11, Maxint));
    Inc(I);
  end;
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep; HandleException: Boolean);
begin
  if CodeRunner <> nil then begin
    try
      CodeRunner.RunProcedure('CurUninstallStepChanged', [Ord(CurUninstallStep)], False);
    except
      if HandleException then begin
        Log('CurUninstallStepChanged raised an exception.');
        ShowExceptionMsg;
      end
      else begin
        Log('CurUninstallStepChanged raised an exception (fatal).');
        raise;
      end;
    end;
  end;
end;

procedure RunFirstPhase;
var
  TempFile: String;
  Wnd: HWND;
  ProcessHandle: THandle;
begin
  { Copy self to TEMP directory with a name like _iu14D2N.tmp. The
    actual uninstallation process must be done from somewhere outside
    the application directory since EXE's can't delete themselves while
    they are running. }
  if not GenerateNonRandomUniqueFilename(GetTempDir, TempFile) then
    try
      RestartReplace(TempFile, '');
    except
      { ignore exceptions }
    end;
  if not CopyFile(PChar(UninstExeFile), PChar(TempFile), False) then
    RaiseLastError(SetupMessages[msgLdrCannotCreateTemp]);
  { Don't want any attribute like read-only transferred }
  SetFileAttributes(PChar(TempFile), FILE_ATTRIBUTE_NORMAL);

  { Create first phase window. This window waits for a WM_KillFirstPhase
    message from the second phase process, and terminates itself in
    response. The reason the first phase doesn't just terminate
    immediately is because the Control Panel Add/Remove applet refreshes
    its list as soon as the program terminates. So it waits until the
    uninstallation is complete before terminating. }
  Wnd := CreateWindowEx(0, 'STATIC', '', 0, 0, 0, 0, 0, HWND_DESKTOP, 0,
    HInstance, nil);
  Longint(OldWindowProc) := SetWindowLong(Wnd, GWL_WNDPROC,
    Longint(@FirstPhaseWindowProc));
  try

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -