Do you know any free component, compatible with Delphi 2010 or XE to manage ZIP archives (actually, only reading archive contents and extracting files required)?
Please no betas.
I thought about ZipForge from ComponentAce, but it's free only for personal use. No software distribution allowed.
You can get the TurboPower Abbrevia for 2010 from:
http://tpabbrevia.sourceforge.net/
you can take a look at this if you like 7zip
If you only need decoding (developed for Delphi 2007, not yet tested under Delphi 2010/XE):
unit UnitZip;
interface
uses
SysUtils, Classes;
type
EZipException = class( Exception );
TZipFileInfo = record
LastModified: TDateTime;
Crc32: Longword;
CompressedSize: Longword;
UncompressedSize: Longword;
end;
TZipFileReader = class
private
// Information about the memory mapped file
FFileHandle: THandle;
FFileMapping: THandle;
FMappedAddress: Pointer;
// Location of the ZIPfile in memory. Currently we only support memory mapped ZIPfiles without disk spanning.
FStart: Pointer;
FSize: Longword;
// ZIP file contents
FFilenames: TStrings;
function GetZipFileInfo ( const FileName: AnsiString ): TZipFileInfo;
public
constructor Create ( const FileName: string; ZipStartOffset: Int64 = 0; Size: Longword = 0 ); overload;
constructor Create ( const ResourceName, ResourceType: string; Instance: HMODULE = 0 ); overload;
constructor Create ( Buffer: Pointer; Size: Longword ); overload;
destructor Destroy; override;
function GetFile ( const FileName: string ): TBytes; overload;
function GetFile ( FileID: Integer ): TBytes; overload;
property FileNames: TStrings read FFileNames;
property FileInfo [ const FileName: AnsiString ]: TZipFileInfo read GetZipFileInfo;
end;
implementation
uses
ZLib, Windows;
const
cResourceNotFound = 'Resource not found: %s.%s.';
cResourceNotLoaded = 'Resource not loaded: %s.%s.';
cCannotOpenFile = 'Cannot open file %s: OS error: %d.';
cCannotGetFileSize = 'Cannot get file size of file %s: OS error: %d.';
cCannotMapFile = 'Cannot create file mapping of file %s: OS error: %d.';
cZipFileTooSmall = 'ZIP file is too small.';
cZipFileFormatError = 'ZIP file is invalid.';
cZipBufferInvalid = 'ZIP memory buffer is invalid.';
cUnsupportedMethod = 'ZIP unsupported compression method: %d.';
cFileNotFoundInZip = 'File not found in ZIP content: %s';
// ZIP file format records.
// The generic zip file format is ( TLocalFileHeader; Name; Extra; compressed data )* ( TFileHeader; Name; Extra; Remark )* TLastHeader
type
TFileInfo = packed record
NeededVersion: Word; // 20
Flags: Word; // 1=Text,4=extra present
ZipMethod: Word; // 0=stored 8=deflate
LastModified: Longword; // time in dos format or Unix Timestamp
Crc32: Longword;
CompressedSize: Longword;
UncompressedSize: Longword;
NameSize: Word;
ExtraSize: Word;
end;
TFileHeader = packed record
Signature: Longword; // $02014b50 PK#1#2
MadeBy: Word; // Version number, 20
FileInfo: TFileInfo;
CommentSize: Word; // 0
FirstDiskNumber: Word; // 0
IntFileAttr: Word; // 0 = binary; 1 = text
ExtFileAttr: Longword; // DOS file attributes (Archived=32)
LocalFileHeaderHeadOff: Longword; // #TLocalFileHeader
end;
PFileHeader = ^TFileHeader;
TLocalFileHeader = packed record
Signature: Longword; // $02014b50 PK#3#4
FileInfo: TFileInfo;
end;
PLocalFileHeader = ^TLocalFileHeader;
TLastHeader = packed record
Signature: Longword; // $02014b50 PK#5#6
ThisDiskNumber: Word;
CentralDirDisk: Word;
ThisDiskFileCount: Word;
TotalFileCount: Word;
FileHeaderSize: Longword;
FileHeaderOffset: Longword;
CommentSize: Word;
end;
PLastHeader = ^TLastHeader;
const
MagicLastHeader = $06054b50;
MagicLocalHeader = $04034b50;
MagicFileHeader = $02014b50;
type
IntPtr = Longword; // NativeInt on Delphi2007 is an Int64 ??
{$if CompilerVersion < 19}
procedure SetAnsiString( var S: AnsiString; P: PAnsiChar; L: Integer ); inline;
begin
SetString( S, P, L );
end;
{$ifend}
{ TZipFileReader }
constructor TZipFileReader.Create( const FileName: string; ZipStartOffset: Int64; Size: Longword );
begin
// Open the file in question.
FFileHandle := CreateFile( PChar( FileName ), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0 );
if FFileHandle = INVALID_HANDLE_VALUE then raise EZipException.CreateFmt( cCannotOpenFile, [ Filename, GetLastError() ] );
if Size = 0 then Size := GetFileSize( FFileHandle, nil );
if Size = INVALID_FILE_SIZE then raise EZipException.CreateFmt( cCannotGetFileSize, [ Filename, GetLastError() ] );
try
// Create a file mapping of the file in question
FFileMapping := CreateFileMapping( FFileHandle, nil, PAGE_READONLY, 0, 0, nil);
if FFileMapping = 0 then raise EZipException.CreateFmt( cCannotMapFile, [ Filename, GetLastError() ] );
try
// Get the file mapped in memory (NOTE: The offset needs to be on the memory allocation granularity of the system)
// Hence we assign it it's own pointer -> todo rounding etc.
FMappedAddress := MapViewOfFile( FFileMapping, FILE_MAP_READ, Int64Rec( ZipStartOffset ).Hi, Int64Rec( ZipStartOffset ).Lo, Size );
if not Assigned( FMappedAddress ) then EZipException.CreateFmt( cCannotMapFile, [ Filename, GetLastError() ] );
Create( FMappedAddress, Size );
except
CloseHandle( FFileMapping );
FFileMapping := 0;
raise;
end;
except
CloseHandle( FFileHandle );
FFileHandle := 0;
raise;
end;
end;
constructor TZipFileReader.Create( const ResourceName, ResourceType: string; Instance: HMODULE );
var
Resource: HRSRC;
Global: HGLOBAL;
begin
Resource := FindResource( Instance, PChar( ResourceName ), PChar( ResourceType ) );
if Resource = 0 then raise EZipException.CreateFmt( cResourceNotFound, [ ResourceName, ResourceType ] );
Global := LoadResource( Instance, Resource );
if Global = 0 then raise EZipException.CreateFmt( cResourceNotLoaded, [ ResourceName, ResourceType ] );
Create( LockResource( Global ), SizeofResource( HInstance, Resource ) );
// Note: kb57808: SizeofResource() returns the resource size rounded up to the alignment size.
end;
constructor TZipFileReader.Create( Buffer: Pointer; Size: Longword );
var
LastHeader: PLastHeader;
FileHeader: PFileHeader;
i, Off: Longword;
Name: AnsiString;
begin
// Note the location.
FStart := Buffer;
FSize := Size;
// Some sanity checks.
if FSize < sizeof( TLocalFileHeader ) + sizeof( TFileHeader ) + sizeof( TLastHeader ) then raise EZipException.Create( cZipFileTooSmall );
if IsBadReadPtr( Buffer, Size ) then raise EZipException.Create( cZipBufferInvalid );
if PLongword( Buffer )^ <> MagicLocalHeader then raise EZipException.Create( cZipFileFormatError );
// Find the last header. Due to the alignment of SizeofResource, we need o search for it.
LastHeader := Pointer( IntPtr( Buffer ) + Size - sizeof( TLastHeader ) );
for i := 0 to 31 do begin
if LastHeader^.Signature = MagicLastHeader then Break;
Dec( IntPtr( LastHeader ) );
end;
if LastHeader^.Signature <> MagicLastHeader then raise EZipException.Create( cZipFileFormatError );
FFilenames := TStringList.Create();
Off := LastHeader^.FileHeaderOffset;
for i := 0 to LastHeader^.TotalFileCount - 1 do begin
// Get header
if Off + sizeof( TFileHeader ) >= Size then raise EZipException.Create( cZipFileFormatError );
FileHeader := Pointer( IntPtr( Buffer ) + Off );
Inc( Off, sizeof( TFileHeader ) );
if FileHeader^.Signature <> MagicFileHeader then raise EZipException.Create( cZipFileFormatError );
// Get filename
if Off + FileHeader^.FileInfo.NameSize + FileHeader^.FileInfo.ExtraSize >= Size then raise EZipException.Create( cZipFileFormatError );
SetAnsiString( Name, Pointer( IntPtr( Buffer ) + Off ), FileHeader^.FileInfo.NameSize );
Inc( Off, FileHeader^.FileInfo.NameSize + FileHeader^.FileInfo.ExtraSize );
// Save filename and offset into ZIPfile where it can be found.
FFileNames.AddObject( Name, Pointer( FileHeader^.LocalFileHeaderHeadOff ) );
end;
// For quick access.
TStringList( FFilenames ).Sorted := True;
end;
destructor TZipFileReader.Destroy;
begin
if Assigned( FMappedAddress ) then UnmapViewOfFile( FMappedAddress );
if FFileMapping <> 0 then CloseHandle( FFileMapping );
if FFileHandle <> 0 then CloseHandle( FFileHandle );
inherited Destroy;
end;
function TZipFileReader.GetFile( const FileName: string ): TBytes;
var
ID: Integer;
begin
// Convert filename in FileID and access by ID.
ID := FFilenames.IndexOf( FileName );
if ID < 0 then raise EZipException.CreateFmt( cFileNotFoundInZip, [ FileName ] );
Result := GetFile( ID );
end;
function TZipFileReader.GetFile( FileID: Integer ): TBytes;
var
Off: Longword;
Local: PLocalFileHeader;
ZRec: TZStreamRec;
const
ZLibHeader: array [ 0..1 ] of Byte = ( $78, $01 ); // Deflate 32KB window size no preset dictionary.
begin
// Sanity check
if ( FileID < 0 ) or ( FileID >= FFilenames.Count ) then raise EZipException.CreateFmt( 'Invalid File ID: %d', [ FileID ] );
// Get the file header and perform sanity check
Off := Longword( FFilenames.Objects[ FileID ] );
if Off + sizeof( TLocalFileHeader ) >= FSize then raise EZipException.Create( cZipFileFormatError );
Local := Pointer( IntPtr( FStart ) + Off );
if Local^.Signature <> MagicLocalHeader then raise EZipException.Create( cZipFileFormatError );
Inc( Off, sizeof( TLocalFileHeader ) + Local^.FileInfo.NameSize + Local^.FileInfo.ExtraSize );
if Off + Local^.FileInfo.CompressedSize >= FSize then raise EZipException.Create( cZipFileFormatError );
// note: should we check the name again?
SetLength( Result, Local^.FileInfo.UncompressedSize );
if Length( Result ) > 0 then case Local^.FileInfo.ZipMethod of
0: begin // STORED
if Local^.FileInfo.CompressedSize <> Local^.FileInfo.UncompressedSize then raise EZipException.Create( cZipFileFormatError );
Move( Pointer( IntPtr( FStart ) + Off )^, Result[ 0 ], Local^.FileInfo.UncompressedSize );
end;
8: begin // DEFLATE
ZeroMemory( #ZRec, sizeof( ZRec ) );
ZRec.next_in := #ZLibHeader;
ZRec.avail_in := sizeof( ZLibHeader );
ZRec.total_in := sizeof( ZLibHeader ) + Local^.FileInfo.CompressedSize;
ZRec.next_out := #Result[ 0 ];
ZRec.avail_out := Local^.FileInfo.UncompressedSize;
ZRec.total_out := Local^.FileInfo.UncompressedSize;
ZRec.zalloc := zlibAllocMem;
ZRec.zfree := zlibFreeMem;
if inflateInit_( ZRec, zlib_Version, sizeof( ZRec ) ) <> 0 then raise EZipException.Create( cZipFileFormatError );
try
if not( inflate( ZRec, Z_FULL_FLUSH ) in [ Z_OK, Z_STREAM_END ] ) then raise EZipException.Create( cZipFileFormatError );
ZRec.next_in := Pointer( IntPtr( FStart ) + Off );
ZRec.avail_in := Local^.FileInfo.CompressedSize;
if not( inflate( ZRec, Z_FINISH ) in [ Z_OK, Z_STREAM_END ] ) then raise EZipException.Create( cZipFileFormatError );
finally
inflateEnd( ZRec );
end;
end;
else raise EZipException.CreateFmt( cUnsupportedMethod, [ Local^.FileInfo.ZipMethod ] );
end;
// todo: CRC32 sanity check if requested.
end;
function TZipFileReader.GetZipFileInfo( const FileName: AnsiString ): TZipFileInfo;
var
FileID: Integer;
Off: Longword;
Local: PLocalFileHeader;
begin
// Get the correct file ID
FileID := FFilenames.IndexOf( FileName );
if FileID < 0 then raise EZipException.CreateFmt( cFileNotFoundInZip, [ FileName ] );
// Get the file header and perform sanity check
Off := Longword( FFilenames.Objects[ FileID ] );
if Off + sizeof( TLocalFileHeader ) >= FSize then raise EZipException.Create( cZipFileFormatError );
Local := Pointer( IntPtr( FStart ) + Off );
if Local^.Signature <> MagicLocalHeader then raise EZipException.Create( cZipFileFormatError );
Inc( Off, sizeof( TLocalFileHeader ) + Local^.FileInfo.NameSize + Local^.FileInfo.ExtraSize );
if Off + Local^.FileInfo.CompressedSize >= FSize then raise EZipException.Create( cZipFileFormatError );
// Return requested data.
Result.LastModified := Local^.FileInfo.LastModified;
Result.Crc32 := Local^.FileInfo.Crc32;
Result.CompressedSize := Local^.FileInfo.CompressedSize;
Result.UncompressedSize := Local^.FileInfo.UncompressedSize;
end;
end.
Take a look at this OpenSource SynZip unit. It's even faster for decompression than the default unit shipped with Delphi, and it will generate a smaller exe (crc tables are created at startup).
No external dll is needed.
I just made some changes to handle Unicode file names inside Zip content, not only Win-Ansi charset but any Unicode chars. Feedback is welcome.
I like the WinZip compatible TZipMaster for Delphi, available here: http://www.delphizip.org/
TZipMaster is a non-visual VCL wrapper
created by ChrisVleghert and
EricW.Engler for their freeware Zip
and Unzip DLLs.
Those DLLs are based on the InfoZip
Official Freeware Zip/Unzip source
code, but are NOT equivalent to
InfoZip's DLLs. The InfoZip source
code has been modified to enhance
their ease-of-use, power, and
flexibility for use with Delphi and
C++ Builder.
Also, this question has been covered before on Stack Overflow, which may yield some other solutions for you.
If distributing an ActiveX DLL with your project is not a problem for you, then Chilkat Zip (http://www.chilkatsoft.com/zip-activex.asp) seems to do the trick. Delphi examples are here: http://www.example-code.com/delphi/zip.asp
DotNetZip is a managed code (.NET) library, that exposes a COM interface.
Free.
Open source
MS-PL licensed.
Related
When I run the prog below, the result value of the stgOpenStorage is STG_E_SHAREVIOLATION. How should I close the IStorage to get it unlocked?
procedure TForm1.btnSaveClick(Sender: TObject);
var
fileName : string;
streamName : string;
procedure storeTextIntoStorageStream( text_ : string );
var
documentStorage : IStorage;
levelIStream : IStream;
i, j : integer;
begin
if ( fileExists( fileName ) ) then
deleteFile( fileName );
stgCreateDocfile( #fileName[1], STGM_WRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT or STGM_CREATE, 0, documentStorage );
try
documentStorage.CreateStream( #streamName[1], STGM_WRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, 0, levelIStream );
try
i := length( text_ );
levelIStream.write( #i, sizeOf( integer ), #j );
levelIStream.write( #text_[1], i*sizeOf( char ), #j );
finally
levelIStream.Commit( 0 );
levelIStream := NIL;
end;
finally
documentStorage.Commit( 0 );
documentStorage := NIL;
end;
end;
function readTextFromStorageStream : string;
var
documentStorage : IStorage;
levelIStream : IStream;
i, j : integer;
begin
i := stgOpenStorage( #fileName[1], NIL, STGM_READ or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, NIL, 0, documentStorage );
try
documentStorage.OpenStream( #streamName[1], NIL, STGM_READ or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, levelIStream );
try
levelIStream.read( #i, sizeOf( integer ), #j );
setLength( result, i );
levelIStream.read( #result[1], i*sizeOf( char ), #j );
finally
levelIStream := NIL;
end;
finally
documentStorage := NIL;
end;
end;
begin
fileName := 'c:\temp\test.stg';
streamName := 'Stream-0';
storeTextIntoStorageStream( memo1.Lines.DelimitedText );
memo1.Lines.DelimitedText := readTextFromStorageStream;
end;
And how could I set the IStorage/IStream default size / size step? Because my test 1.6K byte content stored in 16K.
There are two IStorage implementations in the Delphi source libraries.
WinApi.OLE2 and WinApi.ActiveX. Which one do you use? In the WinApi.OLE2 unit, the IStorage and IStream are CLASSES, they are not INTERFACES. If you use this unit, the interface garbage collection and so the automatic closing does not work on the variables. If you use the WinApi.ActiveX unit, the example will work just fine.
The code looks fine 1, so I have a feeling that the problem is due to your use of STGM_SHARE_EXCLUSIVE. The file is in use at the time you are trying to open it, so I'm betting your OS/AV is the one keeping the file open (ie, to scan its content), not the interfaces in storeTextIntoStorageStream(), which are long gone by the time readTextFromStorageStream() is entered.
1: well, aside from the lack of adequate error handling. And the redundant nil'ing of interface variables. And, consider replacing your string indexes with PChar() casts instead.
In readTextFromStorageStream(), try replacing STGM_SHARE_EXCLUSIVE (which makes sense for a writer, but not a reader) with STGM_SHARE_DENY_WRITE instead and see if the error goes away:
procedure TForm1.btnSaveClick(Sender: TObject);
var
fileName : string;
streamName : string;
procedure storeTextIntoStorageStream( const text_ : string );
var
documentStorage : IStorage;
levelIStream : IStream;
i, j : integer;
begin
if ( FileExists( fileName ) ) then
DeleteFile( fileName );
OleCheck( StgCreateDocFile( PChar(fileName), STGM_WRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT or STGM_CREATE, 0, documentStorage ));
try
OleCheck( documentStorage.CreateStream( PChar(streamName), STGM_WRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, 0, levelIStream ) );
try
i := Length( text_ );
OleCheck( levelIStream.Write( #i, SizeOf( i ), #j ) );
if ( i > 0 ) then
OleCheck( levelIStream.Write( PChar(text_), i * SizeOf( Char ), #j ) );
finally
levelIStream.Commit( 0 );
end;
finally
documentStorage.Commit( 0 );
end;
end;
function readTextFromStorageStream : string;
var
documentStorage : IStorage;
levelIStream : IStream;
i, j : integer;
begin
Result := '';
OleCheck( StgOpenStorage( PChar(fileName), nil, STGM_READ or STGM_SHARE_DENY_WRITE or STGM_DIRECT, NIL, 0, documentStorage ) );
OleCheck( documentStorage.OpenStream( PChar(streamName), nil, STGM_READ or STGM_SHARE_DENY_WRITE or STGM_DIRECT, 0, levelIStream ) );
OleCheck( levelIStream.Read( #i, SizeOf( i ), #j ) );
if ( i > 0 ) then
begin
SetLength( Result, i );
OleCheck( levelIStream.Read( PChar(Result), i * SizeOf( Char ), #j ) );
end;
end;
begin
fileName := 'c:\temp\test.stg';
streamName := 'Stream-0';
storeTextIntoStorageStream( Memo1.Lines.DelimitedText );
Memo1.Lines.DelimitedText := readTextFromStorageStream;
end;
I am working with a DLL from RFIdeas and the documentation indicates the usage for the DLL function is:
short GetActiveID32(BYTE *pBuf, short wBufMaxSz)
They don't have Delphi examples, but do have VB.NET, C#, and others. Here is the excerpt for C#:
public static void getActiveID()
{
pcproxlib.SetDevTypeSrch(PRXDEVTYP_USB);
int rc = pcproxlib.usbConnect();
Thread.Sleep(2500);
if (rc == 1)
{
IntPtr result1 = Marshal.AllocHGlobal(32 * sizeof(int));
byte[] arr = new byte[32];
int nBits = pcproxlib.GetActiveID32(result1, 32);
if(nBits == 0)
{
Console.WriteLine("\nNo Id Found, Please put card on the reader and make sure it must be configured with the card placed on it");
return;
}
int Bytes = (nBits + 7) / 8;
if (Bytes < 8)
{
Bytes = 8;
}
Marshal.Copy(result1, arr, 0, 32);
String cardData = "";
for (int i = 0; i < Bytes; i++)
{
String data = String.Format("{0:X2} ", arr[i]);
cardData = data + cardData;
}
Console.WriteLine("\n" + nBits+"Bits" + ": " + cardData);
}
else
{
Console.WriteLine("\n Reader Not Connected");
}
}
I have the following function defined in Delphi.
function GetActiveID32( CardID : pInt; Max_Buffer_Size : integer ) : word; stdcall external 'pcProxAPI32.dll';
I call the function like so:
// Connect to USB Reader
SetConnectProduct( PRODUCT_PCPROX );
SetDevTypeSrch( PRXDEVTYP_USB );
USBDisconnect;
try
DeviceID := 0;
Results := GetDevCnt;
Results := USBConnect( );
if Results <> 0 then
begin
// Get Active USB reader ID
//CardID := 0;
repeat
Sleep( 250 );
Results := GetActiveID32( #CardID , 32 );
BytesToProcess := ( Results + 7) / 8;
if ( BytesToProcess < 8 ) then
BytesToProcess := 8;
// StrLCopy( BytesArray, PInt( CardID ), 32 );
// Marshal.Copy(result1, arr, 0, 32);
CardData := '';
for I := 0 to Trunc( BytesToProcess ) do
begin
CardData := CardData + BytesArray[ i ];
end;
until Results > 0;
// Disconnect from USB Reader until next needed
USBDisconnect;
end;
except
on E : Exception do
begin
results := GetLastLibErr;
ShowMessage( 'Error reading card ' + Results.ToString);
end;
end;
lblCardValue.Caption := String( #CardID );
The caption is always garbage. What am I doing wrong? I think it has something to do with my misuse of pointers.
We finally have a resolution. Below is the code that works. Hopefully this helps someone in the future.
//function USBConnect( DeviceID : integer ) : SmallInt; external 'pcProxAPI32.dll';
function USBConnect( DeviceID : integer ) : word; stdcall external 'pcProxAPI32.dll';
// short GetActiveID32(BYTE *pBuf, short wBufMaxSz)
function GetActiveID32( CardID : PByte; Max_Buffer_Size : word ) : word; stdcall external 'pcProxAPI32.dll';
// BSHRT USBDisconnect(void)
function USBDisconnect : word; stdcall external 'pcProxAPI32.dll';
var
frmMain : TfrmMain;
DeviceID : integer;
Results : integer;
CardInfo : ^Byte;
const
PRODUCT_PCPROX : integer = 1;
PRXDEVTYP_USB : integer = 0;
procedure TfrmMain.btnReadCardClick(Sender: TObject);
var
CardData : string;
begin
ShowMessage( 'Place card on reader' );
// Connect to USB Reader
SetConnectProduct( PRODUCT_PCPROX );
SetDevTypeSrch( PRXDEVTYP_USB );
USBDisconnect;
try
DeviceID := 0;
Results := GetDevCnt; //Only one device should be on system
Results := USBConnect( DeviceID ); //Connect to the only device on system
if Results <> 0 then
begin
repeat
Sleep( 250 ); //Requried 250ms delay between reads
Results := GetActiveID32( #CardInfo, 8 );
until Results > 0;
USBDisconnect; // Disconnect from USB Reader until needed next
CardData := IntToHex( Integer( Pointer( CardInfo ) ), 8 );
end
else
ShowMessage( 'Unable to connect to reader' );
except
on E : Exception do
begin
results := GetLastLibErr;
ShowMessage( 'Error reading card ' + Results.ToString);
end;
end;
lblCardValue.Caption := CardData; // Display data from card
end;
This is the code that actually resolved the issue.
CardData := IntToHex( Integer( Pointer( CardInfo ) ), 8 );
I was trying out below code that should save clipboard text to a text file in Delphi XE6. The code runs fine but generates only junk values in the output file, even when the clipboard contains a copied text fragment. How can the code be changed to work properly?
function SaveClipboardTextDataToFile(
sFileTo : string ) : boolean;
var
ps1,
ps2 : PChar;
dwLen : DWord;
tf : TextFile;
hData : THandle;
begin
Result := False;
with Clipboard do
begin
try
Open;
if( HasFormat( CF_TEXT ) ) then
begin
hData :=
GetClipboardData( CF_TEXT );
ps1 := GlobalLock( hData );
dwLen := GlobalSize( hData );
ps2 := StrAlloc( 1 + dwLen );
StrLCopy( ps2, ps1, dwLen );
GlobalUnlock( hData );
AssignFile( tf, sFileTo );
ReWrite( tf );
Write( tf, ps2 );
CloseFile( tf );
StrDispose( ps2 );
Result := True;
end;
finally
Close;
end;
end;
end;
You see junk because CF_TEXT is ANSI. You request ANSI text, the OS converts the clipboard contents to ANSI, and you put it in unicode string. Use CF_UNICODETEXT for unicode applications.
Also consider the points raised in the comments to the question.
If you have Delphi XE6 then you can use some of the already implemented features
uses
System.SysUtils,
System.IOUtils,
Vcl.Clipbrd;
function SaveClipboardTextDataToFile( const sFileTo : string ) : boolean;
var
LClipboard : TClipboard;
LContent : string;
begin
// get the clipboard content as text
LClipboard := TClipboard.Create;
try
LContent := LClipboard.AsText;
finally
LClipboard.Free;
end;
// save the text - if any - into a file
if not LContent.IsEmpty
then
begin
TFile.WriteAllText( sFileTo, LContent );
Exit( True );
end;
Result := False;
end;
Is there a method to determine if an exe file has been compressed with UPX?
The function to determine if an exe file has been compressed is excellent except I found a problem with the code. If the function IsUPXCompressed is called then you try to run upx, upx can not save the file it modifies. There is something not sharing rights correctly in the function. I have tested this for several hours. If I do not call the method then UPX can write the files with no problem. You you call it then try to run UPX it will not save the file. UPX reports an IOException Permission denied error when trying to write the file.
Can anyone spot something wrong in the code that would cause this problem?
Thank-you
The function to determine if an exe file has been compressed is excellent except I found a problem with the code. If the function IsUPXCompressed is called then you try to run upx, upx can not save the file it modifies. There is something not sharing rights correctly in the function. I have tested this for several hours. If I do not call the method then UPX can write the files with no problem. You you call it then try to run UPX it will not save the file. UPX reports an IOException Permission denied error when trying to write the file.
Can anyone spot something wrong in the code that would cause this problem?
Thank-you
Another Method, when a exe is packed with the UPX tool, the section of the PE header contains sections called UPX0,UPX1, etc. so if read these sections and compare the name with the string UPX you can determine if the exe was compressed using the UPX packer.
check this function
uses
Windows;
function IsUPXCompressed(const Filename:TFileName): Boolean;
var
i : integer;
pBaseAddress : PByte;
pDosHeader : PImageDosHeader;
pNtHeaders : PImageNtHeaders;
hFile : Cardinal;
hFileMap : Cardinal;
pSectionHeader: PImageSectionHeader;
dwOffset : Cardinal;
SectName : AnsiString;
begin
Result:=False;
hFile := CreateFile(PChar(Filename), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile = INVALID_HANDLE_VALUE) then Exit;
hFileMap := CreateFileMapping(hFile, nil, PAGE_READONLY or SEC_IMAGE, 0, 0, nil);
if (hFileMap = 0) then
begin
CloseHandle(hFile);
Exit;
end;
pBaseAddress := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
if (pBaseAddress = nil) then
begin
CloseHandle(hFileMap);
CloseHandle(hFile);
Exit;
end;
try
dwOffset := Cardinal(pBaseAddress);
pDosHeader := PImageDosHeader(pBaseAddress);
pNtHeaders := PImageNtHeaders(dwOffset + Cardinal(pDosHeader._lfanew));
pSectionHeader := pImageSectionHeader(Cardinal(pNtHeaders) + SizeOf(TImageNtHeaders));
for i := 0 to pNtHeaders.FileHeader.NumberOfSections-1 do
begin
SetString(SectName, PAnsiChar(#pSectionHeader.Name), SizeOf(pSectionHeader.Name));
Result:=Pos('UPX',SectName)>0;
If Result then break;
Inc(pSectionHeader);
end;
finally
UnmapViewOfFile(pBaseAddress);
CloseHandle(hFileMap);
CloseHandle(hFile);
end;
end;
UPX itself does it like this:
if (memcmp(isection[0].name,"UPX",3) == 0)
throwAlreadyPackedByUPX();
This is the implementation for 32-bit PEs; 64-bit PEs need different offsets,
and other executable formats have to be handled separately.
#include <stdio.h>
typedef unsigned int uint;
uint peek_d( FILE* f, uint offs ) {
fseek( f, offs, SEEK_SET );
uint a = 0;
fread( &a, 1,sizeof(a), f );
return a;
}
int main( int argc, char** argv ) {
FILE* f = fopen( argv[1], "rb" ); if( f==0 ) return 1;
uint p,n,x,y;
p = peek_d( f, 0x3C ); // PE header offset
n = peek_d( f, p+0x74 ); // pointer table size
x = p + 0x78 + n*8;
y = peek_d( f, x+0*0x28+0 ); // 1st section name
if( (y&0xFFFFFF) == ('U'+('P'<<8)+('X'<<16)) ) {
printf( "UPX detected!\n" );
} else {
printf( "No UPX!\n" );
}
return 0;
}
try to uncompress it with upx?
// Returns IsUPXCompressed - Modified for Delphi 2010
function IsUPXCompressed( const Filename: TFileName ): Boolean;
var
i: integer;
pBaseAddress: PByte;
pDosHeader: PImageDosHeader;
pNtHeaders: PImageNtHeaders;
hFile: Cardinal;
hFileMap: Cardinal;
pSectionHeader: PImageSectionHeader;
dwOffset: Cardinal;
SectName: AnsiString;
begin
Result := False;
hFile := CreateFile( PChar( Filename ), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
if ( hFile = INVALID_HANDLE_VALUE ) then
Exit;
hFileMap := CreateFileMapping( hFile, nil, PAGE_READONLY or SEC_IMAGE, 0, 0, nil );
if ( hFileMap = 0 ) then
begin
CloseHandle( hFile );
Exit;
end;
pBaseAddress := MapViewOfFile( hFileMap, FILE_MAP_READ, 0, 0, 0 );
if ( pBaseAddress = nil ) then
begin
CloseHandle( hFileMap );
CloseHandle( hFile );
Exit;
end;
dwOffset := Cardinal( pBaseAddress );
pDosHeader := PImageDosHeader( pBaseAddress );
pNtHeaders := PImageNtHeaders( dwOffset + Cardinal( pDosHeader._lfanew ) );
pSectionHeader := pImageSectionHeader( Cardinal( pNtHeaders ) + SizeOf( TImageNtHeaders ) );
for i := 0 to pNtHeaders.FileHeader.NumberOfSections - 1 do
begin
SetString( SectName, PAnsiChar( #pSectionHeader.name ), SizeOf( pSectionHeader.name ) );
if Pos( 'UPX', SectName ) > 0 then
begin
Result := True;
exit;
end;
Inc( pSectionHeader );
end;
end;
Thanks Rob for the pointers.
The section names are not included the UPX word always. It mabebe contain another name changed by user. For certain. Ypu must search for UPX copmpressor signature in the whole file.
Below, I inserted a code written by Ray Konopka (part of the Coderage presentation). I am planning to use it, however, I am not sure how to clean (on the fly) multiple objects.
All my attempts were unsucesfull and rendered memory leak.
Any thoughts are appreciated.
Thanks,
program stringlistDictionary;
{$APPTYPE CONSOLE}
uses
Classes,
SysUtils;
type
TPlayer = class
public
Name: string;
Position: string;
Hits: Integer;
AtBats: Integer;
constructor Create( Name, Position: string );
end;
constructor TPlayer.Create( Name, Position: string );
begin
inherited Create;
Self.Name := Name;
Self.Position := Position;
Hits := 0;
AtBats := 0;
end;
var
Team: TStringList;
Player, NewPlayer: TPlayer;
I: Integer;
function FindPlayer( const Name: string ): TPlayer;
var
Idx: Integer;
begin
Result := nil;
if Team.Find( Name, Idx ) then
Result := TPlayer( Team.Objects[ Idx ] );
end;
begin {== Main ==}
Writeln( 'StringList Dictionary' );
Writeln( '---------------------' );
Writeln;
Team := TStringList.Create;
try
NewPlayer := TPlayer.Create( 'Aramis Ramerez', 'Third Base' );
NewPlayer.Hits := 120;
NewPlayer.AtBats := 350;
Team.AddObject( NewPlayer.Name, NewPlayer );
NewPlayer := TPlayer.Create( 'Derrick Lee', 'First Base' );
NewPlayer.Hits := 143;
NewPlayer.AtBats := 329;
Team.AddObject( NewPlayer.Name, NewPlayer );
NewPlayer := TPlayer.Create( 'Ryan Theriot', 'Short Stop' );
NewPlayer.Hits := 87;
NewPlayer.AtBats := 203;
Team.AddObject( NewPlayer.Name, NewPlayer );
Player := FindPlayer( 'Derrick Lee' );
if Player <> nil then
Writeln( 'Player Found: ', Player.Name, ', ', Player.Position )
else
Writeln( 'Player not found.' );
Writeln;
Writeln( 'Active Roster' );
Writeln( '-------------' );
for I := 0 to Team.Count - 1 do
Writeln( TPlayer( Team.Objects[ I ] ).Name, #9,
TPlayer( Team.Objects[ I ] ).Position );
Readln;
finally
//!! Need to free the players.
Team.Free;
end;
end.
With Delphi 2009, the TStringList constructor has an optional boolean parameter "OwnsObjects". If you set that to true, the objects are freed automatically.
Else you can do the following:
for i := Team.Count-1 downto 0 do begin
Team.Objects.Free;
end;
Team.Free;
And by the way, public fields are discouraged. You beter use properties so you can control what access is possible to the fields. And you can add setter functions to validate the input.
type
TPlayer = class
private
FName : string;
FPosition : string;
FHits : Integer;
FAtBats : Integer;
public
constructor Create(const AName, APosition: string );
property Name: string read FName;
property Position: string read FPosition;
property Hits: Integer read FHits write FHits;
property AtBats: Integer read FAtBats write FAtBats;
end;
Kinda obvious, but still - you don't have to write 'for ... Free' code every time you want to clear TStringList objects. You can put it into a global function.
procedure FreeObjects(sl: TStringList);
var
i: integer;
begin
for i := 0 to sl.Count - 1 do
sl.Objects[i].Free;
end;
FreeObjects(Team);
Or you can put it into a TStringList helper.
TStringListHelper = class helper for TStringList
public
procedure FreeObjects;
end;
procedure TStringListHelper.FreeObjects;
var
i: integer;
begin
for i := 0 to Count - 1 do
Objects[i].Free;
end;
Team.FreeObjects;
just a clarification about gamecat answer: I don't know about delphi 2009 but usually the Objects property need an index, and you don't really need a reverse cycle, so:
for i := 0 to Team.Count-1 do
Team.Objects[i].Free;
Team.Free;
or:
while Team.Count > 0 do
begin
Team.Objects[0].Free;
Team.Delete(0);
end;
Team.Free;
Using D7, I can just subclass TStingList