如何将.arc减压添加到Inno Setup?

时间:2017-03-11 17:57:21

标签: compression inno-setup

我一直在尝试通过 Inno Setup 制作一个安装程序,它只支持zip / bzip / lzma / lzma2压缩方法。我通过 FreeArc 打包我的存档(输出文件扩展名是.arc,但是将其重命名为.bin)但是 Inno Setup 无法提取它。我在互联网上搜索了如何将 arc 解压缩植入 Inno Setup ,但所有网站都引用 FreeArc official website这已经死了一段时间。 我需要的是使用必要的dll文件的代码,以便 Inno Setup 解压缩 arc 档案以及执行此操作所需的那些dll文件列表。 我感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

此答案已被Inno Setup - How to add cancel button to decompressing page?取代,后者使用unarc.dll而不是驾驶控制台Arc.exe

我保持这个答案,因为它的概念对其他档案类型很有用。

请参阅下面的示例。它:

  • 获取ARC文件,将其嵌入安装程序
  • 在安装过程中,ARC文件将解压缩到临时文件夹
  • 将ARC文件中的文件解压缩到目标文件夹
#define ArcArchive "test.arc"

[Files]
Source: {#ArcArchive}; DestDir: "{tmp}"; Flags: nocompression deleteafterinstall
Source: Arc.exe; Flags: dontcopy
Source: InnoCallback.dll; 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;

type
  TTimerProc = procedure(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);

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';

function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord;
  external 'wrapcallback@files:innocallback.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, WrapTimerProc(@UpdateProgressProc, 4));

    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;

代码需要Unicode Inno Setup,InnoTools InnoCallbackarc.exe(我是从PeaZip便携包中获取的)。

Decompressing

或者,为了避免双重提取,您可以沿着安装程序分发弧文件。

只需使用{src}来解析其路径:

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

并从{#ArcArchive}部分删除[Files]条目。

使用unarc.dll实现提取会更加健壮,就像FreeArc + InnoSetup包ISFreeArcExtract v.4.0.rar中所见。

答案 1 :(得分:1)

Freearc实际上带有Inno提取示例 http://freearc2.azurewebsites.net/InnoSetup.aspx