123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293 |
- unit Compression.SevenZipDLLDecoder;
- {
- Inno Setup
- Copyright (C) 1997-2025 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Interface to the 7-Zip Decoder DLLs, used by Setup
- Based on the 7-Zip source code and the 7-Zip Delphi API by Henri Gourvest
- https://github.com/geoffsmith82/d7zip MPL 1.1 licensed
- }
- interface
- uses
- Windows, Shared.FileClass, Shared.VerInfoFunc, Shared.Int64Em, Compression.SevenZipDecoder;
- function SevenZipDLLInit(const SevenZipLibrary: HMODULE;
- [ref] const VersionNumbers: TFileVersionNumbers): Boolean;
- procedure SevenZipDLLDeInit;
- procedure MapArchiveExtensions(const DestExt, SourceExt: String);
- procedure ExtractArchiveRedir(const DisableFsRedir: Boolean;
- const ArchiveFilename, DestDir, Password: String; const FullPaths: Boolean;
- const OnExtractionProgress: TOnExtractionProgress);
- { 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
- 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. }
- type
- TArchiveFindHandle = type NativeUInt;
- TOnExtractToHandleProgress = procedure(const Bytes, Param: Integer64);
- function ArchiveFindFirstFileRedir(const DisableFsRedir: Boolean;
- const ArchiveFilename, DestDir, Password: String;
- const RecurseSubDirs, ExtractIntent: Boolean;
- out FindFileData: TWin32FindData): TArchiveFindHandle;
- function ArchiveFindNextFile(const FindFile: TArchiveFindHandle; out FindFileData: TWin32FindData): Boolean;
- function ArchiveFindClose(const FindFile: TArchiveFindHandle): Boolean;
- procedure ArchiveFindExtract(const FindFile: TArchiveFindHandle; const DestF: TFile;
- const OnExtractToHandleProgress: TOnExtractToHandleProgress; const OnExtractToHandleProgressParam: Integer64);
- type
- TFileTimeHelper = record helper for TFileTime
- procedure Clear;
- function HasTime: Boolean;
- end;
- implementation
- uses
- Classes, SysUtils, Forms, Variants, ActiveX, ComObj, Generics.Collections, Generics.Defaults,
- Compression.SevenZipDLLDecoder.Interfaces, PathFunc,
- Shared.SetupMessageIDs, Shared.CommonFunc,
- SetupLdrAndSetup.Messages, SetupLdrAndSetup.RedirFunc,
- Setup.LoggingFunc, Setup.MainFunc, Setup.InstFunc;
- type
- TInStream = class(TInterfacedObject, IInStream)
- private
- FFile: TFile;
- protected
- function Read(data: Pointer; size: UInt32; processedSize: PUInt32): HRESULT; stdcall;
- function Seek(offset: Int64; seekOrigin: UInt32; newPosition: PUInt64): HRESULT; stdcall;
- public
- constructor Create(const AFile: TFile);
- destructor Destroy; override;
- end;
- TSequentialOutStream = class(TInterfacedObject, ISequentialOutStream)
- private
- FFile: TFile;
- protected
- function Write(data: Pointer; size: UInt32; processedSize: PUInt32): HRESULT; stdcall;
- public
- constructor Create(const AFileToBeDuplicated: TFile);
- destructor Destroy; override;
- end;
- TArchiveCallback = class(TInterfacedObject, ICryptoGetTextPassword)
- private
- FPassword: String;
- protected
- { 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 }
- function CryptoGetTextPassword(out password: WideString): HRESULT; stdcall;
- public
- constructor Create(const Password: String);
- end;
- TArchiveOpenCallback = class(TArchiveCallback, IArchiveOpenCallback)
- protected
- { IArchiveOpenCallback }
- function SetTotal(files, bytes: PUInt64): HRESULT; stdcall;
- function SetCompleted(files, bytes: PUInt64): HRESULT; stdcall;
- end;
- TArchiveOpenCallbackWithStreamBackup = class(TArchiveOpenCallback)
- private
- FStreamBackup: IInStream;
- public
- constructor Create(const Password: String; const StreamToBackup: IInStream);
- end;
- TArchiveOpenFileCallback = class(TArchiveOpenCallback, IArchiveOpenVolumeCallback)
- private
- FDisableFsRedir: Boolean;
- FArchiveFilename: String;
- protected
- { IArchiveOpenVolumeCallback - queried for by 7-Zip on IArchiveOpenCallback }
- function GetProperty(propID: PROPID; var value: OleVariant): HRESULT; stdcall;
- function GetStream(const name: PChar; var inStream: IInStream): HRESULT; stdcall;
- public
- constructor Create(const DisableFsRedir: Boolean; const ArchiveFilename, Password: String);
- end;
- TArchiveExtractBaseCallback = class(TArchiveCallback, IArchiveExtractCallback)
- private
- type
- TResult = record
- SavedFatalException: TObject;
- Res: HRESULT;
- OpRes: TNOperationResult;
- end;
- TArrayOfUInt32 = array of UInt32;
- var
- FInArchive: IInArchive;
- FnumItems: UInt32;
- FLock: TObject;
- FProgress, FProgressMax: UInt64;
- FAbort: Boolean;
- FResult: TResult;
- protected
- { IProgress }
- function SetTotal(total: UInt64): HRESULT; stdcall;
- function SetCompleted(completeValue: PUInt64): HRESULT; stdcall;
- { IArchiveExtractCallback }
- function GetStream(index: UInt32; out outStream: ISequentialOutStream;
- askExtractMode: Int32): HRESULT; virtual; stdcall; abstract;
- function PrepareOperation(askExtractMode: Int32): HRESULT; stdcall;
- function SetOperationResult(opRes: TNOperationResult): HRESULT; virtual; stdcall;
- { Other }
- function GetIndices: TArrayOfUInt32; virtual; abstract;
- procedure Extract;
- procedure HandleProgress; virtual; abstract;
- procedure HandleResult;
- public
- constructor Create(const InArchive: IInArchive; const numItems: UInt32;
- const Password: String);
- destructor Destroy; override;
- end;
- TArchiveExtractAllCallback = class(TArchiveExtractBaseCallback)
- private
- type
- TCurrent = record
- Path, ExpandedPath: String;
- HasAttrib: Boolean;
- Attrib: DWORD;
- CTime, MTime: TFileTime;
- outStream: ISequentialOutStream;
- procedure SetAttrib(const AAttrib: DWORD);
- end;
- var
- FDisableFsRedir: Boolean;
- FExpandedDestDir: String;
- FFullPaths: Boolean;
- FExtractedArchiveName: String;
- FOnExtractionProgress: TOnExtractionProgress;
- FCurrent: TCurrent; { Protected by base's FLock }
- FLogQueue: TStrings; { Same }
- protected
- { IArchiveExtractCallback }
- function GetStream(index: UInt32; out outStream: ISequentialOutStream;
- askExtractMode: Int32): HRESULT; override; stdcall;
- function SetOperationResult(opRes: TNOperationResult): HRESULT; override; stdcall;
- { Other }
- function GetIndices: TArchiveExtractBaseCallback.TArrayOfUInt32; override;
- procedure HandleProgress; override;
- public
- constructor Create(const InArchive: IInArchive; const numItems: UInt32;
- const DisableFsRedir: Boolean; const ArchiveFileName, DestDir, Password: String;
- const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);
- destructor Destroy; override;
- end;
- TArchiveExtractToHandleCallback = class(TArchiveExtractBaseCallback)
- private
- FIndex: UInt32;
- FDestF: TFile;
- FOnExtractToHandleProgress: TOnExtractToHandleProgress;
- FOnExtractToHandleProgressParam: Integer64;
- FPreviousProgress: UInt64;
- protected
- { IArchiveExtractCallback }
- function GetStream(index: UInt32; out outStream: ISequentialOutStream;
- askExtractMode: Int32): HRESULT; override; stdcall;
- { Other }
- function GetIndices: TArchiveExtractBaseCallback.TArrayOfUInt32; override;
- procedure HandleProgress; override;
- public
- constructor Create(const InArchive: IInArchive; const numItems: UInt32;
- const Password: String; const Index: UInt32; const DestF: TFile;
- const OnExtractToHandleProgress: TOnExtractToHandleProgress;
- const OnExtractToHandleProgressParam: Integer64);
- destructor Destroy; override;
- end;
- { Helper functions }
- procedure SevenZipWin32Error(const FunctionName: String; const ErrorCode: DWORD); overload;
- begin
- const ExceptMessage = FmtSetupMessage(msgErrorFunctionFailedWithMessage,
- [FunctionName, IntToStr(ErrorCode), Win32ErrorString(ErrorCode)]);
- const LogMessage = Format('Function %s returned error code %d', [FunctionName, ErrorCode]);
- SevenZipError(ExceptMessage, LogMessage);
- end;
- procedure SevenZipWin32Error(const FunctionName: String); overload;
- begin
- SevenZipWin32Error(FunctionName, GetLastError);
- end;
- function GetHandler(const Filename, NotFoundErrorMsg: String): TGUID; forward;
- 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,
- returning True if it's not empty. Set index to $FFFF to query an archive property
- instead of an item propery }
- begin
- var Res: HRESULT;
- if index = $FFFF then
- Res := InArchive.GetArchiveProperty(propID, value)
- else
- Res := InArchive.GetProperty(index, propID, value);
- 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: UInt64): Boolean; overload;
- begin
- var varValue: OleVariant;
- Result := GetProperty(InArchive, index, propID, [varUInt64], varValue);
- value := 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;
- { TInStream }
- constructor TInStream.Create(const AFile: TFile);
- begin
- inherited Create;
- FFile := AFile;
- end;
- destructor TInStream.Destroy;
- begin
- FFile.Free;
- inherited;
- end;
- function TInStream.Read(data: Pointer; size: UInt32;
- processedSize: PUInt32): HRESULT;
- begin
- try
- var BytesRead := FFile.Read(data^, size);
- 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
- case seekOrigin of
- STREAM_SEEK_SET: FFile.Seek(offset);
- STREAM_SEEK_CUR: FFile.Seek(FFile.Position + offset);
- STREAM_SEEK_END: FFile.Seek(FFile.Size + offset);
- else
- Exit(E_INVALIDARG);
- end;
- if newPosition <> nil then
- newPosition^ := UInt64(FFile.Position);
- Result := S_OK;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- { TSequentialOutStream }
- constructor TSequentialOutStream.Create(const AFileToBeDuplicated: TFile);
- begin
- inherited Create;
- FFile := TFile.CreateDuplicate(AFileToBeDuplicated);
- end;
- destructor TSequentialOutStream.Destroy;
- begin
- FFile.Free;
- inherited;
- end;
- function TSequentialOutStream.Write(data: Pointer; size: UInt32;
- processedSize: PUInt32): HRESULT;
- begin
- try
- FFile.WriteBuffer(data^, size);
- if processedSize <> nil then
- processedSize^ := size;
- Result := S_OK;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- { TArchiveCallback }
- constructor TArchiveCallback.Create(const Password: String);
- begin
- inherited Create;
- FPassword := Password;
- end;
- 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 }
- function TArchiveOpenCallback.SetCompleted(files,
- bytes: PUInt64): HRESULT;
- begin
- Result := S_OK;
- end;
- function TArchiveOpenCallback.SetTotal(files,
- bytes: PUInt64): HRESULT;
- begin
- Result := S_OK;
- end;
- { TArchiveOpenCallbackWithStreamBackup }
- constructor TArchiveOpenCallbackWithStreamBackup.Create(const Password: String;
- const StreamToBackup: IInStream);
- begin
- inherited Create(Password);
- FStreamBackup := StreamToBackup;
- end;
- { TArchiveOpenFileCallback }
- constructor TArchiveOpenFileCallback.Create(const DisableFsRedir: Boolean; const ArchiveFilename,
- Password: String);
- begin
- inherited Create(Password);
- FDisableFsRedir := DisableFsRedir;
- FArchiveFilename := ArchiveFilename;
- end;
- function TArchiveOpenFileCallback.GetProperty(propID: PROPID; var value: OleVariant): HRESULT;
- begin
- { This is for multi-volume archives: when the archive is opened 7-Zip only receives a stream. It
- will then use this callback to find the name of the archive (like archive.7z.001) to figure out
- the name of other volumes (like archive.7z.002) }
- if propID = kpidName then
- value := FArchiveFilename
- else
- value := Unassigned; { Note sure if this is really needed }
- Result := S_OK;
- end;
- function TArchiveOpenFileCallback.GetStream(const name: PChar; var inStream: IInStream): HRESULT;
- begin
- { This is for multi-volume archives: after 7-Zip figures out the name of other volumes (like
- archive.7z.002) it will then use this callback to open it. The callback must either return
- S_FALSE or set instream to nil when it tries to open a volume which doesn't exists (like
- archive.7z.003 when there's two volumes only). }
- try
- if NewFileExistsRedir(FDisableFsRedir, name) then begin
- const F = TFileRedir.Create(FDisableFsRedir, name, fdOpenExisting, faRead, fsRead);
- instream := TInStream.Create(F);
- end else
- instream := nil;
- Result := S_OK;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- { TArchiveExtractBaseCallback }
- constructor TArchiveExtractBaseCallback.Create(const InArchive: IInArchive;
- const numItems: UInt32; const Password: String);
- begin
- inherited Create(Password);
- FInArchive := InArchive;
- FnumItems := numItems;
- FLock := TObject.Create;
- FResult.OpRes := kOK;
- end;
- destructor TArchiveExtractBaseCallback.Destroy;
- begin
- FResult.SavedFatalException.Free;
- FLock.Free;
- inherited;
- end;
- function TArchiveExtractBaseCallback.SetTotal(total: UInt64): HRESULT;
- begin
- { From IArchive.h: 7-Zip can call functions for IProgress or ICompressProgressInfo functions
- from another threads simultaneously with calls for IArchiveExtractCallback interface }
- try
- System.TMonitor.Enter(FLock);
- try
- FProgressMax := total;
- finally
- System.TMonitor.Exit(FLock);
- end;
- Result := S_OK;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- function TArchiveExtractBaseCallback.SetCompleted(completeValue: PUInt64): HRESULT;
- begin
- try
- if FAbort then
- SysUtils.Abort;
- System.TMonitor.Enter(FLock);
- try
- FProgress := completeValue^;
- finally
- System.TMonitor.Exit(FLock);
- end;
- Result := S_OK;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- function TArchiveExtractBaseCallback.PrepareOperation(askExtractMode: Int32): HRESULT;
- begin
- { From Client7z.cpp: PrepareOperation is called *after* GetStream has been called }
- Result := S_OK;
- end;
- function TArchiveExtractBaseCallback.SetOperationResult(
- 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;
- function ExtractThreadFunc(Parameter: Pointer): Integer;
- begin
- const E = TArchiveExtractBaseCallback(Parameter);
- try
- const Indices = E.GetIndices;
- const NIndices = Length(Indices);
- if NIndices > 0 then begin
- { 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
- if (Indices[I] >= E.FnumItems) or ((I > 0) and (Indices[I-1] >= Indices[I])) then
- InternalError('NIndices invalid');
- 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;
- procedure TArchiveExtractBaseCallback.Extract;
- 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 }
- const ThreadHandle = BeginThread(nil, 0, ExtractThreadFunc, Self, 0, ThreadID);
- 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! }
- WAIT_FAILED: SevenZipWin32Error('WaitForSingleObject');
- else
- SevenZipError('WaitForSingleObject returned unknown value');
- 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;
- procedure TArchiveExtractBaseCallback.HandleResult;
- procedure BadOperationResultError(const opRes: TNOperationResult);
- begin
- 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;
- case opRes of
- kUnsupportedMethod:
- SevenZipError(SetupMessages[msgArchiveUnsupportedFormat], LogMessage);
- kDataError, kCRCError, kUnavailable, kUnexpectedEnd, kDataAfterEnd, kIsNotArc, kHeadersError:
- SevenZipError(SetupMessages[msgArchiveIsCorrupted], LogMessage);
- kWrongPassword:
- SevenZipError(SetupMessages[msgArchiveIncorrectPassword], LogMessage);
- else
- SevenZipError(Ord(opRes).ToString, LogMessage);
- end;
- end;
- procedure BadResultError(const Res: HRESULT);
- begin
- if Res = E_OUTOFMEMORY then
- SevenZipError(Win32ErrorString(E_OUTOFMEMORY))
- else
- SevenZipWin32Error('Extract', FResult.Res);
- end;
- 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;
- InternalErrorFmt('Worker thread terminated unexpectedly with exception: %s', [Msg]);
- end else begin
- var OpRes := FResult.OpRes;
- if OpRes <> kOK then
- BadOperationResultError(OpRes)
- else if FResult.Res <> S_OK then
- BadResultError(FResult.Res);
- end;
- end;
- { TArchiveExtractAllCallback }
- procedure TArchiveExtractAllCallback.TCurrent.SetAttrib(const AAttrib: DWORD);
- begin
- Attrib := AAttrib;
- HasAttrib := True;
- end;
- constructor TArchiveExtractAllCallback.Create(const InArchive: IInArchive;
- const numItems: UInt32; const DisableFsRedir: Boolean;
- const ArchiveFileName, DestDir, Password: String;
- const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);
- begin
- inherited Create(InArchive, numItems, Password);
- FDisableFsRedir := DisableFsRedir;
- FExpandedDestDir := AddBackslash(PathExpand(DestDir));
- FFullPaths := FullPaths;
- FExtractedArchiveName := PathExtractName(ArchiveFileName);
- FOnExtractionProgress := OnExtractionProgress;
- FLogQueue := TStringList.Create;
- end;
- destructor TArchiveExtractAllCallback.Destroy;
- begin
- FLogQueue.Free;
- end;
- function TArchiveExtractAllCallback.GetIndices: TArchiveExtractBaseCallback.TArrayOfUInt32;
- begin
- SetLength(Result, 0); { No indices = extract all }
- end;
- function TArchiveExtractAllCallback.GetStream(index: UInt32;
- out outStream: ISequentialOutStream; askExtractMode: Int32): HRESULT;
- begin
- try
- if FAbort then
- SysUtils.Abort;
- var NewCurrent := Default(TCurrent);
- if askExtractMode = kExtract then begin
- var Path: String;
- if not GetProperty(FInArchive, index, kpidPath, Path) then
- Path := PathChangeExt(FExtractedArchiveName, '');
- var IsDir: Boolean;
- GetProperty(FInArchive, index, kpidIsDir, IsDir);
- if IsDir then begin
- if FFullPaths then begin
- NewCurrent.Path := Path + '\';
- if not ValidateAndCombinePath(FExpandedDestDir, Path, NewCurrent.ExpandedPath) then
- OleError(E_ACCESSDENIED);
- ForceDirectories(FDisableFsRedir, NewCurrent.ExpandedPath);
- end;
- outStream := nil;
- end else begin
- var Attrib: DWORD;
- if GetProperty(FInArchive, index, kpidAttrib, Attrib) then begin
- PosixHighDetect(Attrib);
- NewCurrent.SetAttrib(Attrib);
- end;
- GetProperty(FInArchive, index, kpidCTime, NewCurrent.CTime);
- GetProperty(FInArchive, index, kpidMTime, NewCurrent.MTime);
- if not FFullPaths then
- Path := PathExtractName(Path);
- NewCurrent.Path := Path;
- if not ValidateAndCombinePath(FExpandedDestDir, Path, NewCurrent.ExpandedPath) then
- OleError(E_ACCESSDENIED);
- ForceDirectories(FDisableFsRedir, PathExtractPath(NewCurrent.ExpandedPath));
- const ExistingFileAttr = GetFileAttributesRedir(FDisableFsRedir, NewCurrent.ExpandedPath);
- if (ExistingFileAttr <> INVALID_FILE_ATTRIBUTES) and
- (ExistingFileAttr and FILE_ATTRIBUTE_READONLY <> 0) then
- SetFileAttributesRedir(FDisableFsRedir, NewCurrent.ExpandedPath, ExistingFileAttr and not FILE_ATTRIBUTE_READONLY);
- const DestF = TFileRedir.Create(FDisableFsRedir, NewCurrent.ExpandedPath, fdCreateAlways, faWrite, fsNone);
- try
- var BytesLeft: UInt64;
- if GetProperty(FInArchive, index, kpidSize, BytesLeft) then begin
- { To avoid file system fragmentation, preallocate all of the bytes in the
- destination file }
- DestF.Seek(Int64(BytesLeft));
- DestF.Truncate;
- DestF.Seek(0);
- end;
- { From IArchive.h: can also set outstream to nil to tell 7zip to skip the file }
- outstream := TSequentialOutStream.Create(DestF);
- finally
- { TSequentialOutStream duplicates the TFile, so DestF is no longer needed }
- DestF.Free;
- end;
- NewCurrent.outStream := outStream;
- end;
- end;
- System.TMonitor.Enter(FLock);
- try
- FCurrent := NewCurrent;
- if NewCurrent.Path <> '' then
- FLogQueue.Append(NewCurrent.Path)
- finally
- System.TMonitor.Exit(FLock);
- 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;
- function TArchiveExtractAllCallback.SetOperationResult(opRes: TNOperationResult): HRESULT;
- begin
- { From IArchive.h: Can now can close the file, set attributes, timestamps and security information }
- try
- try
- Result := inherited;
- if Result = S_OK then begin
- { 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 }
- 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);
- end;
- finally
- FCurrent.outStream := nil;
- end;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- procedure TArchiveExtractAllCallback.HandleProgress;
- begin
- 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;
- 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;
- end;
- if DownloadTemporaryFileOrExtractArchiveProcessMessages then
- Application.ProcessMessages;
- end;
- { TArchiveExtractToHandleCallback }
- constructor TArchiveExtractToHandleCallback.Create(const InArchive: IInArchive;
- const numItems: UInt32; const Password: String; const Index: UInt32;
- const DestF: TFile; const OnExtractToHandleProgress: TOnExtractToHandleProgress;
- const OnExtractToHandleProgressParam: Integer64);
- begin
- inherited Create(InArchive, numItems, Password);
- FIndex := Index;
- FDestF := TFile.CreateDuplicate(DestF);
- FOnExtractToHandleProgress := OnExtractToHandleProgress;
- FOnExtractToHandleProgressParam := OnExtractToHandleProgressParam;
- end;
- destructor TArchiveExtractToHandleCallback.Destroy;
- begin
- FDestF.Free;
- inherited;
- end;
- function TArchiveExtractToHandleCallback.GetIndices: TArchiveExtractBaseCallback.TArrayOfUInt32;
- 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
- if index <> FIndex then
- OleError(E_INVALIDARG);
- var IsDir: Boolean;
- GetProperty(FInArchive, index, kpidIsDir, IsDir);
- if IsDir then
- OleError(E_INVALIDARG);
- var BytesLeft: UInt64;
- if GetProperty(FInArchive, index, kpidSize, BytesLeft) then begin
- { To avoid file system fragmentation, preallocate all of the bytes in the
- destination file }
- FDestF.Seek(Int64(BytesLeft));
- FDestF.Truncate;
- FDestF.Seek(0);
- end;
- outstream := TSequentialOutStream.Create(FDestF);
- 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;
- FOnExtractToHandleProgress(Integer64(Progress-FPreviousProgress), FOnExtractToHandleProgressParam);
- FPreviousProgress := Progress;
- end;
- end;
- { Additional helper functions }
- type
- TSevenZipHandlers = TDictionary<String, TGUID>;
- var
- CreateSevenZipObject: function(const clsid, iid: TGUID; var outObject): HRESULT; stdcall;
- VersionBanner: String;
- Handlers: TSevenZipHandlers;
- function SevenZipDLLInit(const SevenZipLibrary: HMODULE;
- [ref] const VersionNumbers: TFileVersionNumbers): Boolean;
- begin
- CreateSevenZipObject := GetProcAddress(SevenZipLibrary, 'CreateObject');
- Result := Assigned(CreateSevenZipObject);
- 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 := '';
- Handlers := TSevenZipHandlers.Create(TIStringComparer.Ordinal);
- Handlers.Add('.7z', CLSID_Handler7z);
- Handlers.Add('.zip', CLSID_HandlerZip);
- Handlers.Add('.gz', CLSID_HandlerGzip);
- Handlers.Add('.bz2', CLSID_HandlerBZip2);
- Handlers.Add('.xz', CLSID_HandlerXz);
- Handlers.Add('.tar', CLSID_HandlerTar);
- Handlers.Add('.rar', CLSID_HandlerRar);
- Handlers.Add('.iso', CLSID_HandlerIso);
- Handlers.Add('.msi', CLSID_HandlerCompound);
- Handlers.Add('.cab', CLSID_HandlerCab);
- Handlers.Add('.rpm', CLSID_HandlerRpm);
- Handlers.Add('.vhd', CLSID_HandlerVhd);
- Handlers.Add('.vhdx', CLSID_HandlerVhdx);
- Handlers.Add('.vdi', CLSID_HandlerVDI);
- Handlers.Add('.vmdk', CLSID_HandlerVMDK);
- Handlers.Add('.wim', CLSID_HandlerWim);
- Handlers.Add('.dmg', CLSID_HandlerDmg);
- Handlers.Add('.001', CLSID_HandlerSplit);
- end;
- function GetHandlerForExt(const Ext, NotFoundErrorMsg: String): TGUID;
- begin
- if not Handlers.TryGetValue(Ext, Result) then
- InternalError(NotFoundErrorMsg);
- end;
- function GetHandler(const Filename, NotFoundErrorMsg: String): TGUID;
- begin;
- Result := GetHandlerForExt(PathExtractExt(Filename), NotFoundErrorMsg);
- end;
- procedure MapArchiveExtensions(const DestExt, SourceExt: String);
- begin
- if (Length(DestExt) < 2) or (DestExt[1] <> '.') then
- InternalError('MapArchiveExtensions: Invalid DestExt');
- const clsid = GetHandlerForExt(SourceExt, 'MapArchiveExtensions: Invalid SourceExt');
- Handlers.AddOrSetValue(DestExt, clsid);
- end;
- 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;
- function OpenArchiveRedir(const DisableFsRedir: Boolean;
- const ArchiveFilename, Password: String; const clsid: TGUID; out numItems: UInt32): IInArchive;
- const
- DefaultScanSize: Int64 = 1 shl 23; { From Client7z.cpp }
- begin
- { CreateObject }
- if CreateSevenZipObject(clsid, IInArchive, Result) <> S_OK then
- SevenZipError(SetupMessages[msgArchiveUnsupportedFormat], 'Cannot get class object' { Just like Client7z.cpp });
- { Open }
- var F: TFile := nil; { Set to nil to silence compiler }
- try
- F := TFileRedir.Create(DisableFsRedir, ArchiveFilename, fdOpenExisting, faRead, fsRead);
- except
- on E: EFileError do
- SevenZipWin32Error('CreateFile', E.ErrorCode);
- end;
- const InStream: IInStream = TInStream.Create(F);
- var ScanSize := DefaultScanSize;
- const OpenCallback: IArchiveOpenCallback = TArchiveOpenFileCallback.Create(DisableFsRedir, ArchiveFileName, Password);
- if Result.Open(InStream, @ScanSize, OpenCallback) <> S_OK then
- SevenZipError(SetupMessages[msgArchiveIsCorrupted], 'Cannot open file as archive' { Just like Client7z.cpp });
- if Result.GetNumberOfItems(numItems) <> S_OK then
- SevenZipError(SetupMessages[msgArchiveIsCorrupted], 'Cannot get number of items');
- if numItems = 1 then begin
- { Get inner archive stream if it exists - See OpenArchive.cpp CArchiveLink::Open
- Give up trying to get or open it on any error }
- var MainSubFile: Cardinal;
- var SubSeqStream: ISequentialInStream;
- if not GetProperty(Result, $FFFF, kpidMainSubfile, MainSubFile) or
- (MainSubFile <> 0) or
- not Supports(Result, IInArchiveGetStream) or
- ((Result as IInArchiveGetStream).GetStream(MainSubFile, SubSeqStream) <> S_OK) or
- (SubSeqStream = nil) or
- not Supports(SubSeqStream, IInStream) then
- Exit;
- const SubStream = SubSeqStream as IInStream;
- { Open inner archive }
- var MainSubFilePath: String;
- if not GetProperty(Result, MainSubFile, kpidPath, MainSubFilePath) then
- Exit;
- if MainSubFilePath = '' then
- MainSubFilePath := PathChangeExt(ArchiveFilename, '');
- var SubClsid: TGUID;
- try
- SubClsid := GetHandler(MainSubFilePath, '');
- except
- Exit;
- end;
- var SubResult: IInArchive;
- if CreateSevenZipObject(SubClsid, IInArchive, SubResult) <> S_OK then
- Exit;
- var SubScanSize := DefaultScanSize;
- const SubOpenCallback: IArchiveOpenCallback =
- TArchiveOpenCallbackWithStreamBackup.Create(Password, InStream); { In tests the backup of InStream wasn't needed but better safe than sorry }
- var SubNumItems: UInt32;
- if (SubResult.Open(SubStream, @SubScanSize, SubOpenCallback) <> S_OK) or
- (SubResult.GetNumberOfItems(SubNumItems) <> S_OK) then
- Exit;
- Result := SubResult;
- numItems := SubNumItems;
- end;
- end;
- { ExtractArchiveRedir }
- procedure ExtractArchiveRedir(const DisableFsRedir: Boolean;
- const ArchiveFilename, DestDir, Password: String;
- const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);
- begin
- LogArchiveExtractionModeOnce;
- if ArchiveFileName = '' then
- InternalError('ExtractArchive: Invalid ArchiveFileName value');
- const clsid = GetHandler(ArchiveFilename,
- 'ExtractArchive: Unknown ArchiveFileName extension');
- if DestDir = '' then
- InternalError('ExtractArchive: Invalid DestDir value');
- LogFmt('Extracting archive %s to %s. Full paths? %s', [ArchiveFileName,
- RemoveBackslashUnlessRoot(DestDir), SYesNo[FullPaths]]);
- LogBannerOnce;
- { 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 }
- end;
- { ArchiveFindFirstFileRedir & co }
- type
- TArchiveFindState = record
- InArchive: IInArchive;
- ExpandedDestDir, ExtractedArchiveName, Password: String;
- RecurseSubDirs: Boolean;
- currentIndex, numItems: UInt32;
- function GetInitialCurrentFindData(out FindData: TWin32FindData): Boolean;
- procedure FinishCurrentFindData(var FindData: TWin32FindData);
- end;
- TArchiveFindStates = TList<TArchiveFindState>;
- var
- ArchiveFindStates: TArchiveFindStates;
- function TArchiveFindState.GetInitialCurrentFindData(out FindData: TWin32FindData): Boolean;
- function SkipFile(const Path: String; const IsDir: Boolean): Boolean;
- begin
- Result := (not RecurseSubDirs and (IsDir or (PathPos('\', Path) <> 0))) or
- not ValidateAndCombinePath(ExpandedDestDir, Path);
- end;
- begin
- var Path: String;
- if not GetProperty(InArchive, currentIndex, kpidPath, Path) then
- Path := PathChangeExt(ExtractedArchiveName, '');
- var IsDir: Boolean;
- GetProperty(InArchive, currentIndex, kpidIsDir, IsDir);
- 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;
- 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);
- var Size: UInt64;
- GetProperty(InArchive, currentIndex, kpidSize, Size);
- FindData.nFileSizeHigh := Int64Rec(Size).Hi;
- FindData.nFileSizeLow := Int64Rec(Size).Lo;
- end;
- end;
- function ArchiveFindFirstFileRedir(const DisableFsRedir: Boolean;
- const ArchiveFilename, DestDir, Password: String; const RecurseSubDirs,
- ExtractIntent: Boolean; out FindFileData: TWin32FindData): TArchiveFindHandle;
- begin
- LogArchiveExtractionModeOnce;
- if ArchiveFileName = '' then
- InternalError('ArchiveFindFirstFile: Invalid ArchiveFileName value');
- const clsid = GetHandler(ArchiveFilename,
- 'ArchiveFindFirstFile: Unknown ArchiveFileName extension');
- LogBannerOnce;
- { 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;
- { Log start of extraction }
- if ExtractIntent then begin
- LogFmt('Start extracting archive %s to %s. Recurse subdirs? %s', [ArchiveFilename,
- 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');
- end;
- 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);
- { Finish find data & exit }
- State.FinishCurrentFindData(FindFileData);
- Exit(ArchiveFindStates.Count-1);
- end;
- end;
- end;
- Result := INVALID_HANDLE_VALUE;
- end;
- function CheckFindFileHandle(const FindFile: TArchiveFindHandle): Integer;
- begin
- Result := Integer(FindFile);
- if (Result < 0) or (Result >= ArchiveFindStates.Count) or
- (ArchiveFindStates[Result].InArchive = nil) then
- InternalError('CheckFindFileHandle failed');
- end;
- function ArchiveFindNextFile(const FindFile: TArchiveFindHandle; out FindFileData: TWin32FindData): Boolean;
- begin
- const I = CheckFindFileHandle(FindFile);
- var State := ArchiveFindStates[I];
- for var currentIndex := State.currentIndex+1 to State.numItems-1 do begin
- State.currentIndex := currentIndex;
- if State.GetInitialCurrentFindData(FindFileData) then begin
- { Update state }
- ArchiveFindStates[I] := State; { This just updates currentIndex }
- { Finish find data & exit }
- State.FinishCurrentFindData(FindFileData);
- Exit(True);
- end;
- end;
- Result := False;
- end;
- function ArchiveFindClose(const FindFile: TArchiveFindHandle): Boolean;
- begin
- const I = CheckFindFileHandle(FindFile);
- var State := ArchiveFindStates[I];
- State.InArchive := nil;
- ArchiveFindStates[I] := State; { This just updates InArchive }
- Result := True;
- end;
- procedure ArchiveFindExtract(const FindFile: TArchiveFindHandle; const DestF: TFile;
- const OnExtractToHandleProgress: TOnExtractToHandleProgress;
- const OnExtractToHandleProgressParam: Integer64);
- begin
- const State = ArchiveFindStates[CheckFindFileHandle(FindFile)];
- var FindData: TWin32FindData;
- if not State.GetInitialCurrentFindData(FindData) or
- (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) then
- InternalError('ArchiveFindExtract: Invalid current');
- const ExtractCallback: IArchiveExtractCallback =
- TArchiveExtractToHandleCallback.Create(State.InArchive, State.numItems,
- State.Password, State.currentIndex, DestF, OnExtractToHandleProgress,
- OnExtractToHandleProgressParam);
- (ExtractCallback as TArchiveExtractToHandleCallback).Extract;
- end;
- { 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;
- { SevenZipDLLDeInit }
- procedure SevenZipDLLDeInit;
- begin
- FreeAndNil(Handlers);
- { ArchiveFindStates has references to 7-Zip so must be cleared before the DLL is unloaded }
- FreeAndNil(ArchiveFindStates);
- end;
- end.
|