问题
I am currently using Inno Download Plugin to download files for my installer, the biggest problem with this is that it faila to download the file correctly. Because of many reasons like bad connection. I would like to add an alternative method to download files, so the user may chose if he want regular way, or a torrent way. I know that I can use aria2c.exe app (https://aria2.github.io/), can someone help me with implementing it to the code of inno setup?
What I need is to download a 7z file using torrent (aria2.exe) and then unpack the contents to defined folder in {{app}} location.
Good code example is probably all I need.
回答1:
Just run the aria2c
, redirect its output to a file and poll the file contents for progress of the download.
It's actually very similar to my solution for this answer:
Inno Setup - Make Inno Setup Installer report its installation progress status to master installer
#define TorrentMagnet "magnet:..."
[Files]
Source: aria2c.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;
I: Integer;
L: Integer;
P: Integer;
Max: Integer;
Progress: string;
Buffer: string;
Stream: TFileStream;
Transferred: string;
Percent: Integer;
Found: Boolean;
begin
Found := False;
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', [ProgressFileName]));
end;
if S <> '' then
begin
P := Pos('[#', S);
if P = 0 then
begin
Log('Not found any progress line');
end
else
begin
repeat
Delete(S, 1, P - 1);
P := Pos(']', S);
Progress := Copy(S, 2, P - 2);
Delete(S, 1, P);
P := Pos('[#', S);
until (P = 0);
Log(Format('Found progress line: %s', [Progress]));
P := Pos(' ', Progress);
if P > 0 then
begin
Log('A');
Delete(Progress, 1, P);
P := Pos('(', Progress);
if P > 0 then
begin
Log('b');
Transferred := Copy(Progress, 1, P - 1);
Delete(Progress, 1, P);
P := Pos('%)', Progress);
if P > 0 then
begin
Log('c');
Percent := StrToIntDef(Copy(Progress, 1, P - 1), -1);
if Percent >= 0 then
begin
Log(Format('Transferred: %s, Percent: %d', [Transferred, Percent]));
ProgressPage.SetProgress(Percent, 100);
ProgressPage.SetText(Format('Transferred: %s', [Transferred]), '');
Found := True;
end;
end;
end;
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;
end;
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
TorrentDownloaderPath: string;
TempPath: string;
CommandLine: string;
Timer: LongWord;
InstallError: string;
ResultCode: Integer;
S: AnsiString;
begin
ExtractTemporaryFile('aria2c.exe');
ProgressPage := CreateOutputProgressPage('Torrent download', 'Downloading torrent...');
ProgressPage.SetProgress(0, 100);
ProgressPage.Show;
try
Timer := SetTimer(0, 0, 250, CreateCallback(@UpdateProgressProc));
TempPath := ExpandConstant('{tmp}');
TorrentDownloaderPath := TempPath + '\aria2c.exe';
ProgressFileName := ExpandConstant('{tmp}\progress.txt');
Log(Format('Expecting progress in %s', [ProgressFileName]));
CommandLine :=
Format('"%s" "%s" > "%s"', [
TorrentDownloaderPath, '{#TorrentMagnet}', ProgressFileName]);
Log(Format('Executing: %s', [CommandLine]));
CommandLine := Format('/C "%s"', [CommandLine]);
if not Exec(ExpandConstant('{cmd}'), CommandLine, TempPath, SW_HIDE,
ewWaitUntilTerminated, ResultCode) then
begin
Result := 'Cannot start torrent download';
end
else
if ResultCode <> 0 then
begin
LoadStringFromFile(ProgressFileName, S);
Result := Format('Torrent download failed with code %d', [ResultCode]);
Log(Result);
Log('Output: ' + S);
end;
finally
{ Clean up }
KillTimer(0, Timer);
ProgressPage.Hide;
DeleteFile(ProgressFileName);
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 BufferToAnsi
and its use is based on:
Inno Setup LoadStringFromFile fails when file is open in another process
来源:https://stackoverflow.com/questions/41924722/inno-setup-torrent-download-implementation