如何在Inno Setup中添加.arc解压缩? [英] How to add .arc decompression to Inno Setup?

查看:93
本文介绍了如何在Inno Setup中添加.arc解压缩?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试通过 Inno Setup 创建一个仅支持zip / bzip / lzma / lzma2压缩方法的安装程序。我用 FreeArc 打包了档案文件(输出文件扩展名为.arc,但将其重命名为.bin),但是 Inno Setup 无法提取它。我在互联网上搜索了如何将 arc 减压植入到 Inno Setup 中,但是所有站点都引用了 FreeArc






或者,为避免重复提取,可以沿安装程序分发arc文件。



仅需使用 {src} 来解决其路径:

  ArcArchivePath:= ExpandConstant( '{src} \ { #ArcArchive}); 

并删除 {#ArcArchive} 条目从 [文件] 部分中。






使用 unarc.dll 进行提取的功能强大,如FreeArc + InnoSetup软件包 ISFreeArcExtract v.4.0.rar 所示


I've been trying to make an installer by Inno Setup which only supports zip/bzip/lzma/lzma2 compression methods. I packed my archive by FreeArc (output file extension is .arc but renamed it to .bin) but Inno Setup is not able to extract it. I searched on internet how to implant arc decompression into Inno Setup but all sites refer to FreeArc official website which is dead for a while. All I need is the code to use the necessary dll files to give Inno Setup the ability to decompress arc archives plus the list of those dll files needed to do so. I appreciate any help.

解决方案

This answer has been superseded by Inno Setup - How to add cancel button to decompressing page? that uses unarc.dll instead of driving the console Arc.exe.

I'm keeping this answer, as its concept can be useful for other archive types.


See the example below. It:

  • takes an ARC file, embeds it to the installer
  • during installation, the ARC file is extracted to a temporary folder
  • the files from the ARC file is extracted to the target folder

#define ArcArchive "test.arc"

[Files]
Source: {#ArcArchive}; DestDir: "{tmp}"; Flags: nocompression deleteafterinstall
Source: Arc.exe; Flags: dontcopy

[Code]

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;

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
  ProgressPage: TOutputProgressWizardPage;
  ProgressFileName: string;

procedure UpdateProgressProc(
  H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
var
  S: AnsiString;
  L: Integer;
  P: Integer;
  Max: Integer;
  Progress: string;
  Buffer: string;
  Stream: TFileStream;
  Percent: Integer;
  Found: Boolean;
begin
  Found := False;
  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;

      if S = '' then
      begin
        Log(Format('Progress file %s is empty', [ProgressFileName]));
      end;
    except
      Log(Format('Failed to read progress from file %s - %s', [
                 ProgressFileName, GetExceptionMessage]));
    end;
  end;

  if S <> '' then
  begin
    { Log(S); }
    P := Pos('Extracted', S);
    if P > 0 then
    begin
      Log('Extraction done');
      Percent := 100;
      Found := True;
    end
      else
    begin
      P := Pos('%', S);
      if P > 0 then
      begin
        repeat
          Progress := Copy(S, 1, P - 1);
          Delete(S, 1, P);
          P := Pos('%', S);
        until (P = 0);

        P := Length(Progress);
        while (P > 0) and
              (((Progress[P] >= '0') and (Progress[P] <= '9')) or
               (Progress[P] = '.')) do
        begin
          Dec(P);
        end;

        Progress := Copy(Progress, P + 1, Length(Progress) - P);

        P := Pos('.', Progress);
        if P > 0 then
        begin
          Progress := Copy(Progress, 1, P - 1);
        end;

        Percent := StrToInt(Progress);
        Log(Format('Percent: %d', [Percent]));
        Found := True;
      end;
    end;
  end;

  if not Found then
  begin
    Log('No new data found');
    { no new progress data, at least pump the message queue }
    ProgressPage.SetProgress(ProgressPage.ProgressBar.Position, 100);
  end
    else
  begin
    ProgressPage.SetProgress(Percent, 100);
    ProgressPage.SetText(Format('Extracted: %d%%', [Percent]), '');
  end;
end;

procedure ExtractArc;
var
  ArcExtracterPath: string;
  ArcArchivePath: string;
  TempPath: string;
  CommandLine: string;
  Timer: LongWord;
  ResultCode: Integer;
  S: AnsiString;
  Message: string;
begin
  ExtractTemporaryFile('Arc.exe');

  ProgressPage := CreateOutputProgressPage('Decompression', 'Decompressing archive...');
  ProgressPage.SetProgress(0, 100);
  ProgressPage.Show;
  try
    Timer := SetTimer(0, 0, 250, CreateCallback(@UpdateProgressProc));

    TempPath := ExpandConstant('{tmp}');
    ArcExtracterPath := TempPath + '\Arc.exe';
    ArcArchivePath := TempPath + '\{#ArcArchive}';
    ProgressFileName := ExpandConstant('{tmp}\progress.txt');
    Log(Format('Expecting progress in %s', [ProgressFileName]));
    CommandLine :=
      Format('"%s" x -y -o+ -dp"%s" "%s" > "%s"', [
        ArcExtracterPath, ExpandConstant('{app}'), ArcArchivePath, ProgressFileName]);
    Log(Format('Executing: %s', [CommandLine]));
    CommandLine := Format('/C "%s"', [CommandLine]);
    if not Exec(ExpandConstant('{cmd}'), CommandLine, '', SW_HIDE,
                ewWaitUntilTerminated, ResultCode) then
    begin
      RaiseException('Cannot start extracter');
    end
      else
    if ResultCode <> 0 then
    begin
      LoadStringFromFile(ProgressFileName, S);
      Message := Format('Arc extraction failed failed with code %d', [ResultCode]);
      Log(Message);
      Log('Output: ' + S);
      RaiseException(Message);
    end
      else
    begin
      Log('Arc extraction done');
    end;
  finally
    { Clean up }
    Log('Arc extraction cleanup');
    KillTimer(0, Timer);
    ProgressPage.Hide;
    DeleteFile(ProgressFileName);
  end;
  Log('Arc extraction end');
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep = ssPostInstall then
  begin
    ExtractArc;
  end;
end;

The code needs arc.exe (I've taken it from PeaZip portable package).

For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library (and you need Unicode version of Inno Setup 5).


Alternatively, to avoid double extraction, you can distribute the arc file along the installer.

Just use {src} to resolve its path:

ArcArchivePath := ExpandConstant('{src}\{#ArcArchive}');

And remove the {#ArcArchive} entry from the [Files] section.


It would be more robust to implement the extraction using unarc.dll, like seen in the FreeArc+InnoSetup package ISFreeArcExtract v.4.0.rar.

这篇关于如何在Inno Setup中添加.arc解压缩?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆