Inno Setup安装程序中的嵌入式CMD(在自定义页面上显示命令输出) [英] Embedded CMD in Inno Setup installer (show command output on a custom page)
问题描述
我创建了一个输入页面,该页面使用从这些输入中创建的变量来执行命令行应用程序。自然地, cmd
窗口在我的屏幕上弹出。我想知道是否有任何方法可以将 cmd
窗口(或输出)嵌入到我的Inno Setup安装程序页面上。
我正在运行Inno Setup 5.6.1(因为与Windows XP兼容),但是如果必须切换到最新版本,我可以。
< pre class = lang-pascal prettyprint-override>
[代码]
var
邮件:TInputQueryWizardPage;
最终:TWizardPage;
BotonIniciar:Tbutton;
程序BotonIniciarOnClick(Sender:TObject);
开始
WizardForm.NextButton.Onclick(nil);
Exec(ExpandConstant('{tmp} \imapsync.exe'),'MAIL.Values [0]',',SW_SHOW,
ewWaitUntilTerminated,ResultCode);
结尾;
程序InitializeWizard;
开始
邮件:= CreateInputQueryPage(wpWelcome,’’,’,’);
MAIL.Add(‘请输入您的信息’,错);
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;
结尾;
我可能会丢失部分代码,但我认为这是可以理解的。
Fist Ii创建输入页面,然后创建一个带有 OnClick
属性的按钮,该按钮调用 BotonIniciarOnClick
过程。
实际上,该代码非常有用。但是正如我说的那样,我有一个 cmd
浮动窗口。
我希望看到这样的内容:
这只是我从Google拍摄的随机图像。
我想要看到的内容类似于安装程序上的标准显示详细信息选项
您可以将命令输出重定向到文件,并监视文件中的更改,将其加载到列表框(或备忘录框)中。
var
ProgressPage:TOutputProgressWizardPage;
ProgressListBox:TNewListBox;
函数SetTimer(
Wnd:LongWord; IDEvent,Elapse:LongWord; TimerFunc:LongWord):LongWord;
外部 SetTimer@user32.dll stdcall;
函数KillTimer(hWnd:LongWord; uIDEvent:LongWord):BOOL;
外部 KillTimer@user32.dll stdcall;
var
ProgressFileName:字符串;
函数BufferToAnsi(const Buffer:string):AnsiString;
var
W:单词;
I:整数;
begin
SetLength(Result,Length(Buffer)* 2);
for I:= 1到Length(Buffer)做
开始
W:= Ord(Buffer [I]);
结果[(I * 2)]:= Chr(W shr 8); {高字节}
结果[(I * 2)-1]:= Chr(Byte(W)); {低字节}
结尾;
结尾;
过程UpdateProgress;
var
S:AnsiString;
I,L,Max:整数;
缓冲区:字符串;
流:TFileStream;
行:TStringList;
如果没有FileExists(ProgressFileName),则以
开头;然后
以
开头。
Log(Format(进度文件%s不存在,[ProgressFileName]));
结束
否则
开始
尝试
{需要共享读取,因为输出文件被锁定写,}
{所以我们不能使用LoadStringFromFile}
Stream:= TFileStream.Create(ProgressFileName,fmOpenRead或fmShareDenyNone);
try
L:= Stream.Size;
Max:= 100 * 2014;如果L>
最大然后
开始
Stream.Position:= L-Max;
L:=最大值;
结尾;
SetLength(Buffer,(L div 2)+(L mod 2));
Stream.ReadBuffer(Buffer,L);
S:= BufferToAnsi(Buffer);
最后
流。免费;
结尾;
,但
除外Log(Format(无法从文件%s-%s中读取进度,[[
ProgressFileName,GetExceptionMessage])));
结尾;
结尾;如果S<>
’’然后
开始
Log(’Progress len =’+ IntToStr(Length(S)));
行:= TStringList.Create();
Lines.Text:= S; I的
:Lines。= 0-如果I< 1则
从
开始ProgressListBox.Items.Count然后
开始
ProgressListBox.Items [I]:=行[I];
结束
否则
开始
ProgressListBox.Items.Add(Lines [I]);
结束
结束;
ProgressListBox.ItemIndex:= ProgressListBox.Items.Count-1;
ProgressListBox.Selected [ProgressListBox.ItemIndex]:= False;
行。免费;
结尾;
{仅用于泵送Windows消息队列(可能不需要)}
ProgressPage.SetProgress(0,1);
结尾;
过程UpdateProgressProc(
H:LongWord; Msg:LongWord; Event:LongWord; Time:LongWord);
开始
UpdateProgress;
结尾;
程序BotonIniciarOnClick(Sender:TObject);
var
ResultCode:整数;
计时器:LongWord;
AppPath:字符串;
AppError:字符串;
命令:字符串;
开始
ProgressPage:= $ b $ b CreateOutputProgressPage(
‘正在安装某些东西’,‘请等待,直到完成...’);
ProgressPage.Show();
ProgressListBox:= TNewListBox.Create(WizardForm);
ProgressListBox.Parent:= ProgressPage.Surface;
ProgressListBox.Top:= 0;
ProgressListBox.Left:= 0;
ProgressListBox.Width:= ProgressPage.SurfaceWidth;
ProgressListBox.Height:= ProgressPage.SurfaceHeight;
{UpdateProgressProc中的Fake SetProgress调用将显示它,}
{确保用户不会看到它}
ProgressPage.ProgressBar.Top:= -100;
试试
Timer:= SetTimer(0,0,250,CreateCallback(@UpdateProgressProc));
ExtractTemporaryFile('install.bat');
AppPath:= ExpandConstant(’{tmp} \install.bat’);
ProgressFileName:= ExpandConstant(’{tmp} \progress.txt’);
日志(格式(期待进度,%s,[ProgressFileName]));
命令:=格式(%s>%s [AppPath,ProgressFileName]);
如果不是Exec(ExpandConstant('{cmd}'),'/ c'+ Command,'',SW_HIDE,
ewWaitUntilTerminated,ResultCode)然后
开始
AppError:= '无法启动应用程序';
结束
,否则
,如果ResultCode<> 0,然后
开始
AppError:=格式(应用程序失败,代码为%d,[ResultCode]);
结尾;
UpdateProgress;
最终
{清理}
KillTimer(0,Timer);
ProgressPage.Hide;
DeleteFile(ProgressFileName);
ProgressPage.Free();
结尾;
如果AppError<> ’’然后
开始
{在显示TOutputProgressWizardPage时RaiseException无法正常工作}
RaiseException(AppError);
结尾;
结尾;
上面已用以下批处理文件测试过:
@echo off
echo开始
echo正在执行A ...
echo提取某物...
echo正在执行B ...
echo提取某物...
超时/ t 1> nul
echo正在执行...
echo正在提取...
echo正在执行...
echo正在提取...
超时/ t 1> ; nul
echo正在执行...
echo正在提取...
echo正在执行...
echo正在提取...
超时/ t 1> ; nul
...
如果要将输出显示为一部分安装过程的详细信息,而不是单击按钮,请参阅:
在安装并显示后执行批处理文件其输出在Inno Setup中完成页面之前的自定义页面上
I created an Input page that executes a command line app using the created variables from those inputs. Naturally, the cmd
window pop ups on my screen. I would like to know if there is any way to embed the cmd
window (or the output) on my Inno Setup installer page.
I'm running Inno Setup 5.6.1 (because of Windows XP compatibility), but I'm OK if i have to switch to the last version.
[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;
I'm might be missing some parts of the code, but I think it's understandable.
Fist Ii create the Input page, then I create a button with the OnClick
property that calls to the BotonIniciarOnClick
procedure.
Actually, the code works great. But as I said I'm having a floating cmd
window.
I would like to see something like this:
Its just a random image I took from google.
What I want to see is similar to a standard "show details" option on an installer
You can redirect the command output to a file and monitor the file for changes, loading them to list box (or maybe a memo box).
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;
Above was tested with a batch file like:
@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
...
If you want to display the output as part of the installation process, instead of on a button click, see:
Execute a batch file after installation and display its output on a custom page before Finished page in Inno Setup
这篇关于Inno Setup安装程序中的嵌入式CMD(在自定义页面上显示命令输出)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!