Встроенный CMD в программе установки Inno Setup (вывод команды show на пользовательской странице) - PullRequest
2 голосов
/ 06 июля 2019

Я создал страницу ввода, которая выполняет приложение командной строки, используя созданные переменные из этих входов.Естественно, на моем экране появляется окно cmd.Я хотел бы знать, есть ли способ встроить окно cmd (или вывод) на мою страницу установщика Inno Setup.

Я использую Inno Setup 5.6.1 (из-за совместимости с Windows XP), но я в порядке, если мне нужно переключиться на последнюю версию.

[Code]
var
  MAIL: TInputQueryWizardPage;
  Final: TWizardPage;
  BotonIniciar: Tbutton;

procedure BotonIniciarOnClick(Sender: TObject);
begin
  WizardForm.NextButton.Onclick(nil);
  Exec(ExpandConstant('{tmp}\imapsync.exe'),'MAIL.Values[0]','', SW_SHOW,
    ewWaitUntilTerminated, ResultCode);
end;

procedure InitializeWizard;
begin
  MAIL := CreateInputQueryPage(wpWelcome, '', '', '');
  MAIL.Add('Please input your information', False);

  BotonIniciar := TNewButton.Create(MAIL);
  BotonIniciar.Caption := 'Iniciar';
  BotonIniciar.OnClick := @BotonIniciarOnClick;
  BotonIniciar.Parent :=  WizardForm;
  BotonIniciar.Left := WizardForm.NextButton.Left - 250 ;
  BotonIniciar.Top := WizardForm.CancelButton.Top - 10;
  BotonIniciar.Width := WizardForm.NextButton.Width + 60;
  BotonIniciar.Height := WizardForm.NextButton.Height + 10;
end;

Возможно, мне не хватает некоторых частей кода, но я думаю, что это понятно.Сначала я создаю страницу ввода, затем создаю кнопку со свойством OnClick, которая вызывает процедуру BotonIniciarOnClick.

На самом деле код работает отлично.Но, как я уже сказал, у меня плавающее окно cmd.

Я хотел бы увидеть что-то вроде этого:

Example

Это просто случайное изображениеЯ взял из Google.
То, что я хочу увидеть, похоже на стандартную опцию "показать детали" в установщике

1 Ответ

1 голос
/ 06 июля 2019

Вы можете перенаправить вывод команды в файл и отслеживать файл на предмет изменений, загружая их в список (или, возможно, в блокнот).

var
  ProgressPage: TOutputProgressWizardPage;
  ProgressListBox: TNewListBox;

function SetTimer(
  Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
  external 'SetTimer@user32.dll stdcall';
function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
  external 'KillTimer@user32.dll 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 BotonIniciarOnClick(Sender: TObject);
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));

    ExtractTemporaryFile('install.bat');
    AppPath := ExpandConstant('{tmp}\install.bat');
    ProgressFileName := ExpandConstant('{tmp}\progress.txt');
    Log(Format('Expecting progress in %s', [ProgressFileName]));
    Command := Format('""%s" > "%s""', [AppPath, ProgressFileName]);
    if not Exec(ExpandConstant('{cmd}'), '/c ' + Command, '', 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;

enter image description here


Выше был протестирован с командным файлом, как:

@echo off
echo Starting
echo Doing A...
echo Extracting something...
echo Doing B...
echo Extracting something...
timeout /t 1 > nul
echo Doing C...
echo Extracting something...
echo Doing D...
echo Extracting something...
timeout /t 1 > nul
echo Doing E...
echo Extracting something...
echo Doing F...
echo Extracting something...
timeout /t 1 > nul
...
...