Inno Setup 线程执行插件 ExecCtrl V1.1

V1.1 修正在某些 Windows 平台上(例如 Windows XP SP3)不能正常运行的问题。

暂时解决了 Inno Setup 无法使用线程控件来同步执行程序的问题。
引用来自  execctrl.iss 引用来自 execctrl.iss
; -- execctrl.iss --
; 脚本: restools ( http://restools.hanzify.org )
; execctrl.dll 为一个用于 Inno Setup 的 10.5 KB 的 线程同步执行程序插件.
; 它主要解决需要同步运行其他安装程序,包括 DOS 程序的时候,有可能造成主安装程序进入假死状态的问题。
; 同时它也能控制复杂的安装程序顺序安装步骤,例如 Setup1 和 Setup2 可同步安装,然后 Setup3 在 Setup1 安装完成后再接着安装。
; 还有一个特点,当它运行控制台程序(包括 DOS 程序)的时候,可以实时获取输出内容。这样可以更加方便的对用户提供安装信息。

[Files]
Source: plugin\execctrl.dll; Flags: dontcopy

[Code]
type
  
TStartExecCallbackProc = procedure(ExecID: Integer; CmdLine: PChar);
  TEndExecCallbackProc = procedure(ExecID: Integer; ResultCode: Longword);
  TExecOutputCallbackProc = procedure(ExecID: Integer; Output: PChar);
// ExecID: 每一个执行都会被分配一个 ID 序数,由 1 开始,每一个执行都会自动加 1。在回调函数被执行的时候会同时返回该值,用来识别回调函数是由哪一个 ExecEx 回调回来的。
// CmdLine: 开始执行时返回整理后要执行的命令行。
// ResultCode: 进程结束后的返回值,跟 Inno Setup 里面 Exec 的返回值的效果是一致的。
// Output: 运行期间返回的控制台程序输出内容,一般只适用于 DOS 类程序。

function InitExecCtrl(hThreadWnd: Longword; StartExecCallback: TStartExecCallbackProc;
     EndExecCallback: TEndExecCallbackProc; ExecOutputCallback: TExecOutputCallbackProc): Boolean;
#ifdef UNICODE
external 'InitExecCtrlW@files:execctrl.dll stdcall';
#else
external 'InitExecCtrlA@files:execctrl.dll stdcall';
#endif
// hThreadWnd: 线程窗口句柄,一般用 WizardForm 或者 MainForm 都可以,如果和其他 InnoSetup 插件有冲突,可以使用一个单独创建的 Wnd 也可以,例如一个隐藏的 TPanel。
// StartExecCallback: 执行程序之前会回调一次。
// EndExecCallback: 执行程序结束之后会回调一次。
// ExecOutputCallback: 执行程序时会不停地回调此函数以提供输出内容给安装程序处理。

function UninitExecCtrl(): Boolean; external 'UninitExecCtrl@files:execctrl.dll stdcall';

function ExecEx(Exec_ID: Integer; FileName, Params, WorkingDir: String; ShowCmd: Integer): Boolean;
#ifdef UNICODE
external 'ExecExW@files:execctrl.dll stdcall';
#else
external 'ExecExA@files:execctrl.dll stdcall';
#endif
// FileName: 要执行的程序,如果它为“>”符号时,那么 Params 将作为完整的命令行被执行。
// Params: 参数,如果 FileName 为“>”符号时,它将作为完整的命令行被执行。
// WorkingDir: 工作目录。如果它为空,且 FileName 不为“>”时,将会取 FileName 的目录作为工作目录。
// ShowCmd: 显示的初始状态,例如可视(SW_SHOW),隐藏(SW_HIDE)......状态。
// 基本上它对于参数的处理是跟 Inno Setup 支持的函数 Exec 是一致的。

引用来自  Example_7zip.iss 引用来自 Example_7zip.iss
; 这里演示了 7za.exe 的命令行工具的运行,运行期间,不但主程序不会假死,而且还能实时输出内容。

#include "plugin\execctrl.iss"

[Setup]
AppName=My Application
AppVerName=My Application Ver 1.5
DefaultDirName={pf}\My Application
SolidCompression=yes
Compression=lzma/ultra

[Files]
Source: "plugin\execctrl.dll"; Flags: dontcopy
Source: "runfiles\7za.exe"; DestDir: {tmp}; Flags: dontcopy

[Code]
const
  
EM_LINESCROLL = $00B6;
  PM_REMOVE = 1;

type
  
TMyMsg = record
    
hwnd: HWND;
    message: UINT;
    wParam: Longint;
    lParam: Longint;
    time: DWORD;
    pt: TPoint;
  end;

function PeekMessage(var lpMsg: TMyMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL; external 'PeekMessageA@user32.dll stdcall';
function TranslateMessage(const lpMsg: TMyMsg): BOOL; external 'TranslateMessage@user32.dll stdcall';
function DispatchMessage(const lpMsg: TMyMsg): Longint; external 'DispatchMessageA@user32.dll stdcall';

procedure AppProcessMessages;
var
  
Msg: TMyMsg;
begin
  while
PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
    
TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
end;

var
  
RunButton: TNewButton;
  OutputMemo: TNewMemo;
  LastLine: String;

procedure MainFormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  
CanClose := WizardForm.CancelButton.Enabled;
end;

procedure ExecOutputCallbackProc(ExecID: Integer; Output: PChar);
var
  
i, p, L, rc: Integer;
  s1, s2: string;
begin
  
// Output 是输出字符串,你需要自己维护所有的输出,这里只能实时每次输出一段,你如果需要完整的输出,你必须自己合并多次输出。
  
s1 := LastLine + Output;
  i := 1;
  p := 1;
  L := Length(s1);
  while i <= L do
  begin
    if
s1[i] = #13 then // 这里作用是逐行输出,避免有一个不完整的行出现,让输出比较容易处理,只有 Append 和 Update 的动作。
    
begin
      if
p > i - 1 then
        
s2:=''
      
else
        
s2:=Copy(s1, p, i-p);
      OutputMemo.Lines.Append(s2);
      p := i+2;
    end;
    i := i + 1;
    AppProcessMessages;
  end;
  if (p <= L) then
    
LastLine := Copy(s1, p, L - p + 1)
  else LastLine := '';
  SendMessage(OutputMemo.Handle, EM_LINESCROLL, 0, OutputMemo.Lines.Count-2);
end;

procedure StartExecCallbackProc(ExecID: Integer; CmdLine: PChar);
begin
  
OutputMemo.Lines.Append('ExecEx 7Zip');
  OutputMemo.Lines.Append('ExecID = ' + IntToStr(ExecID));
  OutputMemo.Lines.Append('CmdLine = ' + CmdLine);
end;

procedure EndExecCallbackProc(ExecID: Integer; ResultCode: Longword);
begin
  
OutputMemo.Lines.Append('ExecID = ' + IntToStr(ExecID) + '; ResultCode = ' + IntToStr(ResultCode));
  WizardForm.CancelButton.Enabled := True;
end;

procedure RunBtnClick(Sender: TObject);
begin
  
OutputMemo.Clear;
  LastLine := '';
  TNewButton(Sender).Enabled := False;
  WizardForm.CancelButton.Enabled := False;
  ExecEx(1, ExpandConstant('{tmp}\7za.exe'), ExpandConstant('a -mmt1 -r "C:\test.7z" "{win}\media\*.*"'), '', SW_HIDE);
end;

procedure InitializeWizard();
begin
  
InitExecCtrl(MainForm.Handle, @StartExecCallbackProc, @EndExecCallbackProc, @ExecOutputCallbackProc);

  MainForm.OnCloseQuery := @MainFormCloseQuery;
  
  ExtractTemporaryFile('7za.exe');

  WizardForm.OuterNotebook.Hide;
  
  RunButton := TNewButton.Create(WizardForm);
  RunButton.Parent := WizardForm;
  RunButton.SetBounds(WizardForm.NextButton.Left, WizardForm.NextButton.Top, WizardForm.NextButton.Width, WizardForm.NextButton.Height);
  RunButton.Caption := 'Run 7za.exe';
  RunButton.OnClick := @RunBtnClick;
  
  OutputMemo := TNewMemo.Create(WizardForm);
  OutputMemo.Parent := WizardForm;
  OutputMemo.SetBounds(5, 5, WizardForm.ClientWidth - 10, WizardForm.Bevel.Top - 10);
  OutputMemo.ReadOnly := True;
  OutputMemo.WordWrap := True;
  OutputMemo.ScrollBars := ssVertical;
end;

procedure DeinitializeSetup();
begin
  
UninitExecCtrl();
end;

procedure CurPageChanged(CurPageID: Integer);
begin
  
WizardForm.Caption := 'ExecEx Demo';
  WizardForm.NextButton.Hide;
  WizardForm.CancelButton.Caption := 'Close';
end;

function NextButtonClick(CurPageID: Integer): Boolean;
begin
  
Result := False;
end;

点击下面连接下载例子。
http://restools.hanzify.org/inno/execctrl/InnoExecCtrl_V1.1(7zip).zip


评论: 4 | 引用: 0 | 查看次数: 11526
  • 1
xin [2009-11-28 10:34 PM]
没有现成代码,建议你查看帮助中关于 FindFirst 地解释和例子。
轰隆雷 [2009-11-26 11:41 PM]
原来某个盘符下有个XX目录里面包含DATA,MAP,WAV这个目录,XX目录为绿色软件没有注册也不是用安装程序安装在系统内的。怎么在安装时自动寻找已经存在的XX目录,然后进行安装,安装后自动删除SETUP.EXE文件。请大大示下,感谢!
xin [2009-11-04 11:19 PM]
不知道你说的“自动寻找某个已经存在的目录”是什么意思,如果是先前安装过的目录,最新版本的 Inno Setup 自动就会查找先前安装过的目录。
删除文件使用 DeleteFile 函数。
轰隆雷 [2009-10-27 05:46 PM]
请问大大:怎么在安装时自动寻找某个已经存在的目录,然后进行安装,安装后自动删除SETUP.EXE文件。请大大示下,感谢!
==========================
MAIL: ZOOTOP@GMAIL.COM
  • 1
发表评论
昵 称:
密 码: 游客发言不需要密码.
验证码: 验证码
内 容:
选 项:
虽然发表评论不用注册,但是为了保护您的发言权,建议您注册帐号.
字数限制 5000 字 | UBB代码 开启 | [img]标签 关闭