如何在Inno Setup中添加.arc解压缩? [英] How to add .arc decompression to Inno Setup?
问题描述
我一直在尝试通过 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屋!