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.
[Setup]
AppName=My Program
[Code]
const
BM_CLICK = $00F5;
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';
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, CreateCallback(@SubmitMessageProc));
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;
For CreateCallback
function, you need Inno Setup 6.
If you are stuck with Inno Setup 5, you can use WrapCallback
function from InnoTools InnoCallback library (the code needs Unicode version of Inno Setup 5). 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.
For a different approach to the problem, see Changing uninstall confirmation prompt.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…