I am using this code: How to add .arc decompression to Inno Setup? (answer of Martin Prikryl). I want to add a cancel button at decompressing page and active this page for others functions (when the decompression is active, this page is inactive and, for example, i can not press on/off button of my music implementation).
How to add a cancel button to decompression page? and how to active this page for others functions?
I have reimplemented the solution from How to add .arc decompression to Inno Setup? using unarc.dll
(from FreeArc+InnoSetup package ISFreeArcExtract v.4.0.rar
).
It greatly simplifies the code and also makes it easier to add the ability to cancel the decompression.
#define ArcArchive "test.arc"
[Files]
Source: unarc.dll; Flags: dontcopy
[Code]
const
ArcCancelCode = -10;
function FreeArcExtract(
Callback: LongWord;
Cmd1, Cmd2, Cmd3, Cmd4, Cmd5, Cmd6, Cmd7, Cmd8, Cmd9, Cmd10: PAnsiChar
): Integer;
external 'FreeArcExtract@files:unarc.dll cdecl';
const
CP_UTF8 = 65001;
function WideCharToMultiByte(CodePage: UINT; dwFlags: DWORD;
lpWideCharStr: string; cchWideChar: Integer; lpMultiByteStr: AnsiString;
cchMultiByte: Integer; lpDefaultCharFake: Integer;
lpUsedDefaultCharFake: Integer): Integer;
external 'WideCharToMultiByte@kernel32.dll stdcall';
function GetStringAsUtf8(S: string): AnsiString;
var
Len: Integer;
begin
Len := WideCharToMultiByte(CP_UTF8, 0, S, Length(S), Result, 0, 0, 0);
SetLength(Result, Len);
WideCharToMultiByte(CP_UTF8, 0, S, Length(S), Result, Len, 0, 0);
end;
var
ArcTotalSize: Integer;
ArcExtracted: Integer;
ArcCancel: Boolean;
ArcProgressPage: TOutputProgressWizardPage;
function FreeArcCallback(
AWhat: PAnsiChar; Int1, Int2: Integer; Str: PAnsiChar): Integer;
var
What: string;
begin
What := AWhat;
if What = 'origsize' then
begin
ArcTotalSize := Int1;
Log(Format('Total size of files to be extracted is %d MB', [ArcTotalSize]));
end
else
if What = 'write' then
begin
if ArcTotalSize > 0 then
begin
ArcProgressPage.SetProgress(Int1, ArcTotalSize);
end;
ArcExtracted := Int1;
end
else
begin
// Just to pump message queue more often (particularly for
// 'read' callbacks), to get more smooth progress bar
if (ArcExtracted > 0) and (ArcTotalSize > 0) then
begin
ArcProgressPage.SetProgress(ArcExtracted, ArcTotalSize);
end;
end;
if ArcCancel then Result := ArcCancelCode
else Result := 0;
end;
function FreeArcCmd(
Cmd1, Cmd2, Cmd3, Cmd4, Cmd5, Cmd6, Cmd7, Cmd8, Cmd9, Cmd10: string): Integer;
begin
ArcCancel := False;
try
Result :=
FreeArcExtract(
CreateCallback(@FreeArcCallback),
GetStringAsUtf8(Cmd1), GetStringAsUtf8(Cmd2), GetStringAsUtf8(Cmd3),
GetStringAsUtf8(Cmd4), GetStringAsUtf8(Cmd5), GetStringAsUtf8(Cmd6),
GetStringAsUtf8(Cmd7), GetStringAsUtf8(Cmd8), GetStringAsUtf8(Cmd9),
GetStringAsUtf8(Cmd10));
Log(Format('Arc command "%s" result %d', [Cmd1, Result]));
except
Result := -63;
end;
end;
function UnPackArchive(ArchivePath: string; DestPath: string): Integer;
begin
// Find out length of files to be extracted - origsize
Result := FreeArcCmd('l', '--', ArchivePath, '', '', '', '', '', '', '');
if Result = 0 then
begin
// Actually extract
Result :=
FreeArcCmd('x', '-o+', '-dp' + DestPath, '-w' + DestPath, '--',
ArchivePath, '', '', '', '');
end;
end;
procedure UnpackCancelButtonClick(Sender: TObject);
begin
ArcCancel := True;
end;
procedure ExtractArc;
var
ArcArchivePath: string;
UnpackResult: Integer;
PrevCancelButtonClick: TNotifyEvent;
Error: string;
begin
ArcProgressPage :=
CreateOutputProgressPage('Decompression', 'Decompressing archive...');
ArcProgressPage.SetProgress(0, 100);
ArcProgressPage.Show;
try
WizardForm.CancelButton.Visible := True;
WizardForm.CancelButton.Enabled := True;
PrevCancelButtonClick := WizardForm.CancelButton.OnClick;
WizardForm.CancelButton.OnClick := @UnpackCancelButtonClick;
ArcArchivePath := ExpandConstant('{src}\{#ArcArchive}');
Log(Format('Arc extraction starting - %s', [ArcArchivePath]));
ArcExtracted := 0;
UnpackResult := UnPackArchive(ArcArchivePath, ExpandConstant('{app}'));
if UnpackResult <> 0 then
begin
if ArcCancel then
begin
Error := 'Extraction cancelled';
end
else
begin
Error := Format('Extraction failed with code %d', [UnpackResult]);
end;
MsgBox(Error, mbError, MB_OK);
end;
finally
Log('Arc extraction cleanup');
ArcProgressPage.Hide;
WizardForm.CancelButton.OnClick := PrevCancelButtonClick;
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
ExtractArc;
end;
end;
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.
The code extracts a separate .arc file. If you want to embed the archive to the installer, you can use
[Files]
Source: {#ArcArchive}; DestDir: "{tmp}"; Flags: nocompression deleteafterinstall
And extract the archive from the {tmp}
:
ArcArchivePath := ExpandConstant('{tmp}\{#ArcArchive}');
Note that the unarc.dll
from ISFreeArcExtract v.4.0.rar
does not seem to support password protected archives. The version from ISFreeArcExtract v.4.2.rar
does, but I'm not aware of trustworthy download link.
If you want to extract multiple archives, see Inno Setup - How to add multiple arc files to decompress?