123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745 |
- 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
- Compression.SevenZipDecoder;
- function SevenZipDLLInit(const SevenZipLibrary: HMODULE): Boolean;
- procedure ExtractArchiveRedir(const DisableFsRedir: Boolean;
- const ArchiveFilename, DestDir, Password: String; const FullPaths: Boolean;
- const OnExtractionProgress: TOnExtractionProgress);
- implementation
- uses
- Classes, SysUtils, Forms, Variants,
- Windows, ActiveX, ComObj,
- Compression.SevenZipDLLDecoder.Interfaces, PathFunc,
- Shared.FileClass, Shared.Int64Em, 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(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(AFile: TFile);
- destructor Destroy; override;
- end;
- TArchiveOpenCallback = class(TInterfacedObject, IArchiveOpenCallback,
- ICryptoGetTextPassword)
- private
- FPassword: String;
- protected
- { IArchiveOpenCallback }
- function SetTotal(files, bytes: PUInt64): HRESULT; stdcall;
- function SetCompleted(files, bytes: PUInt64): HRESULT; stdcall;
- { ICryptoGetTextPassword - queried for on openCallback }
- function CryptoGetTextPassword(out password: WideString): HRESULT; stdcall;
- public
- constructor Create(const Password: String);
- end;
- TArchiveExtractCallback = class(TInterfacedObject, IArchiveExtractCallback,
- ICryptoGetTextPassword)
- private
- const
- varFileTime = 64; { Delphi lacks proper VT_FILETIME support }
- type
- TCurrent = record
- Path, ExpandedPath: String;
- HasAttrib: Boolean;
- Attrib: DWORD;
- CTime, MTime: TFileTime;
- outStream: ISequentialOutStream;
- procedure SetAttrib(const AAttrib: DWORD);
- end;
- TProgress = record
- Current: TCurrent;
- Progress, ProgressMax: UInt64;
- Abort: Boolean;
- end;
- TResult = record
- SavedFatalException: TObject;
- Res: HRESULT;
- OpRes: TNOperationResult;
- end;
- TVarTypeSet = set of varEmpty..varFileTime; { Incomplete but don't need others }
- var
- FInArchive: IInArchive;
- FDisableFsRedir: Boolean;
- FExpandedDestDir, FPassword: String;
- FFullPaths: Boolean;
- FExtractedArchiveName: String;
- FOnExtractionProgress: TOnExtractionProgress;
- FProgressAndLogQueueLock: TObject;
- FProgress: TProgress;
- FLogQueue: TStrings;
- FResult: TResult;
- function GetProperty(const index: UInt32; const propID: PROPID;
- const allowedTypes: TVarTypeSet; out value: OleVariant): Boolean; overload;
- function GetProperty(index: UInt32; propID: PROPID; out value: String): Boolean; overload;
- function GetProperty(index: UInt32; propID: PROPID; out value: UInt32): Boolean; overload;
- function GetProperty(index: UInt32; propID: PROPID; out value: Boolean): Boolean; overload;
- function GetProperty(index: UInt32; propID: PROPID; out value: TFileTime): Boolean; overload;
- 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; stdcall;
- function PrepareOperation(askExtractMode: Int32): HRESULT; stdcall;
- function SetOperationResult(opRes: TNOperationResult): HRESULT; stdcall;
- { ICryptoGetTextPassword - queried for on extractCallback }
- function CryptoGetTextPassword(out password: WideString): HRESULT; stdcall;
- public
- constructor Create(const InArchive: IInArchive;
- const DisableFsRedir: Boolean; const ArchiveFileName, DestDir, Password: String;
- const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);
- destructor Destroy; override;
- end;
- TFileTimeHelper = record helper for TFileTime
- procedure Clear;
- function HasTime: Boolean;
- end;
- function SevenZipSetPassword(const Password: String; out outPassword: WideString): HRESULT;
- begin
- try
- if Password = '' then
- Exit(S_FALSE);
- outPassword := Password;
- Result := S_OK;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- { TInStream }
- constructor TInStream.Create(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.Seek64(Integer64(offset));
- STREAM_SEEK_CUR: FFile.Seek64(Integer64(Int64(FFile.Position) + offset));
- STREAM_SEEK_END: FFile.Seek64(Integer64(Int64(FFile.Size) + offset));
- 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(AFile: TFile);
- begin
- inherited Create;
- FFile := AFile;
- 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;
- { TArchiveOpenCallback }
- constructor TArchiveOpenCallback.Create(const Password: String);
- begin
- inherited Create;
- FPassword := Password;
- end;
- function TArchiveOpenCallback.SetCompleted(files,
- bytes: PUInt64): HRESULT;
- begin
- Result := S_OK;
- end;
- function TArchiveOpenCallback.SetTotal(files,
- bytes: PUInt64): HRESULT;
- begin
- Result := S_OK;
- end;
- function TArchiveOpenCallback.CryptoGetTextPassword(
- out password: WideString): HRESULT;
- begin
- { Note: have not yet seen 7-Zip actually call this, so maybe it's not really needed }
- Result := SevenZipSetPassword(FPassword, password);
- end;
- { TArchiveExtractCallback }
- procedure TArchiveExtractCallback.TCurrent.SetAttrib(const AAttrib: DWORD);
- begin
- Attrib := AAttrib;
- HasAttrib := True;
- end;
- constructor TArchiveExtractCallback.Create(const InArchive: IInArchive;
- const DisableFsRedir: Boolean; const ArchiveFileName, DestDir, Password: String;
- const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);
- begin
- inherited Create;
- FInArchive := InArchive;
- FDisableFsRedir := DisableFsRedir;
- FExpandedDestDir := AddBackslash(PathExpand(DestDir));
- FPassword := Password;
- FFullPaths := FullPaths;
- FExtractedArchiveName := PathExtractName(ArchiveFileName);
- FOnExtractionProgress := OnExtractionProgress;
- FProgressAndLogQueueLock := TObject.Create;
- FLogQueue := TStringList.Create;
- FResult.OpRes := kOK;
- end;
- destructor TArchiveExtractCallback.Destroy;
- begin
- FResult.SavedFatalException.Free;
- FLogQueue.Free;
- FProgressAndLogQueueLock.Free;
- end;
- function TArchiveExtractCallback.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(FProgressAndLogQueueLock);
- try
- FProgress.ProgressMax := total;
- finally
- System.TMonitor.Exit(FProgressAndLogQueueLock);
- end;
- Result := S_OK;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- function TArchiveExtractCallback.SetCompleted(completeValue: PUInt64): HRESULT;
- begin
- try
- System.TMonitor.Enter(FProgressAndLogQueueLock);
- try
- if FProgress.Abort then
- SysUtils.Abort;
- FProgress.Progress := completeValue^;
- finally
- System.TMonitor.Exit(FProgressAndLogQueueLock);
- end;
- Result := S_OK;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- function TArchiveExtractCallback.GetProperty(const index: UInt32;
- const propID: PROPID; const allowedTypes: TVarTypeSet; out value: OleVariant): Boolean;
- { Raises an EOleSysError exception on error but otherwise always sets value,
- returning True if it's not empty }
- begin
- var Res := FInArchive.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 TArchiveExtractCallback.GetProperty(index: UInt32; propID: PROPID;
- out value: String): Boolean;
- begin
- var varValue: OleVariant;
- Result := GetProperty(index, propID, [varOleStr], varValue);
- value := varValue;
- end;
- function TArchiveExtractCallback.GetProperty(index: UInt32; propID: PROPID;
- out value: Cardinal): Boolean;
- begin
- var varValue: OleVariant;
- Result := GetProperty(index, propID, [varUInt32], varValue);
- value := varValue;
- end;
- function TArchiveExtractCallback.GetProperty(index: UInt32; propID: PROPID;
- out value: Boolean): Boolean;
- begin
- var varValue: OleVariant;
- Result := GetProperty(index, propID, [varBoolean], varValue);
- value := varValue;
- end;
- function TArchiveExtractCallback.GetProperty(index: UInt32; propID: PROPID;
- out value: TFileTime): Boolean;
- begin
- var varValue: OleVariant;
- Result := GetProperty(index, propID, [varFileTime], varValue);
- if Result then
- value := TFileTime(TVarData(varValue).VInt64)
- else
- value.Clear;
- end;
- function TArchiveExtractCallback.GetStream(index: UInt32;
- out outStream: ISequentialOutStream; askExtractMode: Int32): HRESULT;
- begin
- try
- var NewCurrent := Default(TCurrent);
- if askExtractMode = kExtract then begin
- var Path: String;
- if not GetProperty(index, kpidPath, Path) then
- Path := PathChangeExt(FExtractedArchiveName, '');
- var IsDir: Boolean;
- GetProperty(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(index, kpidAttrib, Attrib) then begin
- if Attrib and $F0000000 <> 0 then
- Attrib := Attrib and $3FFF; { "PosixHighDetect", just like FileDir.cpp and similar to 7zMain.c }
- NewCurrent.SetAttrib(Attrib);
- end;
- GetProperty(index, kpidCTime, NewCurrent.CTime);
- GetProperty(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);
- { From IArchive.h: can also set outstream to nil to tell 7zip to skip the file }
- outstream := TSequentialOutStream.Create(TFileRedir.Create(FDisableFsRedir, NewCurrent.ExpandedPath, fdCreateAlways, faWrite, fsNone));
- NewCurrent.outStream := outStream;
- end;
- end;
- System.TMonitor.Enter(FProgressAndLogQueueLock);
- try
- if FProgress.Abort then
- SysUtils.Abort;
- FProgress.Current := NewCurrent;
- if NewCurrent.Path <> '' then
- FLogQueue.Append(NewCurrent.Path)
- finally
- System.TMonitor.Exit(FProgressAndLogQueueLock);
- 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 TArchiveExtractCallback.PrepareOperation(askExtractMode: Int32): HRESULT;
- begin
- { From Client7z.cpp: PrepareOperation is called *after* GetStream has been called }
- Result := S_OK;
- end;
- function TArchiveExtractCallback.SetOperationResult(opRes: TNOperationResult): HRESULT;
- begin
- { From IArchive.h: Can now can close the file, set attributes, timestamps and security information }
- try
- try
- if opRes <> kOK then begin
- FResult.OpRes := opRes;
- Result := E_FAIL; { Make sure it doesn't continue with the next file }
- end else 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 (FProgress.Current.outStream <> nil) and (FProgress.Current.CTime.HasTime or FProgress.Current.MTime.HasTime) then
- SetFileTime((FProgress.Current.outStream as TSequentialOutStream).FFile.Handle,
- @FProgress.Current.CTime, nil, @FProgress.Current.MTime);
- FProgress.Current.outStream := nil; { Like 7zMain.c close the file before setting attributes - note that 7-Zip has cleared its own reference as well already }
- if (FProgress.Current.ExpandedPath <> '') and FProgress.Current.HasAttrib then
- SetFileAttributesRedir(FDisableFsRedir, FProgress.Current.ExpandedPath, FProgress.Current.Attrib);
- Result := S_OK;
- end;
- finally
- FProgress.Current.outStream := nil;
- end;
- except
- on E: EAbort do
- Result := E_ABORT
- else
- Result := E_FAIL;
- end;
- end;
- function TArchiveExtractCallback.CryptoGetTextPassword(
- out password: WideString): HRESULT;
- begin
- Result := SevenZipSetPassword(FPassword, password);
- end;
- {---}
- var
- CreateSevenZipObject: function(const clsid, iid: TGUID; var outObject): HRESULT; stdcall;
- function SevenZipDLLInit(const SevenZipLibrary: HMODULE): Boolean;
- begin
- CreateSevenZipObject := GetProcAddress(SevenZipLibrary, 'CreateObject');
- Result := Assigned(CreateSevenZipObject);
- end;
- procedure SevenZipError(const LogMessage, ExceptMessage: String);
- { Do not call from secondary thread. LogMessage may contain non-localized text
- ExceptMessage should not. }
- begin
- LogFmt('ERROR: %s', [LogMessage]); { Just like 7zMain.c }
- raise Exception.Create(ExceptMessage);
- end;
- procedure SevenZipWin32Error(const FunctionName: String; LastError: DWORD = 0); overload;
- begin
- if LastError = 0 then
- LastError := GetLastError;
- const Msg = Format('%s (%u)', [Win32ErrorString(LastError), LastError]);
- SevenZipError(Format('%s failed: %s', [FunctionName, Msg]), Msg);
- end;
- function ExtractThreadFunc(Parameter: Pointer): Integer;
- begin
- const E = TArchiveExtractCallback(Parameter);
- try
- 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 ExtractArchiveRedir(const DisableFsRedir: Boolean;
- const ArchiveFilename, DestDir, Password: String;
- const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);
- 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;
- procedure HandleProgress(const E: TArchiveExtractCallback);
- begin
- var Progress: TArchiveExtractCallback.TProgress;
- System.TMonitor.Enter(E.FProgressAndLogQueueLock);
- try
- Progress := E.FProgress;
- for var S in E.FLogQueue do
- LogFmt('- %s', [S]); { Just like 7zMain.c }
- E.FLogQueue.Clear;
- finally
- System.TMonitor.Exit(E.FProgressAndLogQueueLock);
- end;
- if Progress.Abort then
- Exit;
- var Abort := False;
- if (Progress.Current.Path <> '') and Assigned(E.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 E.FOnExtractionProgress(E.FExtractedArchiveName, Progress.Current.Path, Progress.Progress, Progress.ProgressMax) then
- Abort := True;
- end;
- if not Abort and DownloadTemporaryFileOrExtractArchiveProcessMessages then
- Application.ProcessMessages;
- if Abort then begin
- System.TMonitor.Enter(E.FProgressAndLogQueueLock);
- try
- E.FProgress.Abort := True;
- finally
- System.TMonitor.Exit(E.FProgressAndLogQueueLock);
- end;
- end;
- end;
- function OperationResultToString(const opRes: TNOperationResult): String;
- begin
- case opRes of
- kOK: Result := 'OK';
- kUnsupportedMethod: Result := 'Unsupported method';
- kDataError: Result := 'Data error';
- kCRCError: Result := 'CRC error';
- kUnavailable: Result := 'Unavailable';
- kUnexpectedEnd: Result := 'Unexpected end';
- kDataAfterEnd: Result := 'Data after end';
- kIsNotArc: Result := 'Is not an archive';
- kHeadersError: Result := 'Headers error';
- kWrongPassword: Result := 'Wrong password';
- else
- Result := Format('Unknown operation result: %d', [Ord(opRes)]);
- end;
- end;
- procedure HandleResult([Ref] const Result: TArchiveExtractCallback.TResult);
- begin
- if Assigned(Result.SavedFatalException) then begin
- var Msg: String;
- if Result.SavedFatalException is Exception then
- Msg := (Result.SavedFatalException as Exception).Message
- else
- Msg := Result.SavedFatalException.ClassName;
- SevenZipError(Format('Worker thread terminated unexpectedly with exception: %s', [Msg]), Msg);
- end else if Result.Res = E_ABORT then
- Abort
- else begin
- var OpRes := Result.OpRes;
- if OpRes <> kOK then
- SevenZipError(OperationResultToString(Result.OpRes), Ord(OpRes).ToString)
- else if Result.Res <> S_OK then
- SevenZipWin32Error('Extract', Result.Res);
- end;
- end;
- procedure Extract(const E: TArchiveExtractCallback);
- 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, E, 0, ThreadID);
- if ThreadHandle = 0 then
- SevenZipWin32Error('BeginThread');
- try
- while True do begin
- case WaitForSingleObject(ThreadHandle, 50) of
- WAIT_OBJECT_0: Break;
- WAIT_TIMEOUT: HandleProgress(E);
- else
- SevenZipWin32Error('WaitForSingleObject');
- end;
- end;
- finally
- CloseHandle(ThreadHandle);
- end;
- HandleProgress(E);
- HandleResult(E.FResult);
- end;
- begin
- LogArchiveExtractionModeOnce;
- if ArchiveFileName = '' then
- InternalError('ExtractArchive: Invalid ArchiveFileName value');
- const clsid = GetHandler(PathExtractExt(ArchiveFilename),
- 'ExtractArchive: Unknown ArchiveFileName extension');
- if DestDir = '' then
- InternalError('ExtractArchive: Invalid DestDir value');
- LogFmt('Extracting archive %s to %s. Full paths? %s', [ArchiveFileName, DestDir, SYesNo[FullPaths]]);
- LogFmt('%s Decoder : Igor Pavlov', [SetupHeader.SevenZipLibraryName]); { Just like 7zMain.c }
- try
- { CreateObject }
- var InArchive: IInArchive;
- if CreateSevenZipObject(clsid, IInArchive, InArchive) <> S_OK then
- SevenZipError('Cannot get class object' { Just like Client7z.cpp }, '-1');
- { 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 InArchive.Open(InStream, @ScanSize, OpenCallback) <> S_OK then
- SevenZipError('Cannot open file as archive' { Just like Client7z.cpp }, '-2');
- { Extract }
- const ExtractCallback: IArchiveExtractCallback =
- TArchiveExtractCallback.Create(InArchive, DisableFsRedir,
- ArchiveFilename, DestDir, Password, FullPaths, OnExtractionProgress);
- Extract(ExtractCallback as TArchiveExtractCallback);
- Log('Everything is Ok'); { Just like 7zMain.c }
- except
- on E: EAbort do
- raise Exception.Create(SetupMessages[msgErrorExtractionAborted])
- else
- raise Exception.Create(FmtSetupMessage(msgErrorExtractionFailed, [GetExceptMessage]));
- end;
- 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;
- end.
|