Inno Setup - Automatically submitting uninstall pr

2019-01-26 01:37发布

I want to hide the first and the last message of the unisntaller. This code works with a modified version of inno setup (Inno Setup Ultra 5.5.1.ee2) but does not work well to hide the first message (appears briefly and disappears):

function FindWindowEx(Parent, Child: HWND; ClassName, WindowName: PansiChar): HWND;
  external 'FindWindowExA@user32.dll stdcall';

const
  BM_CLICK    = $00F5;
var
  Timer: TTimer;
  msg: string;
  Wnd, WndEx: HWND;

procedure OnTimer(Sender: TObject);
begin
  Wnd:= FindWindowByWindowName(msg);
  if Wnd > 0 then
  begin
    WndEx:= FindWindowEx(Wnd, 0,'Button', '');
    if WndEx > 0 then
    begin
      PostMessage(WndEx, BM_CLICK, 0, 0);
      Timer.Enabled:= False;
    end;
  end;
end;

function InitializeUninstall:boolean;
begin
  Result := True;
  msg:= SetupMessage(msgUninstallAppFullTitle);
  StringChange(msg, '%1', '{#SetupSetting('AppName')}');
  OnTimer(nil);
  Timer:= TTimer.Create(nil);
  with Timer do
  begin
    OnTimer:= @OnTimer;
    Interval:= 1;
    Enabled:= True;
  end;
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep=usPostUninstall then
  begin
    OnTimer(nil);
    Timer:= TTimer.Create(nil);
    with Timer do
    begin
      OnTimer:= @OnTimer;
      Interval:= 1;
      Enabled:= True;
    end;
  end;
end;

How to modify this code to work correctly with the current official version of Inno Setup and to correctly hide both messages?

1条回答
We Are One
2楼-- · 2019-01-26 02:13

First, I must say that I do not agree with this at all. But it's interesting problem anyway, and the implementation might be useful for other, more appropriate cases.

Also you cannot avoid the message to appear briefly. The solution automates the UI, so needs the UI to work. That's one of the reasons I do not like it.


This code uses the InnoTools InnoCallback DLL library to implement a timer. But using an external DLL library from an uninstaller is tricky and has its drawbacks. See Load external DLL for uninstall process in Inno Setup.

[Setup]
AppName=My Program

[Files]
Source: "InnoCallback.dll"; DestDir: {app}

[Code]

const
  BM_CLICK = $00F5;

type
  TTimerProc = procedure(HandleW, msg, idEvent, TimeSys: LongWord);

function FindWindowEx(Parent, Child: HWND; ClassName, WindowName: string): HWND;
  external 'FindWindowExW@user32.dll stdcall';
function SetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc: LongWord): LongWord;
  external 'SetTimer@User32.dll stdcall';
function KillTimer(hWnd, nIDEvent: LongWord): LongWord;
  external 'KillTimer@User32.dll stdcall';

function WrapTimerProcUninstall(Callback: TTimerProc; ParamCount: Integer): LongWord;
  external 'wrapcallback@{%TEMP}\InnoCallback.dll stdcall uninstallonly delayload';

var
  UpcomingMessage: string;  
  SubmitMessageTimer: LongWord;
  SubmitMessagePossible: Boolean;

procedure SubmitMessageProc(
  H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
  WindowHandle, ButtonHandle: HWND;
begin
  { TODO: Cancel the timer, if the message does not appear within few seconds }
  WindowHandle := FindWindowByWindowName(UpcomingMessage);
  if WindowHandle > 0 then
  begin
    Log(Format('Found message window "%s"', [UpcomingMessage]));
    ButtonHandle := FindWindowEx(WindowHandle, 0, 'Button', '');
    if ButtonHandle > 0 then
    begin
      Log('Found button');
      PostMessage(ButtonHandle, BM_CLICK, 0, 0);
      KillTimer(0, SubmitMessageTimer);
      SubmitMessageTimer := 0;
    end;
  end;
end;

procedure SubmitUpcomingMessage(Msg: string);
begin
  if not SubmitMessagePossible then
  begin
    Log('Cannot submit message');
  end
    else
  begin
    if SubmitMessageTimer > 0 then
      KillTimer(0, SubmitMessageTimer);

    Log(Format('Want to automatically submit message "%s"', [Msg]));
    UpcomingMessage := Msg;
    SubmitMessageTimer :=
      SetTimer(0, 0, 100, WrapTimerProcUninstall(@SubmitMessageProc, 4));
  end;
end;

function FmtSetupMessageWithAppName(const ID: TSetupMessageID): string;
begin
  Result := FmtMessage(SetupMessage(ID), ['{#SetupSetting('AppName')}']);
end;

function InitializeUninstall:boolean;
begin
  Result := True;

  SubmitMessagePossible :=
    FileCopy(
      ExpandConstant('{app}\InnoCallback.dll'),
      ExpandConstant('{%TEMP}\InnoCallback.dll'), False);

  SubmitUpcomingMessage(FmtSetupMessageWithAppName(msgUninstallAppFullTitle));
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep = usPostUninstall then
  begin
    SubmitUpcomingMessage(FmtSetupMessageWithAppName(msgUninstallAppFullTitle));
  end;
end;

The code requires the Unicode Inno Setup.


For a different approach to the problem, see Changing uninstall confirmation prompt.

查看更多
登录 后发表回答