Inno Setup 安装程序中嵌入 CMD(在自定义页面上显示命令输出)

问题描述 投票:0回答:1

我创建了一个输入页面,该页面使用从这些输入创建的变量来执行命令行应用程序。自然地,我的屏幕上会弹出

cmd
窗口。我想知道是否有任何方法可以在我的 Inno Setup 安装程序页面上嵌入
cmd
窗口(或输出)。

我正在运行 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

这只是我从谷歌上随机拍摄的图像。
我想看到的是类似于安装程序上的标准“显示详细信息”选项。

cmd installation inno-setup pascalscript
1个回答
3
投票

您可以将命令输出重定向到文件并监视文件的更改,将它们加载到列表框(或者可能是备忘录框)。

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 writing,
      // 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;


上面是使用批处理文件进行测试的,例如:

@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
...

© www.soinside.com 2019 - 2024. All rights reserved.