I am running into problems with my inno setup hanging indefinitely while logging output from a bat file. The bat file is launched properly, and terminates with an EXIT 0. This has been tested by launching the bat with the regular [RUN] {cmd} approach and wait enabled. As well, I have tried changing the exec command to run with SW_SHOW
, and it does run the bat, and the window closes.
Hoping to get some help with this please!
The code creates a page and a window for a file Stream from output of the {cmd} batch to be logged. This works, and the bat file logs until it's last line and exits. However, the page hangs indefinitely. No Response code is ever caught by Exec, and/or the file Stream logic is flawed somehow since I actually do get the input back from the log, but it never seems to stop looping. In Inno Setup's debug console, the UpdateProgress method just keeps showing the same number for a long time... then shows a box like: ☐ However, it never finishes this loop and the page never moves forward.
(Working from code provided in the answer @ Embedded CMD in Inno Setup installer (show command output on a custom page) by Martin Prikryl)
[code]
// Embed installation bat into installation progress page
var
ProgressPage: TOutputProgressWizardPage;
ProgressListBox: TNewListBox;
function SetTimer(
Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
external '[email protected] stdcall';
function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
external '[email protected] stdcall';
var
ProgressFileName: string;
function BufferToAnsi(const Buffer: string): AnsiString;
var
W: Word;
I: Integer;
begin
SetLength(Result, Length(Buffer) * 2);
for I := 1 to Length(Buffer) do
begin
W := Ord(Buffer[I]);
Result[(I * 2)] := Chr(W shr 8); { high byte }
Result[(I * 2) - 1] := Chr(Byte(W)); { low byte }
end;
end;
procedure UpdateProgress;
var
S: AnsiString;
I, L, Max: Integer;
Buffer: string;
Stream: TFileStream;
Lines: TStringList;
begin
if not FileExists(ProgressFileName) then
begin
Log(Format('Progress file %s does not exist', [ProgressFileName]));
end
else
begin
try
{ Need shared read as the output file is locked for writting, }
{ so we cannot use LoadStringFromFile }
Stream := TFileStream.Create(ProgressFileName, fmOpenRead or fmShareDenyNone);
try
L := Stream.Size;
Max := 100*2014;
if L > Max then
begin
Stream.Position := L - Max;
L := Max;
end;
SetLength(Buffer, (L div 2) + (L mod 2));
Stream.ReadBuffer(Buffer, L);
S := BufferToAnsi(Buffer);
finally
Stream.Free;
end;
except
Log(Format('Failed to read progress from file %s - %s', [
ProgressFileName, GetExceptionMessage]));
end;
end;
if S <> '' then
begin
Log('Progress len = ' + IntToStr(Length(S)));
Lines := TStringList.Create();
Lines.Text := S;
for I := 0 to Lines.Count - 1 do
begin
if I < ProgressListBox.Items.Count then
begin
ProgressListBox.Items[I] := Lines[I];
end
else
begin
ProgressListBox.Items.Add(Lines[I]);
end
end;
ProgressListBox.ItemIndex := ProgressListBox.Items.Count - 1;
ProgressListBox.Selected[ProgressListBox.ItemIndex] := False;
Lines.Free;
end;
{ Just to pump a Windows message queue (maybe not be needed) }
ProgressPage.SetProgress(0, 1);
end;
procedure UpdateProgressProc(
H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
begin
UpdateProgress;
end;
procedure RunInstallBatInsideProgress;
var
ResultCode: Integer;
Timer: LongWord;
AppPath: string;
AppError: string;
Command: string;
begin
ProgressPage :=
CreateOutputProgressPage(
'Installing something', 'Please wait until this finishes...');
ProgressPage.Show();
ProgressListBox := TNewListBox.Create(WizardForm);
ProgressListBox.Parent := ProgressPage.Surface;
ProgressListBox.Top := 0;
ProgressListBox.Left := 0;
ProgressListBox.Width := ProgressPage.SurfaceWidth;
ProgressListBox.Height := ProgressPage.SurfaceHeight;
{ Fake SetProgress call in UpdateProgressProc will show it, }
{ make sure that user won't see it }
ProgressPage.ProgressBar.Top := -100;
try
Timer := SetTimer(0, 0, 250, CreateCallback(@UpdateProgressProc));
AppPath := ExpandConstant('{app}\installers\install.bat');
ProgressFileName := ExpandConstant('{app}\logs\install-progress.log');
Log(Format('Expecting progress in %s', [ProgressFileName]));
Command := Format('""%s" > "%s""', [AppPath, ProgressFileName]);
if not Exec(ExpandConstant('{cmd}'), '/c ' + Command + ExpandConstant(' {app}\PIPE'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
AppError := 'Cannot start app';
end
else
if ResultCode <> 0 then
begin
AppError := Format('App failed with code %d', [ResultCode]);
end;
UpdateProgress;
finally
{ Clean up }
KillTimer(0, Timer);
ProgressPage.Hide;
DeleteFile(ProgressFileName);
ProgressPage.Free();
end;
if AppError <> '' then
begin
{ RaiseException does not work properly while TOutputProgressWizardPage is shown }
RaiseException(AppError);
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep=ssPostInstall then
begin
RunInstallBatInsideProgress;
end
end;
I have adapted this code slightly so as to do the following:
- Instead of as in the original example this being launched from a button, I have changed the procedure method to "RunInstallBatInsideProgress", and run it from the
procedure CurStepChanged
, on the step ofssPostInstall
- In addition, I have added some parameters to my bat file, while also expanding those variables.
Finally, in my bat file, the only thing I am doing is running some git clone commands. For a sample install.bat, anything will do as long as it does an EXIT 0, but try this:
SETLOCAL ENABLEDELAYEDEXPANSION
setlocal
::: Set backward slashes to forward slashes
SET variable=%CLONE_DIR%
IF "%variable%"=="" SET variable= %~1
SET CLONE_DIR=%variable:\=/%
git clone https://github.com/poelzi/git-clone-test.git %CLONE_DIR%
ECHO "SUCCESS"
EXIT 0
Log
function to debug the problem. – Martin Prikryl