2025-05-24 14:24:23 +02:00
|
|
|
unit Compression.SevenZipDLLDecoder;
|
2025-05-17 08:49:25 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
Inno Setup
|
|
|
|
Copyright (C) 1997-2025 Jordan Russell
|
|
|
|
Portions by Martijn Laan
|
|
|
|
For conditions of distribution and use, see LICENSE.TXT.
|
|
|
|
|
2025-05-23 10:56:53 +02:00
|
|
|
Interface to the 7-Zip Decoder DLLs, used by Setup
|
2025-05-17 08:49:25 +02:00
|
|
|
|
2025-05-23 10:56:53 +02:00
|
|
|
Based on the 7-Zip source code and the 7-Zip Delphi API by Henri Gourvest
|
2025-05-17 08:49:25 +02:00
|
|
|
https://github.com/geoffsmith82/d7zip MPL 1.1 licensed
|
|
|
|
}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
2025-05-18 20:31:54 +02:00
|
|
|
uses
|
2025-06-11 20:22:33 +02:00
|
|
|
Windows, Shared.FileClass, Shared.VerInfoFunc, Shared.Int64Em, Compression.SevenZipDecoder;
|
2025-05-17 13:35:44 +02:00
|
|
|
|
2025-05-30 12:12:56 +02:00
|
|
|
function SevenZipDLLInit(const SevenZipLibrary: HMODULE;
|
|
|
|
[ref] const VersionNumbers: TFileVersionNumbers): Boolean;
|
2025-06-02 16:03:21 +02:00
|
|
|
procedure SevenZipDLLDeInit;
|
2025-05-22 10:31:24 +02:00
|
|
|
|
2025-05-18 20:31:54 +02:00
|
|
|
procedure ExtractArchiveRedir(const DisableFsRedir: Boolean;
|
|
|
|
const ArchiveFilename, DestDir, Password: String; const FullPaths: Boolean;
|
|
|
|
const OnExtractionProgress: TOnExtractionProgress);
|
2025-05-17 08:49:25 +02:00
|
|
|
|
2025-05-31 09:58:44 +02:00
|
|
|
{ These functions work similar to Windows' FindFirstFile, FindNextFile, and
|
|
|
|
FindClose with the exception that recursion is built-in and that the
|
|
|
|
resulting FindFileData.cFilename contains not just a filename but also the
|
2025-06-02 16:03:21 +02:00
|
|
|
subdir. Also, ArchiveFindFirstFileRedir throws an exception for most errors:
|
|
|
|
INVALID_HANDLE_VALUE is only used if the archive is ok but no suitable file
|
|
|
|
was found. }
|
2025-05-31 10:16:30 +02:00
|
|
|
type
|
2025-06-02 16:03:21 +02:00
|
|
|
TArchiveFindHandle = type NativeUInt;
|
2025-06-11 20:22:33 +02:00
|
|
|
TOnExtractToHandleProgress = procedure(const Bytes, Param: Integer64);
|
2025-05-30 15:13:12 +02:00
|
|
|
function ArchiveFindFirstFileRedir(const DisableFsRedir: Boolean;
|
2025-06-01 14:15:23 +02:00
|
|
|
const ArchiveFilename, DestDir, Password: String;
|
|
|
|
const RecurseSubDirs, ExtractIntent: Boolean;
|
2025-05-31 10:16:30 +02:00
|
|
|
out FindFileData: TWin32FindData): TArchiveFindHandle;
|
|
|
|
function ArchiveFindNextFile(const FindFile: TArchiveFindHandle; out FindFileData: TWin32FindData): Boolean;
|
|
|
|
function ArchiveFindClose(const FindFile: TArchiveFindHandle): Boolean;
|
2025-05-31 22:31:36 +02:00
|
|
|
procedure ArchiveFindExtract(const FindFile: TArchiveFindHandle; const DestF: TFile;
|
2025-06-11 20:22:33 +02:00
|
|
|
const OnExtractToHandleProgress: TOnExtractToHandleProgress; const OnExtractToHandleProgressParam: Integer64);
|
2025-05-30 15:13:12 +02:00
|
|
|
|
2025-05-31 15:45:14 +02:00
|
|
|
type
|
|
|
|
TFileTimeHelper = record helper for TFileTime
|
|
|
|
procedure Clear;
|
|
|
|
function HasTime: Boolean;
|
|
|
|
end;
|
|
|
|
|
2025-05-17 08:49:25 +02:00
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
2025-06-03 20:09:20 +02:00
|
|
|
Classes, SysUtils, Forms, Variants, ActiveX, ComObj, Generics.Collections,
|
2025-05-24 14:24:23 +02:00
|
|
|
Compression.SevenZipDLLDecoder.Interfaces, PathFunc,
|
2025-06-11 20:22:33 +02:00
|
|
|
Shared.SetupMessageIDs, Shared.CommonFunc,
|
2025-05-17 15:50:47 +02:00
|
|
|
SetupLdrAndSetup.Messages, SetupLdrAndSetup.RedirFunc,
|
|
|
|
Setup.LoggingFunc, Setup.MainFunc, Setup.InstFunc;
|
2025-05-17 08:49:25 +02:00
|
|
|
|
|
|
|
type
|
|
|
|
TInStream = class(TInterfacedObject, IInStream)
|
|
|
|
private
|
2025-05-17 15:50:47 +02:00
|
|
|
FFile: TFile;
|
2025-05-17 08:49:25 +02:00
|
|
|
protected
|
|
|
|
function Read(data: Pointer; size: UInt32; processedSize: PUInt32): HRESULT; stdcall;
|
|
|
|
function Seek(offset: Int64; seekOrigin: UInt32; newPosition: PUInt64): HRESULT; stdcall;
|
|
|
|
public
|
2025-05-31 22:31:36 +02:00
|
|
|
constructor Create(const AFile: TFile);
|
2025-05-17 08:49:25 +02:00
|
|
|
destructor Destroy; override;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TSequentialOutStream = class(TInterfacedObject, ISequentialOutStream)
|
|
|
|
private
|
2025-05-17 15:50:47 +02:00
|
|
|
FFile: TFile;
|
2025-05-31 22:31:36 +02:00
|
|
|
FOwnsFile: Boolean;
|
2025-05-17 08:49:25 +02:00
|
|
|
protected
|
|
|
|
function Write(data: Pointer; size: UInt32; processedSize: PUInt32): HRESULT; stdcall;
|
|
|
|
public
|
2025-05-31 22:31:36 +02:00
|
|
|
constructor Create(const AFile: TFile; const AOwnsFile: Boolean = True);
|
2025-05-17 08:49:25 +02:00
|
|
|
destructor Destroy; override;
|
|
|
|
end;
|
|
|
|
|
2025-06-01 18:14:39 +02:00
|
|
|
TArchiveCallback = class(TInterfacedObject, ICryptoGetTextPassword)
|
2025-05-17 08:49:25 +02:00
|
|
|
private
|
|
|
|
FPassword: String;
|
|
|
|
protected
|
2025-06-01 18:14:39 +02:00
|
|
|
{ ICryptoGetTextPassword - queried for by 7-Zip both on IArchiveOpenCallback
|
|
|
|
and IArchiveExtractCallback instances - note: have not yet seen 7-Zip actually
|
|
|
|
call it on an IArchiveOpenCallback instance }
|
2025-05-23 10:56:53 +02:00
|
|
|
function CryptoGetTextPassword(out password: WideString): HRESULT; stdcall;
|
2025-05-17 08:49:25 +02:00
|
|
|
public
|
|
|
|
constructor Create(const Password: String);
|
|
|
|
end;
|
|
|
|
|
2025-06-01 18:14:39 +02:00
|
|
|
TArchiveOpenCallback = class(TArchiveCallback, IArchiveOpenCallback)
|
|
|
|
protected
|
|
|
|
{ IArchiveOpenCallback }
|
|
|
|
function SetTotal(files, bytes: PUInt64): HRESULT; stdcall;
|
|
|
|
function SetCompleted(files, bytes: PUInt64): HRESULT; stdcall;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TArchiveExtractBaseCallback = class(TArchiveCallback, IArchiveExtractCallback)
|
2025-05-31 20:10:03 +02:00
|
|
|
private
|
|
|
|
type
|
|
|
|
TResult = record
|
|
|
|
SavedFatalException: TObject;
|
|
|
|
Res: HRESULT;
|
|
|
|
OpRes: TNOperationResult;
|
|
|
|
end;
|
2025-06-01 12:53:12 +02:00
|
|
|
TArrayOfUInt32 = array of UInt32;
|
2025-05-31 20:10:03 +02:00
|
|
|
var
|
|
|
|
FInArchive: IInArchive;
|
2025-06-02 20:37:54 +02:00
|
|
|
FnumItems: UInt32;
|
2025-05-31 20:10:03 +02:00
|
|
|
FLock: TObject;
|
|
|
|
FProgress, FProgressMax: UInt64;
|
|
|
|
FAbort: Boolean;
|
|
|
|
FResult: TResult;
|
|
|
|
protected
|
|
|
|
{ IProgress }
|
|
|
|
function SetTotal(total: UInt64): HRESULT; stdcall;
|
|
|
|
function SetCompleted(completeValue: PUInt64): HRESULT; stdcall;
|
|
|
|
{ IArchiveExtractCallback }
|
2025-05-31 22:01:02 +02:00
|
|
|
function GetStream(index: UInt32; out outStream: ISequentialOutStream;
|
|
|
|
askExtractMode: Int32): HRESULT; virtual; stdcall; abstract;
|
2025-05-31 20:10:03 +02:00
|
|
|
function PrepareOperation(askExtractMode: Int32): HRESULT; stdcall;
|
|
|
|
function SetOperationResult(opRes: TNOperationResult): HRESULT; stdcall;
|
|
|
|
{ Other }
|
2025-06-01 12:53:12 +02:00
|
|
|
function GetIndices: TArrayOfUInt32; virtual; abstract;
|
2025-05-31 20:10:03 +02:00
|
|
|
procedure Extract;
|
|
|
|
procedure HandleProgress; virtual; abstract;
|
|
|
|
procedure HandleResult;
|
|
|
|
public
|
2025-06-02 20:37:54 +02:00
|
|
|
constructor Create(const InArchive: IInArchive; const numItems: UInt32;
|
|
|
|
const Password: String);
|
2025-05-31 20:10:03 +02:00
|
|
|
destructor Destroy; override;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 22:01:02 +02:00
|
|
|
TArchiveExtractAllCallback = class(TArchiveExtractBaseCallback)
|
2025-05-17 08:49:25 +02:00
|
|
|
private
|
2025-05-21 22:19:23 +02:00
|
|
|
type
|
|
|
|
TCurrent = record
|
|
|
|
Path, ExpandedPath: String;
|
|
|
|
HasAttrib: Boolean;
|
|
|
|
Attrib: DWORD;
|
2025-05-28 17:31:26 +02:00
|
|
|
CTime, MTime: TFileTime;
|
|
|
|
outStream: ISequentialOutStream;
|
2025-05-21 22:19:23 +02:00
|
|
|
procedure SetAttrib(const AAttrib: DWORD);
|
2025-05-27 19:57:20 +02:00
|
|
|
end;
|
2025-05-21 22:19:23 +02:00
|
|
|
var
|
|
|
|
FDisableFsRedir: Boolean;
|
2025-05-31 20:10:03 +02:00
|
|
|
FExpandedDestDir: String;
|
2025-05-21 22:19:23 +02:00
|
|
|
FFullPaths: Boolean;
|
|
|
|
FExtractedArchiveName: String;
|
|
|
|
FOnExtractionProgress: TOnExtractionProgress;
|
2025-05-31 20:10:03 +02:00
|
|
|
FCurrent: TCurrent; { Protected by base's FLock }
|
|
|
|
FLogQueue: TStrings; { Same }
|
2025-05-17 08:49:25 +02:00
|
|
|
protected
|
|
|
|
{ IArchiveExtractCallback }
|
|
|
|
function GetStream(index: UInt32; out outStream: ISequentialOutStream;
|
2025-05-31 22:01:02 +02:00
|
|
|
askExtractMode: Int32): HRESULT; override; stdcall;
|
2025-05-20 07:16:50 +02:00
|
|
|
function SetOperationResult(opRes: TNOperationResult): HRESULT; stdcall;
|
2025-05-31 20:10:03 +02:00
|
|
|
{ Other }
|
2025-06-01 12:53:12 +02:00
|
|
|
function GetIndices: TArchiveExtractBaseCallback.TArrayOfUInt32; override;
|
2025-05-31 20:10:03 +02:00
|
|
|
procedure HandleProgress; override;
|
2025-05-17 08:49:25 +02:00
|
|
|
public
|
2025-06-02 20:37:54 +02:00
|
|
|
constructor Create(const InArchive: IInArchive; const numItems: UInt32;
|
2025-05-17 15:50:47 +02:00
|
|
|
const DisableFsRedir: Boolean; const ArchiveFileName, DestDir, Password: String;
|
|
|
|
const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);
|
2025-05-27 19:57:20 +02:00
|
|
|
destructor Destroy; override;
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-31 22:31:36 +02:00
|
|
|
TArchiveExtractToHandleCallback = class(TArchiveExtractBaseCallback)
|
|
|
|
private
|
|
|
|
FIndex: UInt32;
|
|
|
|
FDestF: TFile;
|
|
|
|
FOnExtractToHandleProgress: TOnExtractToHandleProgress;
|
2025-06-11 20:22:33 +02:00
|
|
|
FOnExtractToHandleProgressParam: Integer64;
|
2025-05-31 22:31:36 +02:00
|
|
|
FPreviousProgress: UInt64;
|
|
|
|
protected
|
|
|
|
{ IArchiveExtractCallback }
|
|
|
|
function GetStream(index: UInt32; out outStream: ISequentialOutStream;
|
|
|
|
askExtractMode: Int32): HRESULT; override; stdcall;
|
|
|
|
{ Other }
|
2025-06-01 12:53:12 +02:00
|
|
|
function GetIndices: TArchiveExtractBaseCallback.TArrayOfUInt32; override;
|
2025-05-31 22:31:36 +02:00
|
|
|
procedure HandleProgress; override;
|
|
|
|
public
|
2025-06-02 20:37:54 +02:00
|
|
|
constructor Create(const InArchive: IInArchive; const numItems: UInt32;
|
|
|
|
const Password: String; const Index: UInt32; const DestF: TFile;
|
2025-06-11 20:22:33 +02:00
|
|
|
const OnExtractToHandleProgress: TOnExtractToHandleProgress;
|
|
|
|
const OnExtractToHandleProgressParam: Integer64);
|
2025-05-31 22:31:36 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-31 20:10:03 +02:00
|
|
|
{ Helper functions }
|
|
|
|
|
2025-06-03 20:09:20 +02:00
|
|
|
procedure SevenZipWin32Error(const FunctionName: String; ErrorCode: DWORD = 0); overload;
|
2025-05-31 20:10:03 +02:00
|
|
|
begin
|
2025-06-03 20:09:20 +02:00
|
|
|
if ErrorCode = 0 then
|
|
|
|
ErrorCode := GetLastError;
|
|
|
|
const ExceptMessage = FmtSetupMessage(msgErrorFunctionFailedWithMessage,
|
|
|
|
[FunctionName, IntToStr(ErrorCode), Win32ErrorString(ErrorCode)]);
|
|
|
|
const LogMessage = Format('Function %s returned error code %d', [FunctionName, ErrorCode]);
|
|
|
|
SevenZipError(ExceptMessage, LogMessage);
|
2025-05-31 20:10:03 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-30 15:13:12 +02:00
|
|
|
const
|
|
|
|
varFileTime = 64; { Delphi lacks proper VT_FILETIME support }
|
|
|
|
type
|
|
|
|
TVarTypeSet = set of varEmpty..varFileTime; { Incomplete but don't need others }
|
|
|
|
|
|
|
|
function GetProperty(const InArchive: IInArchive; const index: UInt32;
|
|
|
|
const propID: PROPID; const allowedTypes: TVarTypeSet; out value: OleVariant): Boolean; overload;
|
|
|
|
{ Raises an EOleSysError exception on error but otherwise always sets value,
|
2025-06-01 14:15:23 +02:00
|
|
|
returning True if it's not empty. Set index to $FFFF to query an archive property
|
|
|
|
instead of an item propery }
|
2025-05-30 15:13:12 +02:00
|
|
|
begin
|
2025-06-01 14:15:23 +02:00
|
|
|
var Res: HRESULT;
|
|
|
|
if index = $FFFF then
|
|
|
|
Res := InArchive.GetArchiveProperty(propID, value)
|
|
|
|
else
|
|
|
|
Res := InArchive.GetProperty(index, propID, value);
|
2025-05-30 15:13:12 +02:00
|
|
|
if Res <> S_OK then
|
|
|
|
OleError(Res);
|
|
|
|
Result := not VarIsEmpty(Value);
|
|
|
|
if Result and not (VarType(value) in allowedTypes) then
|
|
|
|
OleError(E_FAIL);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function GetProperty(const InArchive: IInArchive; index: UInt32; propID: PROPID;
|
|
|
|
out value: String): Boolean; overload;
|
|
|
|
begin
|
|
|
|
var varValue: OleVariant;
|
|
|
|
Result := GetProperty(InArchive, index, propID, [varOleStr], varValue);
|
|
|
|
value := varValue;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function GetProperty(const InArchive: IInArchive; index: UInt32; propID: PROPID;
|
|
|
|
out value: Cardinal): Boolean; overload;
|
|
|
|
begin
|
|
|
|
var varValue: OleVariant;
|
|
|
|
Result := GetProperty(InArchive, index, propID, [varUInt32], varValue);
|
|
|
|
value := varValue;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function GetProperty(const InArchive: IInArchive; index: UInt32; propID: PROPID;
|
|
|
|
out value: Boolean): Boolean; overload;
|
|
|
|
begin
|
|
|
|
var varValue: OleVariant;
|
|
|
|
Result := GetProperty(InArchive, index, propID, [varBoolean], varValue);
|
|
|
|
value := varValue;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function GetProperty(const InArchive: IInArchive; index: UInt32; propID: PROPID;
|
|
|
|
out value: Integer64): Boolean; overload;
|
|
|
|
begin
|
|
|
|
var varValue: OleVariant;
|
|
|
|
Result := GetProperty(InArchive, index, propID, [varUInt64], varValue);
|
|
|
|
value := Integer64(UInt64(varValue));
|
|
|
|
end;
|
|
|
|
|
|
|
|
function GetProperty(const InArchive: IInArchive; index: UInt32; propID: PROPID;
|
|
|
|
out value: TFileTime): Boolean; overload;
|
|
|
|
begin
|
|
|
|
var varValue: OleVariant;
|
|
|
|
Result := GetProperty(InArchive, index, propID, [varFileTime], varValue);
|
|
|
|
if Result then
|
|
|
|
value := TFileTime(TVarData(varValue).VInt64)
|
|
|
|
else
|
|
|
|
value.Clear;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure PosixHighDetect(var Attrib: DWORD);
|
|
|
|
begin
|
|
|
|
{ "PosixHighDetect", just like FileDir.cpp and similar to 7zMain.c }
|
|
|
|
if Attrib and $F0000000 <> 0 then
|
|
|
|
Attrib := Attrib and $3FFF;
|
|
|
|
end;
|
|
|
|
|
2025-05-17 08:49:25 +02:00
|
|
|
{ TInStream }
|
|
|
|
|
2025-05-31 22:31:36 +02:00
|
|
|
constructor TInStream.Create(const AFile: TFile);
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
|
|
|
inherited Create;
|
2025-05-17 15:50:47 +02:00
|
|
|
FFile := AFile;
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TInStream.Destroy;
|
|
|
|
begin
|
2025-05-17 15:50:47 +02:00
|
|
|
FFile.Free;
|
2025-05-17 08:49:25 +02:00
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TInStream.Read(data: Pointer; size: UInt32;
|
|
|
|
processedSize: PUInt32): HRESULT;
|
|
|
|
begin
|
|
|
|
try
|
2025-05-17 15:50:47 +02:00
|
|
|
var BytesRead := FFile.Read(data^, size);
|
2025-05-17 08:49:25 +02:00
|
|
|
if processedSize <> nil then
|
|
|
|
processedSize^ := BytesRead;
|
|
|
|
Result := S_OK;
|
|
|
|
except
|
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TInStream.Seek(offset: Int64; seekOrigin: UInt32;
|
|
|
|
newPosition: PUInt64): HRESULT;
|
|
|
|
begin
|
|
|
|
try
|
2025-05-17 15:50:47 +02:00
|
|
|
case seekOrigin of
|
|
|
|
STREAM_SEEK_SET: FFile.Seek64(Integer64(offset));
|
|
|
|
STREAM_SEEK_CUR: FFile.Seek64(Integer64(Int64(FFile.Position) + offset));
|
|
|
|
STREAM_SEEK_END: FFile.Seek64(Integer64(Int64(FFile.Size) + offset));
|
|
|
|
end;
|
2025-05-17 08:49:25 +02:00
|
|
|
if newPosition <> nil then
|
2025-05-17 15:50:47 +02:00
|
|
|
newPosition^ := UInt64(FFile.Position);
|
2025-05-17 08:49:25 +02:00
|
|
|
Result := S_OK;
|
|
|
|
except
|
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TSequentialOutStream }
|
|
|
|
|
2025-05-31 22:31:36 +02:00
|
|
|
constructor TSequentialOutStream.Create(const AFile: TFile; const AOwnsFile: Boolean);
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
|
|
|
inherited Create;
|
2025-05-17 15:50:47 +02:00
|
|
|
FFile := AFile;
|
2025-05-31 22:31:36 +02:00
|
|
|
FOwnsFile := AOwnsFile;
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TSequentialOutStream.Destroy;
|
|
|
|
begin
|
2025-05-31 22:31:36 +02:00
|
|
|
if FOwnsFile then
|
|
|
|
FFile.Free;
|
2025-05-17 08:49:25 +02:00
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TSequentialOutStream.Write(data: Pointer; size: UInt32;
|
|
|
|
processedSize: PUInt32): HRESULT;
|
|
|
|
begin
|
|
|
|
try
|
2025-05-17 15:50:47 +02:00
|
|
|
FFile.WriteBuffer(data^, size);
|
2025-05-17 08:49:25 +02:00
|
|
|
if processedSize <> nil then
|
2025-05-17 15:50:47 +02:00
|
|
|
processedSize^ := size;
|
2025-05-17 08:49:25 +02:00
|
|
|
Result := S_OK;
|
|
|
|
except
|
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2025-06-01 18:14:39 +02:00
|
|
|
{ TArchiveCallback }
|
2025-05-17 08:49:25 +02:00
|
|
|
|
2025-06-01 18:14:39 +02:00
|
|
|
constructor TArchiveCallback.Create(const Password: String);
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
FPassword := Password;
|
|
|
|
end;
|
|
|
|
|
2025-06-01 18:14:39 +02:00
|
|
|
function TArchiveCallback.CryptoGetTextPassword(
|
|
|
|
out password: WideString): HRESULT;
|
|
|
|
begin
|
|
|
|
try
|
|
|
|
password := FPassword;
|
|
|
|
Result := S_OK;
|
|
|
|
except
|
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TArchiveOpenCallback }
|
|
|
|
|
2025-05-17 08:49:25 +02:00
|
|
|
function TArchiveOpenCallback.SetCompleted(files,
|
|
|
|
bytes: PUInt64): HRESULT;
|
|
|
|
begin
|
|
|
|
Result := S_OK;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TArchiveOpenCallback.SetTotal(files,
|
|
|
|
bytes: PUInt64): HRESULT;
|
|
|
|
begin
|
|
|
|
Result := S_OK;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
{ TArchiveExtractBaseCallback }
|
2025-05-21 22:19:23 +02:00
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
constructor TArchiveExtractBaseCallback.Create(const InArchive: IInArchive;
|
2025-06-02 20:37:54 +02:00
|
|
|
const numItems: UInt32; const Password: String);
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
2025-06-01 18:14:39 +02:00
|
|
|
inherited Create(Password);
|
2025-05-17 08:49:25 +02:00
|
|
|
FInArchive := InArchive;
|
2025-06-02 20:37:54 +02:00
|
|
|
FnumItems := numItems;
|
2025-05-31 20:10:03 +02:00
|
|
|
FLock := TObject.Create;
|
2025-05-27 19:57:20 +02:00
|
|
|
FResult.OpRes := kOK;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
destructor TArchiveExtractBaseCallback.Destroy;
|
2025-05-27 19:57:20 +02:00
|
|
|
begin
|
2025-05-28 16:32:42 +02:00
|
|
|
FResult.SavedFatalException.Free;
|
2025-05-31 20:10:03 +02:00
|
|
|
FLock.Free;
|
|
|
|
inherited;
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
function TArchiveExtractBaseCallback.SetTotal(total: UInt64): HRESULT;
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
2025-05-17 13:35:44 +02:00
|
|
|
{ From IArchive.h: 7-Zip can call functions for IProgress or ICompressProgressInfo functions
|
|
|
|
from another threads simultaneously with calls for IArchiveExtractCallback interface }
|
2025-05-27 19:57:20 +02:00
|
|
|
try
|
2025-05-31 20:10:03 +02:00
|
|
|
System.TMonitor.Enter(FLock);
|
2025-05-27 19:57:20 +02:00
|
|
|
try
|
2025-05-31 20:10:03 +02:00
|
|
|
FProgressMax := total;
|
2025-05-27 19:57:20 +02:00
|
|
|
finally
|
2025-05-31 20:10:03 +02:00
|
|
|
System.TMonitor.Exit(FLock);
|
2025-05-27 19:57:20 +02:00
|
|
|
end;
|
|
|
|
Result := S_OK;
|
|
|
|
except
|
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end;
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
function TArchiveExtractBaseCallback.SetCompleted(completeValue: PUInt64): HRESULT;
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
2025-05-27 19:57:20 +02:00
|
|
|
try
|
2025-05-29 15:37:27 +02:00
|
|
|
if FAbort then
|
|
|
|
SysUtils.Abort;
|
|
|
|
|
2025-05-31 20:10:03 +02:00
|
|
|
System.TMonitor.Enter(FLock);
|
2025-05-27 19:57:20 +02:00
|
|
|
try
|
2025-05-31 20:10:03 +02:00
|
|
|
FProgress := completeValue^;
|
2025-05-27 19:57:20 +02:00
|
|
|
finally
|
2025-05-31 20:10:03 +02:00
|
|
|
System.TMonitor.Exit(FLock);
|
2025-05-27 19:57:20 +02:00
|
|
|
end;
|
|
|
|
Result := S_OK;
|
|
|
|
except
|
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end;
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
function TArchiveExtractBaseCallback.PrepareOperation(askExtractMode: Int32): HRESULT;
|
2025-05-31 20:10:03 +02:00
|
|
|
begin
|
|
|
|
{ From Client7z.cpp: PrepareOperation is called *after* GetStream has been called }
|
|
|
|
Result := S_OK;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
function TArchiveExtractBaseCallback.SetOperationResult(
|
2025-05-31 20:10:03 +02:00
|
|
|
opRes: TNOperationResult): HRESULT;
|
|
|
|
begin
|
|
|
|
try
|
|
|
|
if opRes <> kOK then begin
|
|
|
|
FResult.OpRes := opRes;
|
|
|
|
Result := E_FAIL; { Make sure it doesn't continue with the next file }
|
|
|
|
end else
|
|
|
|
Result := S_OK;
|
|
|
|
except
|
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 22:01:02 +02:00
|
|
|
function ExtractThreadFunc(Parameter: Pointer): Integer;
|
|
|
|
begin
|
|
|
|
const E = TArchiveExtractBaseCallback(Parameter);
|
|
|
|
try
|
2025-06-01 12:53:12 +02:00
|
|
|
const Indices = E.GetIndices;
|
2025-05-31 22:01:02 +02:00
|
|
|
const NIndices = Length(Indices);
|
|
|
|
if NIndices > 0 then begin
|
2025-06-01 12:53:12 +02:00
|
|
|
{ From IArchive.h: indices must be sorted. Also: 7-Zip's code crashes if
|
|
|
|
sent an invalid index. So we check them fully. }
|
|
|
|
for var I := 0 to NIndices-1 do
|
2025-06-02 20:37:54 +02:00
|
|
|
if (Indices[I] >= E.FnumItems) or ((I > 0) and (Indices[I-1] >= Indices[I])) then
|
2025-06-01 12:53:12 +02:00
|
|
|
InternalError('NIndices invalid');
|
2025-05-31 22:01:02 +02:00
|
|
|
E.FResult.Res := E.FInArchive.Extract(@Indices[0], NIndices, 0, E)
|
|
|
|
end else
|
|
|
|
E.FResult.Res := E.FInArchive.Extract(nil, $FFFFFFFF, 0, E)
|
|
|
|
except
|
|
|
|
const Ex = AcquireExceptionObject;
|
|
|
|
MemoryBarrier;
|
|
|
|
E.FResult.SavedFatalException := Ex;
|
|
|
|
end;
|
|
|
|
{ Be extra sure FSavedFatalException (and everything else) is made visible
|
|
|
|
prior to thread termination. (Likely redundant, but you never know...) }
|
|
|
|
MemoryBarrier;
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
procedure TArchiveExtractBaseCallback.Extract;
|
2025-05-31 20:10:03 +02:00
|
|
|
begin
|
|
|
|
{ We're calling 7-Zip's Extract in a separate thread. This is because packing
|
|
|
|
our example MyProg.exe into a (tiny) .7z and extracting it caused a problem:
|
|
|
|
GetStream and PrepareOperation and SetOperationResult were *all* called by
|
|
|
|
7-Zip from a secondary thread. So we can't block our main thread as well
|
|
|
|
because then we can't communicate progress to it. Having this extra thread
|
|
|
|
has the added bonus of being able to communicate progress more often from
|
|
|
|
SetCompleted. }
|
|
|
|
|
|
|
|
var ThreadID: TThreadID; { Not used but BeginThread requires it }
|
2025-05-31 22:01:02 +02:00
|
|
|
const ThreadHandle = BeginThread(nil, 0, ExtractThreadFunc, Self, 0, ThreadID);
|
2025-05-31 20:10:03 +02:00
|
|
|
if ThreadHandle = 0 then
|
|
|
|
SevenZipWin32Error('BeginThread');
|
|
|
|
|
|
|
|
try
|
|
|
|
try
|
|
|
|
while True do begin
|
|
|
|
case WaitForSingleObject(ThreadHandle, 50) of
|
|
|
|
WAIT_OBJECT_0: Break;
|
|
|
|
WAIT_TIMEOUT: HandleProgress; { This calls the user's OnExtractionProgress handler! }
|
|
|
|
else
|
|
|
|
SevenZipWin32Error('WaitForSingleObject');
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
except
|
|
|
|
{ If an exception was raised during the loop (most likely it would
|
|
|
|
be from the user's OnExtractionProgress handler), request abort
|
|
|
|
and make one more attempt to wait on the thread. If we don't get
|
|
|
|
definitive confirmation that the thread terminated (WAIT_OBJECT_0),
|
|
|
|
then bump the object's reference count to prevent it from being
|
|
|
|
freed, because the thread could still be running and accessing the
|
|
|
|
object. Leaking memory isn't ideal, but a use-after-free problem
|
|
|
|
is worse. Realisitically, though, WaitForSingleObject should never
|
|
|
|
fail if given a valid handle. }
|
|
|
|
FAbort := True; { Atomic so no lock }
|
|
|
|
if WaitForSingleObject(ThreadHandle, INFINITE) <> WAIT_OBJECT_0 then
|
|
|
|
_AddRef;
|
|
|
|
raise;
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
CloseHandle(ThreadHandle);
|
|
|
|
end;
|
|
|
|
|
|
|
|
HandleProgress;
|
|
|
|
HandleResult;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
procedure TArchiveExtractBaseCallback.HandleResult;
|
2025-05-31 20:10:03 +02:00
|
|
|
|
2025-06-03 20:09:20 +02:00
|
|
|
procedure BadOperationResultError(const opRes: TNOperationResult);
|
2025-05-31 20:10:03 +02:00
|
|
|
begin
|
2025-06-03 20:09:20 +02:00
|
|
|
var LogMessage: String;
|
|
|
|
case opRes of
|
|
|
|
kUnsupportedMethod: LogMessage := 'Unsupported method';
|
|
|
|
kDataError: LogMessage := 'Data error';
|
|
|
|
kCRCError: LogMessage := 'CRC error';
|
|
|
|
kUnavailable: LogMessage := 'Unavailable data';
|
|
|
|
kUnexpectedEnd: LogMessage := 'Unexpected end';
|
|
|
|
kDataAfterEnd: LogMessage := 'Data after end';
|
|
|
|
kIsNotArc: LogMessage := 'Is not an archive';
|
|
|
|
kHeadersError: LogMessage := 'Headers error';
|
|
|
|
kWrongPassword: LogMessage := 'Wrong password';
|
|
|
|
else
|
|
|
|
LogMessage := Format('Unknown operation result: %d', [Ord(opRes)]);
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:10:03 +02:00
|
|
|
case opRes of
|
2025-06-03 20:09:20 +02:00
|
|
|
kUnsupportedMethod:
|
2025-06-04 10:10:08 +02:00
|
|
|
SevenZipError(SetupMessages[msgArchiveUnsupportedFormat], LogMessage);
|
2025-06-03 20:09:20 +02:00
|
|
|
kDataError, kCRCError, kUnavailable, kUnexpectedEnd, kDataAfterEnd, kIsNotArc, kHeadersError:
|
2025-06-04 10:10:08 +02:00
|
|
|
SevenZipError(SetupMessages[msgArchiveIsCorrupted], LogMessage);
|
2025-06-03 20:09:20 +02:00
|
|
|
kWrongPassword:
|
2025-06-04 10:10:08 +02:00
|
|
|
SevenZipError(SetupMessages[msgArchiveIncorrectPassword], LogMessage);
|
2025-05-31 20:10:03 +02:00
|
|
|
else
|
2025-06-03 20:09:20 +02:00
|
|
|
SevenZipError(Ord(opRes).ToString, LogMessage);
|
2025-05-31 20:10:03 +02:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2025-06-03 20:09:20 +02:00
|
|
|
procedure BadResultError(const Res: HRESULT);
|
|
|
|
begin
|
|
|
|
if Res = E_OUTOFMEMORY then
|
2025-06-04 10:10:08 +02:00
|
|
|
SevenZipError(Win32ErrorString(E_OUTOFMEMORY))
|
2025-06-03 20:09:20 +02:00
|
|
|
else
|
|
|
|
SevenZipWin32Error('Extract', FResult.Res);
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:10:03 +02:00
|
|
|
begin
|
|
|
|
if Assigned(FResult.SavedFatalException) then begin
|
|
|
|
var Msg: String;
|
|
|
|
if FResult.SavedFatalException is Exception then
|
|
|
|
Msg := (FResult.SavedFatalException as Exception).Message
|
|
|
|
else
|
|
|
|
Msg := FResult.SavedFatalException.ClassName;
|
2025-06-03 20:09:20 +02:00
|
|
|
InternalErrorFmt('Worker thread terminated unexpectedly with exception: %s', [Msg]);
|
2025-05-31 20:10:03 +02:00
|
|
|
end else if FResult.Res = E_ABORT then
|
|
|
|
Abort
|
|
|
|
else begin
|
|
|
|
var OpRes := FResult.OpRes;
|
|
|
|
if OpRes <> kOK then
|
2025-06-03 20:09:20 +02:00
|
|
|
BadOperationResultError(OpRes)
|
2025-05-31 20:10:03 +02:00
|
|
|
else if FResult.Res <> S_OK then
|
2025-06-03 20:09:20 +02:00
|
|
|
BadResultError(FResult.Res);
|
2025-05-31 20:10:03 +02:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
{ TArchiveExtractAllCallback }
|
2025-05-31 20:10:03 +02:00
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
procedure TArchiveExtractAllCallback.TCurrent.SetAttrib(const AAttrib: DWORD);
|
2025-05-31 20:10:03 +02:00
|
|
|
begin
|
|
|
|
Attrib := AAttrib;
|
|
|
|
HasAttrib := True;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
constructor TArchiveExtractAllCallback.Create(const InArchive: IInArchive;
|
2025-06-02 20:37:54 +02:00
|
|
|
const numItems: UInt32; const DisableFsRedir: Boolean;
|
|
|
|
const ArchiveFileName, DestDir, Password: String;
|
2025-05-31 20:10:03 +02:00
|
|
|
const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);
|
|
|
|
begin
|
2025-06-02 20:37:54 +02:00
|
|
|
inherited Create(InArchive, numItems, Password);
|
2025-05-31 20:10:03 +02:00
|
|
|
FDisableFsRedir := DisableFsRedir;
|
|
|
|
FExpandedDestDir := AddBackslash(PathExpand(DestDir));
|
|
|
|
FFullPaths := FullPaths;
|
|
|
|
FExtractedArchiveName := PathExtractName(ArchiveFileName);
|
|
|
|
FOnExtractionProgress := OnExtractionProgress;
|
|
|
|
FLogQueue := TStringList.Create;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
destructor TArchiveExtractAllCallback.Destroy;
|
2025-05-31 20:10:03 +02:00
|
|
|
begin
|
|
|
|
FLogQueue.Free;
|
|
|
|
end;
|
|
|
|
|
2025-06-01 12:53:12 +02:00
|
|
|
function TArchiveExtractAllCallback.GetIndices: TArchiveExtractBaseCallback.TArrayOfUInt32;
|
2025-05-31 22:01:02 +02:00
|
|
|
begin
|
|
|
|
SetLength(Result, 0); { No indices = extract all }
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
function TArchiveExtractAllCallback.GetStream(index: UInt32;
|
2025-05-17 08:49:25 +02:00
|
|
|
out outStream: ISequentialOutStream; askExtractMode: Int32): HRESULT;
|
|
|
|
begin
|
|
|
|
try
|
2025-05-29 15:37:27 +02:00
|
|
|
if FAbort then
|
|
|
|
SysUtils.Abort;
|
|
|
|
|
2025-05-27 19:57:20 +02:00
|
|
|
var NewCurrent := Default(TCurrent);
|
2025-05-17 08:49:25 +02:00
|
|
|
if askExtractMode = kExtract then begin
|
2025-05-25 21:47:13 +02:00
|
|
|
var Path: String;
|
2025-05-30 15:13:12 +02:00
|
|
|
if not GetProperty(FInArchive, index, kpidPath, Path) then
|
2025-05-25 21:47:13 +02:00
|
|
|
Path := PathChangeExt(FExtractedArchiveName, '');
|
|
|
|
var IsDir: Boolean;
|
2025-05-30 15:13:12 +02:00
|
|
|
GetProperty(FInArchive, index, kpidIsDir, IsDir);
|
2025-05-17 08:49:25 +02:00
|
|
|
if IsDir then begin
|
|
|
|
if FFullPaths then begin
|
2025-05-27 19:57:20 +02:00
|
|
|
NewCurrent.Path := Path + '\';
|
|
|
|
if not ValidateAndCombinePath(FExpandedDestDir, Path, NewCurrent.ExpandedPath) then
|
2025-05-25 21:47:13 +02:00
|
|
|
OleError(E_ACCESSDENIED);
|
2025-05-27 19:57:20 +02:00
|
|
|
ForceDirectories(FDisableFsRedir, NewCurrent.ExpandedPath);
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
outStream := nil;
|
|
|
|
end else begin
|
2025-05-25 21:47:13 +02:00
|
|
|
var Attrib: DWORD;
|
2025-05-30 15:13:12 +02:00
|
|
|
if GetProperty(FInArchive, index, kpidAttrib, Attrib) then begin
|
|
|
|
PosixHighDetect(Attrib);
|
2025-05-27 19:57:20 +02:00
|
|
|
NewCurrent.SetAttrib(Attrib);
|
2025-05-26 09:28:26 +02:00
|
|
|
end;
|
2025-05-30 15:13:12 +02:00
|
|
|
GetProperty(FInArchive, index, kpidCTime, NewCurrent.CTime);
|
|
|
|
GetProperty(FInArchive, index, kpidMTime, NewCurrent.MTime);
|
2025-05-17 08:49:25 +02:00
|
|
|
if not FFullPaths then
|
2025-05-25 21:47:13 +02:00
|
|
|
Path := PathExtractName(Path);
|
2025-05-27 19:57:20 +02:00
|
|
|
NewCurrent.Path := Path;
|
|
|
|
if not ValidateAndCombinePath(FExpandedDestDir, Path, NewCurrent.ExpandedPath) then
|
2025-05-25 21:47:13 +02:00
|
|
|
OleError(E_ACCESSDENIED);
|
2025-05-27 19:57:20 +02:00
|
|
|
ForceDirectories(FDisableFsRedir, PathExtractPath(NewCurrent.ExpandedPath));
|
|
|
|
const ExistingFileAttr = GetFileAttributesRedir(FDisableFsRedir, NewCurrent.ExpandedPath);
|
2025-05-26 15:11:20 +02:00
|
|
|
if (ExistingFileAttr <> INVALID_FILE_ATTRIBUTES) and
|
|
|
|
(ExistingFileAttr and FILE_ATTRIBUTE_READONLY <> 0) then
|
2025-05-27 19:57:20 +02:00
|
|
|
SetFileAttributesRedir(FDisableFsRedir, NewCurrent.ExpandedPath, ExistingFileAttr and not FILE_ATTRIBUTE_READONLY);
|
2025-05-31 17:45:22 +02:00
|
|
|
const DestF = TFileRedir.Create(FDisableFsRedir, NewCurrent.ExpandedPath, fdCreateAlways, faWrite, fsNone);
|
|
|
|
var BytesLeft: Integer64;
|
|
|
|
if GetProperty(FInArchive, index, kpidSize, BytesLeft) then begin
|
|
|
|
{ To avoid file system fragmentation, preallocate all of the bytes in the
|
|
|
|
destination file }
|
|
|
|
DestF.Seek64(BytesLeft);
|
|
|
|
DestF.Truncate;
|
|
|
|
DestF.Seek(0);
|
|
|
|
end;
|
2025-05-17 08:49:25 +02:00
|
|
|
{ From IArchive.h: can also set outstream to nil to tell 7zip to skip the file }
|
2025-05-31 17:45:22 +02:00
|
|
|
outstream := TSequentialOutStream.Create(DestF);
|
2025-05-28 17:31:26 +02:00
|
|
|
NewCurrent.outStream := outStream;
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
end;
|
2025-05-31 20:10:03 +02:00
|
|
|
System.TMonitor.Enter(FLock);
|
2025-05-27 19:57:20 +02:00
|
|
|
try
|
2025-05-31 20:10:03 +02:00
|
|
|
FCurrent := NewCurrent;
|
2025-05-27 19:57:20 +02:00
|
|
|
if NewCurrent.Path <> '' then
|
|
|
|
FLogQueue.Append(NewCurrent.Path)
|
|
|
|
finally
|
2025-05-31 20:10:03 +02:00
|
|
|
System.TMonitor.Exit(FLock);
|
2025-05-27 19:57:20 +02:00
|
|
|
end;
|
2025-05-17 08:49:25 +02:00
|
|
|
Result := S_OK;
|
|
|
|
except
|
2025-05-25 21:47:13 +02:00
|
|
|
on E: EOleSysError do
|
|
|
|
Result := E.ErrorCode;
|
2025-05-17 08:49:25 +02:00
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
function TArchiveExtractAllCallback.SetOperationResult(opRes: TNOperationResult): HRESULT;
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
2025-05-17 09:01:39 +02:00
|
|
|
{ From IArchive.h: Can now can close the file, set attributes, timestamps and security information }
|
2025-05-21 22:19:23 +02:00
|
|
|
try
|
2025-05-28 17:31:26 +02:00
|
|
|
try
|
2025-05-31 20:10:03 +02:00
|
|
|
Result := inherited;
|
|
|
|
if Result = S_OK then begin
|
2025-05-28 17:31:26 +02:00
|
|
|
{ GetStream is the only writer to outStream and ExpandedPath and HasAttrib so we don't need a lock because of this note from
|
|
|
|
IArchive.h: 7-Zip doesn't call GetStream/PrepareOperation/SetOperationResult from different threads simultaneously }
|
2025-05-31 20:10:03 +02:00
|
|
|
if (FCurrent.outStream <> nil) and (FCurrent.CTime.HasTime or FCurrent.MTime.HasTime) then
|
|
|
|
SetFileTime((FCurrent.outStream as TSequentialOutStream).FFile.Handle,
|
|
|
|
@FCurrent.CTime, nil, @FCurrent.MTime);
|
|
|
|
FCurrent.outStream := nil; { Like 7zMain.c close the file before setting attributes - note that 7-Zip has cleared its own reference as well already }
|
|
|
|
if (FCurrent.ExpandedPath <> '') and FCurrent.HasAttrib then
|
|
|
|
SetFileAttributesRedir(FDisableFsRedir, FCurrent.ExpandedPath, FCurrent.Attrib);
|
2025-05-28 17:31:26 +02:00
|
|
|
end;
|
|
|
|
finally
|
2025-05-31 20:10:03 +02:00
|
|
|
FCurrent.outStream := nil;
|
2025-05-21 22:19:23 +02:00
|
|
|
end;
|
|
|
|
except
|
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end;
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-31 20:15:12 +02:00
|
|
|
procedure TArchiveExtractAllCallback.HandleProgress;
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
2025-05-31 20:10:03 +02:00
|
|
|
var CurrentPath: String;
|
|
|
|
var Progress, ProgressMax: UInt64;
|
|
|
|
|
|
|
|
System.TMonitor.Enter(FLock);
|
|
|
|
try
|
|
|
|
CurrentPath := FCurrent.Path;
|
|
|
|
Progress := FProgress;
|
|
|
|
ProgressMax := FProgressMax;
|
|
|
|
for var S in FLogQueue do
|
|
|
|
LogFmt('- %s', [S]); { Just like 7zMain.c }
|
|
|
|
FLogQueue.Clear;
|
|
|
|
finally
|
|
|
|
System.TMonitor.Exit(FLock);
|
|
|
|
end;
|
|
|
|
|
|
|
|
var Abort := FAbort;
|
|
|
|
if Abort then
|
|
|
|
Exit;
|
|
|
|
|
|
|
|
if (CurrentPath <> '') and Assigned(FOnExtractionProgress) then begin
|
|
|
|
{ Calls to HandleProgress are already throttled so here we don't have to worry
|
|
|
|
about calling the script to often }
|
|
|
|
if not FOnExtractionProgress(FExtractedArchiveName, CurrentPath, Progress, ProgressMax) then
|
|
|
|
Abort := True;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if not Abort and DownloadTemporaryFileOrExtractArchiveProcessMessages then
|
|
|
|
Application.ProcessMessages;
|
|
|
|
|
|
|
|
if Abort then
|
|
|
|
FAbort := Abort; { Atomic so no lock }
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-31 22:31:36 +02:00
|
|
|
{ TArchiveExtractToHandleCallback }
|
|
|
|
|
|
|
|
constructor TArchiveExtractToHandleCallback.Create(const InArchive: IInArchive;
|
2025-06-02 20:37:54 +02:00
|
|
|
const numItems: UInt32; const Password: String; const Index: UInt32;
|
2025-06-11 20:22:33 +02:00
|
|
|
const DestF: TFile; const OnExtractToHandleProgress: TOnExtractToHandleProgress;
|
|
|
|
const OnExtractToHandleProgressParam: Integer64);
|
2025-05-31 22:31:36 +02:00
|
|
|
begin
|
2025-06-02 20:37:54 +02:00
|
|
|
inherited Create(InArchive, numItems, Password);
|
2025-05-31 22:31:36 +02:00
|
|
|
FIndex := Index;
|
|
|
|
FDestF := DestF;
|
|
|
|
FOnExtractToHandleProgress := OnExtractToHandleProgress;
|
2025-06-11 20:22:33 +02:00
|
|
|
FOnExtractToHandleProgressParam := OnExtractToHandleProgressParam;
|
2025-05-31 22:31:36 +02:00
|
|
|
end;
|
|
|
|
|
2025-06-01 12:53:12 +02:00
|
|
|
function TArchiveExtractToHandleCallback.GetIndices: TArchiveExtractBaseCallback.TArrayOfUInt32;
|
2025-05-31 22:31:36 +02:00
|
|
|
begin
|
|
|
|
SetLength(Result, 1);
|
|
|
|
Result[0] := FIndex;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TArchiveExtractToHandleCallback.GetStream(index: UInt32;
|
|
|
|
out outStream: ISequentialOutStream; askExtractMode: Int32): HRESULT;
|
|
|
|
begin
|
|
|
|
try
|
|
|
|
if askExtractMode = kExtract then begin
|
2025-06-01 12:53:12 +02:00
|
|
|
if index <> FIndex then
|
|
|
|
OleError(E_INVALIDARG);
|
2025-05-31 22:31:36 +02:00
|
|
|
var IsDir: Boolean;
|
|
|
|
GetProperty(FInArchive, index, kpidIsDir, IsDir);
|
|
|
|
if IsDir then
|
|
|
|
OleError(E_INVALIDARG);
|
|
|
|
var BytesLeft: Integer64;
|
|
|
|
if GetProperty(FInArchive, index, kpidSize, BytesLeft) then begin
|
|
|
|
{ To avoid file system fragmentation, preallocate all of the bytes in the
|
|
|
|
destination file }
|
|
|
|
FDestF.Seek64(BytesLeft);
|
|
|
|
FDestF.Truncate;
|
|
|
|
FDestF.Seek(0);
|
|
|
|
end;
|
|
|
|
outstream := TSequentialOutStream.Create(FDestF, False);
|
|
|
|
end;
|
|
|
|
Result := S_OK;
|
|
|
|
except
|
|
|
|
on E: EOleSysError do
|
|
|
|
Result := E.ErrorCode;
|
|
|
|
on E: EAbort do
|
|
|
|
Result := E_ABORT
|
|
|
|
else
|
|
|
|
Result := E_FAIL;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TArchiveExtractToHandleCallback.HandleProgress;
|
|
|
|
begin
|
|
|
|
if Assigned(FOnExtractToHandleProgress) then begin
|
|
|
|
var Progress: UInt64;
|
|
|
|
|
|
|
|
System.TMonitor.Enter(FLock);
|
|
|
|
try
|
|
|
|
Progress := FProgress;
|
|
|
|
finally
|
|
|
|
System.TMonitor.Exit(FLock);
|
|
|
|
end;
|
|
|
|
|
2025-06-11 20:22:33 +02:00
|
|
|
FOnExtractToHandleProgress(Integer64(Progress-FPreviousProgress), FOnExtractToHandleProgressParam);
|
2025-05-31 22:31:36 +02:00
|
|
|
FPreviousProgress := Progress;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:10:03 +02:00
|
|
|
{ Additional helper functions }
|
2025-05-17 08:49:25 +02:00
|
|
|
|
|
|
|
var
|
|
|
|
CreateSevenZipObject: function(const clsid, iid: TGUID; var outObject): HRESULT; stdcall;
|
2025-05-30 12:12:56 +02:00
|
|
|
VersionBanner: String;
|
2025-05-17 08:49:25 +02:00
|
|
|
|
2025-05-30 12:12:56 +02:00
|
|
|
function SevenZipDLLInit(const SevenZipLibrary: HMODULE;
|
|
|
|
[ref] const VersionNumbers: TFileVersionNumbers): Boolean;
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
2025-05-24 13:37:16 +02:00
|
|
|
CreateSevenZipObject := GetProcAddress(SevenZipLibrary, 'CreateObject');
|
|
|
|
Result := Assigned(CreateSevenZipObject);
|
2025-05-30 12:12:56 +02:00
|
|
|
if (VersionNumbers.MS <> 0) or (VersionNumbers.LS <> 0) then
|
|
|
|
VersionBanner := Format(' %u.%.2u', [(VersionNumbers.MS shr 16) and $FFFF, VersionNumbers.MS and $FFFF])
|
|
|
|
else
|
|
|
|
VersionBanner := '';
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-30 15:13:12 +02:00
|
|
|
function GetHandler(const Ext, NotFoundErrorMsg: String): TGUID;
|
|
|
|
begin
|
|
|
|
if SameText(Ext, '.7z') then
|
|
|
|
Result := CLSID_Handler7z
|
|
|
|
else if SameText(Ext, '.zip') then
|
|
|
|
Result := CLSID_HandlerZip
|
|
|
|
else if SameText(Ext, '.gz') then
|
|
|
|
Result := CLSID_HandlerGzip
|
|
|
|
else if SameText(Ext, '.bz2') then
|
|
|
|
Result := CLSID_HandlerBZip2
|
|
|
|
else if SameText(Ext, '.xz') then
|
|
|
|
Result := CLSID_HandlerXz
|
|
|
|
else if SameText(Ext, '.tar') then
|
|
|
|
Result := CLSID_HandlerTar
|
|
|
|
else if SameText(Ext, '.rar') then
|
|
|
|
Result := CLSID_HandlerRar
|
|
|
|
else if SameText(Ext, '.iso') then
|
|
|
|
Result := CLSID_HandlerIso
|
|
|
|
else if SameText(Ext, '.msi') then
|
|
|
|
Result := CLSID_HandlerCompound
|
|
|
|
else if SameText(Ext, '.cab') then
|
|
|
|
Result := CLSID_HandlerCab
|
|
|
|
else if SameText(Ext, '.rpm') then
|
|
|
|
Result := CLSID_HandlerRpm
|
|
|
|
else if SameText(Ext, '.vhd') then
|
|
|
|
Result := CLSID_HandlerVhd
|
|
|
|
else if SameText(Ext, '.vhdx') then
|
|
|
|
Result := CLSID_HandlerVhdx
|
|
|
|
else if SameText(Ext, '.vdi') then
|
|
|
|
Result := CLSID_HandlerVDI
|
|
|
|
else if SameText(Ext, '.vmdk') then
|
|
|
|
Result := CLSID_HandlerVMDK
|
|
|
|
else if SameText(Ext, '.wim') then
|
|
|
|
Result := CLSID_HandlerWim
|
|
|
|
else if SameText(Ext, '.dmg') then
|
|
|
|
Result := CLSID_HandlerDmg
|
|
|
|
else
|
|
|
|
InternalError(NotFoundErrorMsg);
|
|
|
|
end;
|
|
|
|
|
2025-05-31 20:18:04 +02:00
|
|
|
var
|
|
|
|
LoggedBanner: Boolean;
|
|
|
|
|
|
|
|
procedure LogBannerOnce;
|
|
|
|
begin
|
|
|
|
if not LoggedBanner then begin
|
|
|
|
LogFmt('%s Decoder%s : Igor Pavlov', [SetupHeader.SevenZipLibraryName, VersionBanner]); { Just like 7zMain.c }
|
|
|
|
LoggedBanner := True;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2025-05-30 15:13:12 +02:00
|
|
|
function OpenArchiveRedir(const DisableFsRedir: Boolean;
|
2025-06-02 20:37:54 +02:00
|
|
|
const ArchiveFilename, Password: String; const clsid: TGUID; out numItems: UInt32): IInArchive;
|
2025-05-30 15:13:12 +02:00
|
|
|
begin
|
|
|
|
{ CreateObject }
|
|
|
|
if CreateSevenZipObject(clsid, IInArchive, Result) <> S_OK then
|
2025-06-04 10:10:08 +02:00
|
|
|
SevenZipError(SetupMessages[msgArchiveUnsupportedFormat], 'Cannot get class object' { Just like Client7z.cpp });
|
2025-05-30 15:13:12 +02:00
|
|
|
|
|
|
|
{ Open }
|
|
|
|
var F: TFile := nil; { Set to nil to silence compiler }
|
|
|
|
try
|
|
|
|
F := TFileRedir.Create(DisableFsRedir, ArchiveFilename, fdOpenExisting, faRead, fsRead);
|
|
|
|
except
|
|
|
|
SevenZipWin32Error('CreateFile');
|
|
|
|
end;
|
|
|
|
const InStream: IInStream = TInStream.Create(F);
|
|
|
|
var ScanSize: Int64 := 1 shl 23; { From Client7z.cpp }
|
|
|
|
const OpenCallback: IArchiveOpenCallback = TArchiveOpenCallback.Create(Password);
|
|
|
|
if Result.Open(InStream, @ScanSize, OpenCallback) <> S_OK then
|
2025-06-04 10:10:08 +02:00
|
|
|
SevenZipError(SetupMessages[msgArchiveIsCorrupted], 'Cannot open file as archive' { Just like Client7z.cpp });
|
2025-06-02 20:37:54 +02:00
|
|
|
if Result.GetNumberOfItems(numItems) <> S_OK then
|
2025-06-04 10:10:08 +02:00
|
|
|
SevenZipError(SetupMessages[msgArchiveIsCorrupted], 'Cannot get number of items');
|
2025-05-30 15:13:12 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
{ ExtractArchiveRedir }
|
|
|
|
|
2025-05-18 20:31:54 +02:00
|
|
|
procedure ExtractArchiveRedir(const DisableFsRedir: Boolean;
|
|
|
|
const ArchiveFilename, DestDir, Password: String;
|
2025-05-17 13:35:44 +02:00
|
|
|
const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);
|
2025-05-17 08:49:25 +02:00
|
|
|
begin
|
2025-05-28 20:34:25 +02:00
|
|
|
LogArchiveExtractionModeOnce;
|
|
|
|
|
2025-05-17 12:13:11 +02:00
|
|
|
if ArchiveFileName = '' then
|
|
|
|
InternalError('ExtractArchive: Invalid ArchiveFileName value');
|
2025-05-22 12:31:18 +02:00
|
|
|
const clsid = GetHandler(PathExtractExt(ArchiveFilename),
|
|
|
|
'ExtractArchive: Unknown ArchiveFileName extension');
|
2025-05-17 12:13:11 +02:00
|
|
|
if DestDir = '' then
|
|
|
|
InternalError('ExtractArchive: Invalid DestDir value');
|
|
|
|
|
2025-06-02 20:37:54 +02:00
|
|
|
LogFmt('Extracting archive %s to %s. Full paths? %s', [ArchiveFileName,
|
|
|
|
RemoveBackslashUnlessRoot(DestDir), SYesNo[FullPaths]]);
|
2025-05-17 12:13:11 +02:00
|
|
|
|
2025-05-31 20:18:04 +02:00
|
|
|
LogBannerOnce;
|
2025-05-22 12:28:22 +02:00
|
|
|
|
2025-06-06 20:11:14 +02:00
|
|
|
{ Open }
|
|
|
|
var numItems: UInt32;
|
|
|
|
const InArchive = OpenArchiveRedir(DisableFsRedir, ArchiveFilename, Password,
|
|
|
|
clsid, numItems);
|
|
|
|
|
|
|
|
{ Extract }
|
|
|
|
const ExtractCallback: IArchiveExtractCallback =
|
|
|
|
TArchiveExtractAllCallback.Create(InArchive, numItems, DisableFsRedir,
|
|
|
|
ArchiveFilename, DestDir, Password, FullPaths, OnExtractionProgress);
|
|
|
|
(ExtractCallback as TArchiveExtractAllCallback).Extract;
|
|
|
|
|
|
|
|
Log('Everything is Ok'); { Just like 7zMain.c }
|
2025-05-17 08:49:25 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-30 15:13:12 +02:00
|
|
|
{ ArchiveFindFirstFileRedir & co }
|
|
|
|
|
|
|
|
type
|
|
|
|
TArchiveFindState = record
|
|
|
|
InArchive: IInArchive;
|
2025-05-31 22:31:36 +02:00
|
|
|
ExpandedDestDir, ExtractedArchiveName, Password: String;
|
2025-05-31 09:25:50 +02:00
|
|
|
RecurseSubDirs: Boolean;
|
2025-05-30 15:13:12 +02:00
|
|
|
currentIndex, numItems: UInt32;
|
2025-05-31 10:18:46 +02:00
|
|
|
function GetInitialCurrentFindData(out FindData: TWin32FindData): Boolean;
|
2025-05-31 09:25:50 +02:00
|
|
|
procedure FinishCurrentFindData(var FindData: TWin32FindData);
|
2025-05-30 15:13:12 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
TArchiveFindStates = TList<TArchiveFindState>;
|
|
|
|
|
|
|
|
var
|
|
|
|
ArchiveFindStates: TArchiveFindStates;
|
|
|
|
|
2025-05-31 10:18:46 +02:00
|
|
|
function TArchiveFindState.GetInitialCurrentFindData(out FindData: TWin32FindData): Boolean;
|
2025-05-31 10:10:11 +02:00
|
|
|
|
|
|
|
function SkipFile(const Path: String; const IsDir: Boolean): Boolean;
|
|
|
|
begin
|
2025-05-31 11:09:26 +02:00
|
|
|
Result := (not RecurseSubDirs and (IsDir or (PathPos('\', Path) <> 0))) or
|
|
|
|
not ValidateAndCombinePath(ExpandedDestDir, Path);
|
2025-05-31 10:10:11 +02:00
|
|
|
end;
|
2025-05-30 15:13:12 +02:00
|
|
|
|
2025-05-31 10:10:11 +02:00
|
|
|
begin
|
2025-05-30 15:13:12 +02:00
|
|
|
var Path: String;
|
|
|
|
if not GetProperty(InArchive, currentIndex, kpidPath, Path) then
|
|
|
|
Path := PathChangeExt(ExtractedArchiveName, '');
|
|
|
|
var IsDir: Boolean;
|
|
|
|
GetProperty(InArchive, currentIndex, kpidIsDir, IsDir);
|
2025-05-31 10:10:11 +02:00
|
|
|
|
|
|
|
Result := not SkipFile(Path, IsDir);
|
|
|
|
if Result then begin
|
|
|
|
FindData := Default(TWin32FindData);
|
|
|
|
if Length(Path) >= MAX_PATH then
|
|
|
|
InternalError('GetInitialCurrentFindData: Length(Path) >= MAX_PATH');
|
|
|
|
StrPCopy(FindData.cFileName, Path);
|
|
|
|
if IsDir then
|
|
|
|
FindData.dwFileAttributes := FindData.dwFileAttributes or FILE_ATTRIBUTE_DIRECTORY;
|
|
|
|
end;
|
2025-05-31 09:25:50 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TArchiveFindState.FinishCurrentFindData(var FindData: TWin32FindData);
|
|
|
|
begin
|
|
|
|
if FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = 0 then begin
|
|
|
|
var Attrib: DWORD;
|
|
|
|
GetProperty(InArchive, currentIndex, kpidAttrib, Attrib);
|
|
|
|
PosixHighDetect(Attrib);
|
|
|
|
FindData.dwFileAttributes := FindData.dwFileAttributes or Attrib;
|
|
|
|
GetProperty(InArchive, currentIndex, kpidCTime, FindData.ftCreationTime);
|
|
|
|
GetProperty(InArchive, currentIndex, kpidMTime, FindData.ftLastWriteTime);
|
2025-05-30 15:13:12 +02:00
|
|
|
var Size: Integer64;
|
|
|
|
GetProperty(InArchive, currentIndex, kpidSize, Size);
|
2025-05-31 09:25:50 +02:00
|
|
|
FindData.nFileSizeHigh := Size.Hi;
|
|
|
|
FindData.nFileSizeLow := Size.Lo;
|
2025-05-30 15:13:12 +02:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function ArchiveFindFirstFileRedir(const DisableFsRedir: Boolean;
|
2025-06-01 14:15:23 +02:00
|
|
|
const ArchiveFilename, DestDir, Password: String; const RecurseSubDirs,
|
|
|
|
ExtractIntent: Boolean; out FindFileData: TWin32FindData): TArchiveFindHandle;
|
2025-05-30 15:13:12 +02:00
|
|
|
begin
|
2025-05-31 20:18:04 +02:00
|
|
|
LogArchiveExtractionModeOnce;
|
|
|
|
|
2025-05-30 15:13:12 +02:00
|
|
|
if ArchiveFileName = '' then
|
|
|
|
InternalError('ArchiveFindFirstFile: Invalid ArchiveFileName value');
|
|
|
|
const clsid = GetHandler(PathExtractExt(ArchiveFilename),
|
|
|
|
'ArchiveFindFirstFile: Unknown ArchiveFileName extension');
|
|
|
|
|
2025-05-31 20:18:04 +02:00
|
|
|
LogBannerOnce;
|
|
|
|
|
2025-06-06 20:48:32 +02:00
|
|
|
{ Open }
|
|
|
|
var State := Default(TArchiveFindState);
|
|
|
|
State.InArchive := OpenArchiveRedir(DisableFsRedir, ArchiveFilename, Password, clsid, State.numItems);
|
|
|
|
if DestDir <> '' then
|
|
|
|
State.ExpandedDestDir := AddBackslash(PathExpand(DestDir));
|
|
|
|
State.ExtractedArchiveName := PathExtractName(ArchiveFilename);
|
|
|
|
State.Password := Password;
|
|
|
|
State.RecurseSubDirs := RecurseSubDirs;
|
|
|
|
|
|
|
|
if State.numItems > 0 then begin
|
|
|
|
for var currentIndex: UInt32 := 0 to State.numItems-1 do begin
|
|
|
|
if State.GetInitialCurrentFindData(FindFileData) then begin
|
|
|
|
{ Finish state }
|
|
|
|
State.currentIndex := currentIndex;
|
|
|
|
|
|
|
|
{ Save state }
|
|
|
|
if ArchiveFindStates = nil then
|
|
|
|
ArchiveFindStates := TArchiveFindStates.Create;
|
|
|
|
ArchiveFindStates.Add(State);
|
|
|
|
|
|
|
|
{ Log start of extraction }
|
|
|
|
if ExtractIntent then begin
|
2025-06-08 17:28:09 +02:00
|
|
|
LogFmt('Start extracting archive %s to %s. Recurse subdirs? %s', [ArchiveFilename,
|
2025-06-06 20:48:32 +02:00
|
|
|
RemoveBackslashUnlessRoot(DestDir), SYesNo[RecurseSubDirs]]);
|
|
|
|
var Solid: Boolean;
|
|
|
|
if GetProperty(State.InArchive, $FFFF, kpidSolid, Solid) and Solid then
|
|
|
|
Log('Archive is solid; extraction performance may degrade');
|
2025-06-02 16:03:21 +02:00
|
|
|
end;
|
2025-06-06 20:48:32 +02:00
|
|
|
|
|
|
|
{ Finish find data & exit }
|
|
|
|
State.FinishCurrentFindData(FindFileData);
|
|
|
|
Exit(ArchiveFindStates.Count-1);
|
2025-05-31 09:25:50 +02:00
|
|
|
end;
|
|
|
|
end;
|
2025-05-30 15:13:12 +02:00
|
|
|
end;
|
2025-06-06 20:48:32 +02:00
|
|
|
Result := INVALID_HANDLE_VALUE;
|
2025-05-30 15:13:12 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-31 10:16:30 +02:00
|
|
|
function CheckFindFileHandle(const FindFile: TArchiveFindHandle): Integer;
|
2025-05-30 15:13:12 +02:00
|
|
|
begin
|
2025-05-31 10:16:30 +02:00
|
|
|
Result := Integer(FindFile);
|
2025-06-02 16:39:06 +02:00
|
|
|
if (Result < 0) or (Result >= ArchiveFindStates.Count) or
|
|
|
|
(ArchiveFindStates[Result].InArchive = nil) then
|
2025-05-30 15:13:12 +02:00
|
|
|
InternalError('CheckFindFileHandle failed');
|
|
|
|
end;
|
|
|
|
|
2025-05-31 10:16:30 +02:00
|
|
|
function ArchiveFindNextFile(const FindFile: TArchiveFindHandle; out FindFileData: TWin32FindData): Boolean;
|
2025-05-30 15:13:12 +02:00
|
|
|
begin
|
|
|
|
const I = CheckFindFileHandle(FindFile);
|
|
|
|
var State := ArchiveFindStates[I];
|
2025-05-31 09:25:50 +02:00
|
|
|
|
|
|
|
for var currentIndex := State.currentIndex+1 to State.numItems-1 do begin
|
|
|
|
State.currentIndex := currentIndex;
|
2025-05-31 10:18:46 +02:00
|
|
|
if State.GetInitialCurrentFindData(FindFileData) then begin
|
2025-05-31 09:25:50 +02:00
|
|
|
{ Update state }
|
|
|
|
ArchiveFindStates[I] := State; { This just updates currentIndex }
|
|
|
|
|
|
|
|
{ Finish find data & exit }
|
|
|
|
State.FinishCurrentFindData(FindFileData);
|
|
|
|
Exit(True);
|
|
|
|
end;
|
2025-05-30 15:13:12 +02:00
|
|
|
end;
|
2025-05-31 09:25:50 +02:00
|
|
|
Result := False;
|
2025-05-30 15:13:12 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-31 10:16:30 +02:00
|
|
|
function ArchiveFindClose(const FindFile: TArchiveFindHandle): Boolean;
|
2025-05-30 15:13:12 +02:00
|
|
|
begin
|
2025-06-02 16:39:06 +02:00
|
|
|
const I = CheckFindFileHandle(FindFile);
|
|
|
|
var State := ArchiveFindStates[I];
|
|
|
|
State.InArchive := nil;
|
|
|
|
ArchiveFindStates[I] := State; { This just updates InArchive }
|
2025-05-30 15:13:12 +02:00
|
|
|
Result := True;
|
|
|
|
end;
|
|
|
|
|
2025-05-31 22:31:36 +02:00
|
|
|
procedure ArchiveFindExtract(const FindFile: TArchiveFindHandle; const DestF: TFile;
|
2025-06-11 20:22:33 +02:00
|
|
|
const OnExtractToHandleProgress: TOnExtractToHandleProgress;
|
|
|
|
const OnExtractToHandleProgressParam: Integer64);
|
2025-05-31 22:31:36 +02:00
|
|
|
begin
|
2025-06-02 16:39:06 +02:00
|
|
|
const State = ArchiveFindStates[CheckFindFileHandle(FindFile)];
|
2025-05-31 22:31:36 +02:00
|
|
|
|
2025-06-01 12:53:12 +02:00
|
|
|
var FindData: TWin32FindData;
|
|
|
|
if not State.GetInitialCurrentFindData(FindData) or
|
|
|
|
(FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) then
|
|
|
|
InternalError('ArchiveFindExtract: Invalid current');
|
|
|
|
|
2025-06-06 20:48:32 +02:00
|
|
|
const ExtractCallback: IArchiveExtractCallback =
|
|
|
|
TArchiveExtractToHandleCallback.Create(State.InArchive, State.numItems,
|
2025-06-11 20:22:33 +02:00
|
|
|
State.Password, State.currentIndex, DestF, OnExtractToHandleProgress,
|
|
|
|
OnExtractToHandleProgressParam);
|
2025-06-06 20:48:32 +02:00
|
|
|
(ExtractCallback as TArchiveExtractToHandleCallback).Extract;
|
2025-05-31 22:31:36 +02:00
|
|
|
end;
|
|
|
|
|
2025-05-28 17:31:26 +02:00
|
|
|
{ TFileTimeHelper }
|
|
|
|
|
|
|
|
procedure TFileTimeHelper.Clear;
|
|
|
|
begin
|
|
|
|
{ SetFileTime regards a pointer to a FILETIME structure with both members
|
|
|
|
set to 0 the same as a NULL pointer and we make use of that. Note that
|
|
|
|
7-Zip may return a value with both members set to 0 as well. }
|
|
|
|
dwLowDateTime := 0;
|
|
|
|
dwHighDateTime := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TFileTimeHelper.HasTime: Boolean;
|
|
|
|
begin
|
|
|
|
Result := (dwLowDateTime <> 0) or (dwHighDateTime <> 0);
|
|
|
|
end;
|
|
|
|
|
2025-06-02 16:03:21 +02:00
|
|
|
{ SevenZipDLLDeInit }
|
2025-05-30 15:13:12 +02:00
|
|
|
|
2025-06-02 16:03:21 +02:00
|
|
|
procedure SevenZipDLLDeInit;
|
|
|
|
begin
|
|
|
|
{ ArchiveFindStates has references to 7-Zip so must be cleared before the DLL is unloaded }
|
2025-05-30 15:13:12 +02:00
|
|
|
ArchiveFindStates.Free;
|
2025-06-02 16:03:21 +02:00
|
|
|
end;
|
2025-05-30 15:13:12 +02:00
|
|
|
|
2025-05-17 08:49:25 +02:00
|
|
|
end.
|