unit CabFile;
{$ObjExportAll On}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, HSetup;
type
TCabInfo = record
CabinetPath:string;
CabinetFile:string;
DiskName:string;
Id:Shortint;
CabinetNumber:Shortint;
end;
{$EXTERNALSYM TCabInfo}
TOnCabInfo = procedure (Sender: TObject;CabInfo:TCabInfo) of object;
{$EXTERNALSYM TOnCabInfo}
TOnExtracted = procedure (Sender: TObject;Successed:boolean;var Continue:boolean;Source,Dest:string) of object;
{$EXTERNALSYM TOnExtracted}
TOnExtractFile = procedure (Sender: TObject;FileName:string;DestPath:string) of object;
{$EXTERNALSYM TOnExtractFile}
TOnNeedNewCabinet = procedure (Sender: TObject;var Continue:boolean;CabInfo:TCabInfo;var NewPath:string) of object;
{$EXTERNALSYM TOnNeedNewCabinet}
TCabFile = class(TComponent)
private
FFileName: TFileName;
FFiles: TstringList;
FOnCabInfo: TOnCabInfo;
FOnFiles: TNotifyEvent;
FOnExtracted: TOnExtracted;
FDestPath:string;
FOnExtractFile: TOnExtractFile;
FOnNeed: TOnNeedNewCabinet;
FTmpstring : string;
Fdll:THandle;
FSetupIterateCabinet:TSetupIterateCabinet;
procedure SetFileName(const Value: TFileName);
procedure SetFiles(const Value: TstringList);
procedure RefreshFiles;
protected
public
constructor Create(AOwner: TComponent);override;
destructor Destroy;override;
published
property FileName:TFileName read FFileName write SetFileName;
property Files:TstringList read FFiles write SetFiles;
property OnCabInfo:TOnCabInfo read FOnCabInfo write FOnCabInfo;
property OnFilesListed:TNotifyEvent read FOnFiles write FonFiles;
property OnFileExtracted:TOnExtracted read FOnExtracted write FOnExtracted;
property OnStartFileExtraction:TOnExtractFile read FOnExtractFile write FonExtractFile;
property OnNeedNewCabinet:TOnNeedNewCabinet read FonNeed write FOnNeed;
function ExtractAll(destpath:string):boolean;
function ExtractFile(FileName:string;DestPath:string):boolean;
end;
implementation
resourcestring
RC_SetupApiDll = 'Unable to find setupapi.dll';
const
FileTimeBase = -109205.0;
FileTimeStep: Extended = 24.0 * 60.0 * 60.0 * 1000.0 * 1000.0 * 10.0;
// 100 nSek per Day
function FileTimeToDateTime(const FileTime: TFileTime): TDateTime;
begin
Result := Int64(FileTime) / FileTimeStep;
Result := Result + FileTimeBase;
end;
{**************************************************}
constructor TCabFile.Create(AOwner: TComponent);
begin
inherited;
FFiles:=TstringList.Create;
FFileName:='';
FDll:=LoadLibrary('setupapi.dll');
if Fdll<>0 then
FSetupIterateCabinet:=GetProcAddress(Fdll,'SetupIterateCabinetA')
else
raise Exception.Create(RC_SetupApiDll);
end;
{**************************************************}
destructor TCabFile.Destroy;
begin
Ffiles.free;
if Fdll<>0 then
FreeLibrary(Fdll);
inherited;
end;
{**************************************************}
procedure TCabFile.SetFileName(const Value: TFileName);
begin
FFileName := Value;
RefreshFiles;
end;
{**************************************************}
procedure TCabFile.SetFiles(const Value: TstringList);
begin
//do nothing !!!!
end;
{**************************************************}
function CBack(Context
ointer;Notification,Param1:Integer;var Param2:integer):Integer;stdcall;
var
cab
FILE_IN_CABINET_INFO;
Sender:TCabFile;
cabinfo:TCabInfo;
fileinfo: string;
filetime: TFileTime;
begin
result:=ERROR_BAD_COMMAND;
if Context<>nil then
Sender:=TCabFile(Context^)
else
exit;
//this callback is only for listing files in a cabinet ... pouet pouet !
if Notification = SPFILENOTIFY_FILEINCABINET then
//found a file in the cabinet
begin
result:=FILEOP_SKIP;
cab:=PFILE_IN_CABINET_INFO(Param1);
fileinfo := format('%-50s',[strpas(cab^.NameInCabinet)]);
fileinfo := fileinfo + format('%10s',[inttostr(cab^.FileSize)]);
Windows.DosDateTimeToFileTime(Cab^.DosDate,Cab^.DosTime,fileTime);
fileinfo := fileinfo + format('%20s',[formatdatetime('yyyy-mm-dd hh:nn', FileTimeToDateTime(filetime))]);
Sender.FFiles.Add(fileinfo);
// Sender.FFiles.Add(strpas(cab^.NameInCabinet));
end
else
if Notification = SPFILENOTIFY_CABINETINFO then
//give cabinet info
begin
if Assigned(Sender.FOnCabInfo)and(Param1<>0) then
begin
cabinfo.CabinetPath:=strpas(PCabinet_Info(Param1)^.CabinetPath);
cabinfo.CabinetFile:=strpas(PCabinet_Info(Param1)^.CabinetFile);
cabinfo.DiskName:=strpas(PCabinet_Info(Param1)^.DiskName);
cabinfo.Id:=PCabinet_Info(Param1)^.Id;
cabinfo.CabinetNumber:=PCabinet_Info(Param1)^.CabinetNumber;
Sender.FOnCabInfo(Sender,cabinfo);
end;
result:=0;
end;
end;
{**************************************************}
function CExtract(Context
ointer;Notification,Param1:Integer;var Param2:integer):Integer;stdcall;
var
cab
FILE_IN_CABINET_INFO;
Sender:TCabFile;
cabinfo:TCabInfo;
continue:boolean;
FPath:TFilePath;
Path:string;
i:Integer;
begin
result:=ERROR_BAD_COMMAND;
if Context<>nil then
Sender:=TCabFile(Context^)
else
exit;
//this callback is only for listing files in a cabinet ...
if notification = SPFILENOTIFY_CABINETINFO then
result:=0
else
if Notification = SPFILENOTIFY_FILEINCABINET then
//found a file in the cabinet
begin
try
result:=FILEOP_DOIT;
cab:=PFILE_IN_CABINET_INFO(Param1);
if Sender.FDestPath[length(Sender.FDestPath)]='/' then
begin
//extract all
path:=Sender.FDestPath+strpas(cab^.NameInCabinet);
for i:=1 to length(path)do
cab^.FullTargetName[i-1]:=path
;
cab^.FullTargetName[length(path)]:=#0;
if Assigned(Sender.FOnExtractFile) then
Sender.FOnExtractFile(Sender,cab^.FullTargetName,Sender.FDestPath);
end
else
begin
//Extract specific file
if UpperCase(extractFileName(Sender.FDestPath))=UpperCase(strpas(cab^.NameInCabinet)) then
begin
path:=Sender.FDestPath;
for i:=1 to length(path)do
cab^.FullTargetName[i-1]:=path;
cab^.FullTargetName[length(path)]:=#0;
if Assigned(Sender.FOnExtractFile) then
Sender.FOnExtractFile(Sender,cab^.FullTargetName,Sender.FDestPath);
end
else
result:=FILEOP_SKIP;
end;
except
result:=FILEOP_SKIP;
end;
end
else
if Notification = SPFILENOTIFY_FILEEXTRACTED then
begin
Continue:=true;
if param1<>0 then
FPath:=PFilePath(param1)^;
if Assigned(Sender.FOnExtracted) then
Sender.FOnExtracted(Sender,(FPath.ErrorCode=NO_ERROR),continue,
StrPas(FPath.Source),strpas(FPath.Target));
if continue then
result:=NO_ERROR
else
result:=ERROR_BAD_COMMAND;
end
else
if Notification = SPFILENOTIFY_NEEDNEWCABINET then
begin
if param1<>0 then
begin
cabinfo.CabinetPath:=strpas(PCabinet_Info(Param1)^.CabinetPath);
cabinfo.CabinetFile:=strpas(PCabinet_Info(Param1)^.CabinetFile);
cabinfo.DiskName:=strpas(PCabinet_Info(Param1)^.DiskName);
cabinfo.Id:=PCabinet_Info(Param1)^.Id;
cabinfo.CabinetNumber:=PCabinet_Info(Param1)^.CabinetNumber;
Continue:=true;
path:='';
if Assigned(Sender.FonNeed) then
begin
Sender.FOnNeed(Sender,Continue,cabinfo,path);
Sender.FTmpstring:=path;
param2:=Longint(PChar(Sender.FTmpstring));
end
else
result:=ERROR_BAD_COMMAND;
end
else
result:=ERROR_BAD_COMMAND;
end;
end;
{**************************************************}
procedure TCabFile.RefreshFiles;
begin
FFiles.Clear;
if @FSetupIterateCabinet<>nil then
if FSetupIterateCabinet(PChar(FFileName),0,CBack,@self) then
if Assigned(FonFiles) then
FOnfiles(self);
end;
{**************************************************}
function TCabFile.ExtractAll(destpath: string):boolean;
begin
if destpath[length(destpath)]<>'/' then
DestPath:=DestPath+'/';
FDestPath:=DestPath;
if @FSetupIterateCabinet<>nil then
result:=FSetupIterateCabinet(PChar(FFileName),0,CExtract,@self)
else
result:=false;
end;
{**************************************************}
function TCabFile.ExtractFile(FileName, DestPath: string):boolean;
begin
if destpath[length(destpath)]<>'/' then
DestPath:=DestPath+'/';
FDestPath:=DestPath+FileName;
if @FSetupIterateCabinet<>nil then
result:=FSetupIterateCabinet(PChar(FFileName),0,CExtract,@self)
else
result:=false;
end;
{**************************************************}
end.