Delphi using DLL Pointer Parameter - delphi

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 );

Related

How to extract local computers Site Local and Link Local IPv6 address in Delphi?

I am currently trying to develop an application for displaying IPv6 traffic. I am confused as to how to extract the site local and the link local addresses of the local machine . Any guidance would be appreciated.
The structure of the IPv6 packet in delphi i am using is :
type
PIPV6HeaderPtr = ^TIPV6Header;
TIPV6Header = packed record
ip6_flow : DWORD; // 4 bits = version #,// 8 bits = Trafic class,// 20 bits = flow label
ip6_len : Word;//Cardinal; // Payload length
//ip6_next : Cardinal; // Next Header
ip6_next : Byte; // Next Header
ip6_hops : Byte; // Hop Limit
h_source : IN6_ADDR;
h_dest : IN6_ADDR;
end;
//
// IPv6 extension header format
//
type
PIPV6EXTPTR = ^TIPV6EXT;
TIPV6EXT = packed record
ip6_next : Byte;
ip6_len : Byte;
ip6_data : array[0..1] of Byte;
End;
type
PIPV6EXT_FRAGPTR = ^TIPV6EXT_FRAG;
TIPV6EXT_FRAG = packed record
ip6_next : Byte;
ip6_reserved : Byte;
ip6_offlg : Word;
ip6_ident : DWORD;
End;
Thanks in advance.
See the following explanation of what site local addresses and link local addresses are and how they work:
IPv6 tutorial – Part 6: Site-local addresses and link-local addresses
You can use the Win32 API GetAdapterAddresses() function to get the local machine's IPv6 addresses.
I have found another method to get the addresses using WSAIoctl . I am adding my code below .
uses WinSock2; // Widely available on Net
function TForm1.GetIPUsingIoctlMethod(var sInt: string): Boolean;
var
s: TSocket;
wsaD: TWSADATA;
NumInterfaces: Integer;
BytesReturned, SetFlags: u_long;
pAddrInet: SOCKADDR_IN;
pAddrString: PCHAR;
PtrA: pointer;
Buffer: Array[0..20] Of INTERFACE_INFO_EX;
i: Integer;
Local_IpList : TStringList ;
addrList : LPSOCKET_ADDRESS_LIST ;
in6 : PSockAddr;
protoInfo : WSAProtocol_Info;
text : Array[1..46] Of Char;
Buffer1 : DWORD;
Str : String;
begin
result := False; // Initialize
sInt := '';
Try
WSAStartup($0101, wsaD); // Start WinSock
// You should normally check
// for errors here :)
{Create a WSA Socket}
//s := WSASocketA(AF_INET6,SOCK_STREAM, IPPROTO_IP, nil, 0, 0);
s:= Socket(AF_INET6,SOCK_STREAM,IPPROTO_IP);
//s := Socket(AF_INET6, SOCK_STREAM, 0); // Open a socket
if (s = INVALID_SOCKET) then exit;
try // Call WSAIoCtl
PtrA := #bytesReturned;
if (WSAIoCtl(s, SIO_ADDRESS_LIST_QUERY, nil, 0, #Buffer, SizeOf(Buffer), PtrA, nil, nil)<> SOCKET_ERROR)
then
begin // If ok, find out how
// many interfaces exist
Result := True;
addrList := LPSOCKET_ADDRESS_LIST(#Buffer);
DebugLog(' i = ' + IntToStr(addrList.iAddressCount),0);
// NumInterfaces := BytesReturned div SizeOf(INTERFACE_INFO);
Local_IpList := TStringList.Create ;
for i := 0 to addrList.iAddressCount - 1 do // For every interface
begin
If ( addrList.Address[i].lpSockaddr.sin_family = AF_Inet6) Then
Begin
in6 := PSockaddr(#addrList.Address[i].lpSockaddr);
Buffer1 := SizeOf(Text);
//protoInfo.
FillChar(Text,SizeOf(Text),#0);
If WSAAddressToString(addrList.Address[i].lpSockaddr,addrList.Address[i].iSockaddrLength,
nil,#text,Buffer1) <> 0 Then
Begin
debuglog('err1 = ' + SysErrorMessage(WSAGetLastError),0);
end;
Str := Text;
DebugLog(' Addr cnt =' + IntToStr(i) + ' = ' + Str,0);
Buffer1 := 0;
FillChar(Text,SizeOf(Text),#0);
//in6.
//DebugLog('ip = ' + );
//IN6_IS_ADDR_LINKLOCAL
end;
end;
end
Else
sInt := SysErrorMessage(WSAGetLastError);
except
end;
//
// Close sockets
//
CloseSocket(s);
WSACleanUp;
Except
End;
end;
debugLog is simply a method to write in a text file.

128 bit operation with delphi

I need to convert a hexadecimal value to a decimal integer. Is there some unit that can do this?
On the web I have found something about it but it is not helping me much. I understood that using inline asm it is possible to represent it as a packed array of four 32 bit Integer. I have found as convert a int32 or int64 to int128 and viceversa, but not found nothing for take for example two int64 and to do a int128 and too i have found issue searching in asm inline something that emule div operator, mul operator and sum operator.
So I ask if someone can help me to solve this problem. My objective is to take a string in hexadecimal and convert it in decimal and after that calculate the equivalent value in base 35 (0..9, A..Z).
Thanks very much.
If you convert the hexadecimal string into an array of bytes (see SysUtils for that) you can use the follewing code to convert it into base 35:
function EncodeBaseX( const Values: array of Byte; var Dest: array of Byte; Radix: Integer ): Boolean;
var
i,j,Carry: Integer;
begin
// We're unsuccesful up to now
Result := False;
// Check if we have an output buffer and clear it
if Length( Dest ) = 0 then Exit;
System.FillChar( Dest[ 0 ], Length( Dest ), 0 );
// fill in the details
for i := 0 to High( Values ) do begin
Carry := Values[ i ];
for j := 0 to High( Dest ) do begin
Inc( Carry, Radix * Dest[ j ] );
Dest[ j ] := Carry and $ff;
Carry := Carry shr 8;
end;
if Carry <> 0 then Exit; // overflow
end;
// We're succesful
Result := True;
end;
{Bytes: array of byte (0..255); Dest: array of Byte(0..Radix-1)}
function DecodeBaseX( const Bytes: array of Byte; var Dest: array of Byte; Radix: Integer ): Boolean;
var
i,j,Carry: Integer;
B: array of Byte;
begin
// We're unsuccesful up to now
Result := False;
// Copy data
if Length( Bytes ) = 0 then Exit;
SetLength( B, Length( Bytes ) );
System.Move( Bytes[ 0 ], B[ 0 ], Length( B ) );
// fill in the details
for i := High( Dest ) downto 0 do begin
Carry := 0;
for j := High( Bytes ) downto 0 do begin
Carry := Carry shl 8 + B[ j ];
B[ j ] := Carry div Radix; Carry := Carry mod Radix;
end;
Dest[ i ] := Carry;
end;
// Check if we collected all the bits
Carry := 0;
for i := 0 to High( B ) do Carry := Carry or B[ i ];
// We're succesful if no bits stayed pending.
Result := ( Carry = 0 );
end;
Then transform the base 35 bytes into characters:
function EncodeKeyToString( const Num128Bits: array of Byte ): Ansistring;
var
Src: array [0..15] of Byte; // your 128 bits
Dest: array [0..24] of Byte;
i: Integer;
const
EncodeTable: AnsiString = '0123456789ABCDEFGHIJKLMNPQRSTUVWXYZ';
// O is not present to make base 35. If you want a different code, be my guest.
begin
// Convert to an array of 25 values between 0-35
System.Move( Num128Bits[ 0 ], Src[ 0 ], Length( Src ) ); // Copy data in our private placeholder
DecodeBaseX( Src, Dest, 35 );
// Convert to a representable string
SetLength( Result, Length( Dest ) );
for i := 0 to High( Dest ) do begin
Assert( Dest[ i ] < Length( EncodeTable ) );
Result[ i + 1 ] := EncodeTable[ 1 + Dest[ i ] ];
end;
end;
I don't think you need 128 bit math..
Good luck!

Method to determine if an exe file has been compressed with UPX

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.

Freeware ZIP component for Delphi 2010/Delphi XE?

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.

Convert string with commas to float

Is there a built-in Delphi function which would convert a string such as '3,232.00' to float? StrToFloat raises an exception because of the comma. Or is the only way to strip out the comma first and then do StrToFloat?
Thanks.
Do you exactly know, that '.' is decimal separator and ',' is thousand separator (always)?
If so, then you should fill the TFormatSettings record and pass it to StrToFloat.
FillChar(FS, SizeOf(FS), 0);
... // filling other fields
FS.ThousandSeparator := ',';
FS.DecimalSeparator := '.';
V := StrToFloat(S, FS);
below is what i use. there might be more efficient ways, but this works for me. in short, no, i don't know of any built-in delphi function that will convert a string-float containing commas to a float
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
safeFloat
Strips many bad characters from a string and returns it as a double.
}
function safeFloat(sStringFloat : AnsiString) : double;
var
dReturn : double;
begin
sStringFloat := stringReplace(sStringFloat, '%', '', [rfIgnoreCase, rfReplaceAll]);
sStringFloat := stringReplace(sStringFloat, '$', '', [rfIgnoreCase, rfReplaceAll]);
sStringFloat := stringReplace(sStringFloat, ' ', '', [rfIgnoreCase, rfReplaceAll]);
sStringFloat := stringReplace(sStringFloat, ',', '', [rfIgnoreCase, rfReplaceAll]);
try
dReturn := strToFloat(sStringFloat);
except
dReturn := 0;
end;
result := dReturn;
end;
function StrToFloat_Universal( pText : string ): Extended;
const
EUROPEAN_ST = ',';
AMERICAN_ST = '.';
var
lformatSettings : TFormatSettings;
lFinalValue : string;
lAmStDecimalPos : integer;
lIndx : Byte;
lIsAmerican : Boolean;
lIsEuropean : Boolean;
begin
lIsAmerican := False;
lIsEuropean := False;
for lIndx := Length( pText ) - 1 downto 0 do
begin
if ( pText[ lIndx ] = AMERICAN_ST ) then
begin
lIsAmerican := True;
pText := StringReplace( pText, ',', '', [ rfIgnoreCase, rfReplaceAll ]); //get rid of thousand incidental separators
Break;
end;
if ( pText[ lIndx ] = EUROPEAN_ST ) then
begin
lIsEuropean := True;
pText := StringReplace( pText, '.', '', [ rfIgnoreCase, rfReplaceAll ]); //get rid of thousand incidental separators
Break;
end;
end;
GetLocaleFormatSettings( LOCALE_SYSTEM_DEFAULT, lformatSettings );
if ( lformatSettings.DecimalSeparator = EUROPEAN_ST ) then
begin
if lIsAmerican then
begin
lFinalValue := StringReplace( pText, '.', ',', [ rfIgnoreCase, rfReplaceAll ] );
end;
end;
if ( lformatSettings.DecimalSeparator = AMERICAN_ST ) then
begin
if lIsEuropean then
begin
lFinalValue := StringReplace( pText, ',', '.', [ rfIgnoreCase, rfReplaceAll ] );
end;
end;
pText := lFinalValue;
Result := StrToFloat( pText, lformatSettings );
end;
Try: StrToFloat(StringReplace('3,232.00', ',', '')
It should get rid of the commas before doing the conversion.
In C# / VB.NET I use would use something like decimal.convert("3,232.00", ",", "");
I know of no way to do the conversion without stripping out the extra characters. In fact, I have a special function in my library that strips out commas and currency symbols. So a actually call MyConverer.decimalConverter("$3,232.00");
I use a function which is able to handle the ',' and the '.' as decimalseparator...:
function ConvertToFloat(aNr: String; aDefault:Integer): Extended;
var
sNr, s3R, sWhole, sCent:String;
eRC:Extended;
begin
sNr:=ReplaceStr(sNr, ' ', '');
if (Pos('.', sNr) > 0) or (Pos(',', sNr) > 0) then
begin
// Get 3rd character from right
s3R:=LeftStr(RightStr(sNr, 3), 1);
if s3R <> DecimalSeparator then
begin
if not IsNumber(s3R) then
begin
s3R := DecimalSeparator;
sWhole := LeftSr(sNr, Length(sNr) - 3);
sCent := (RightStr(sNr, 2);
sNr := sWhole + DecimalSeparator + sCent;
end
else
// there are no decimals... add ',00'
sNr:=sNr + DecimalSeparator + '00';
end;
// DecimalSeparator is present; get rid of other symbols
if (DecimalSeparator = '.') and (Pos(',', sNr) > 0) then sNr:=ReplaceStr(sNr, ',', '');
if (DecimalSeparator = ',') and (Pos('.', sNr) > 0) then sNr:=ReplaceStr(sNr, '.', '');
end;
eRc := StrToFloat(sNr);
end;
I had the same problem when my Users need to enter 'scientific' values such as "1,234.06mV". Here there is a comma, a multiplier (m=x0.001) and a unit (V). I created a 'wide' format converter routine to handle these situtations.
Brian
Myfunction:
function StrIsFloat2 (S: string; out Res: Extended): Boolean;
var
I, PosDecimal: Integer;
Ch: Char;
STrunc: string;
liDots, liComma, J: Byte;
begin
Result := False;
if S = ''
then Exit;
liDots := 0;
liComma := 0;
for I := 1 to Length(S) do begin
Ch := S[I];
if Ch = FormatSettings.DecimalSeparator then begin
Inc (liDots);
if liDots > 1 then begin
Exit;
end;
end
else if (Ch = '-') and (I > 1) then begin
Exit;
end
else if Ch = FormatSettings.ThousandSeparator then begin
Inc (liComma);
end
else if not CharIsCipher(Ch) then begin
Exit;
end;
end;
if liComma > 0 then begin
PosDecimal := Pos (FormatSettings.DecimalSeparator, S);
if PosDecimal = 0 then
STrunc := S
else
STrunc := Copy (S, 1, PosDecimal-1);
if STrunc[1] = '-' then
Delete (S, 1, 1);
if Length(STrunc) < ((liComma * 3) + 2) then
Exit;
J := 0;
for I := Length(STrunc) downto 1 do begin
Inc(J);
if J mod 4 = 0 then
if STrunc[I] <> FormatSettings.ThousandSeparator then
Exit;
end;
S := ReplaceStr (S, FormatSettings.ThousandSeparator, '');
end;
try
Res := StrToFloat (S);
Result := True;
except
Result := False;
end;
end;
Using Foreach loop
public static float[] ToFloatArray()
{
string pcords="200.812, 551.154, 232.145, 482.318, 272.497, 511.752";
float[] spiltfloat = new float[pcords.Split(',').Length];
int i = 0;
foreach (string s in pcords.Split(','))
{
spiltfloat[i] = (float)(Convert.ToDouble(s));
i++;
}
return spiltfloat;
}
using lemda Expression to convert string comma seprated to float array
public static float[] ToFloatArrayUsingLemda()
{
string pcords="200.812, 551.154, 232.145, 482.318, 272.497, 511.752";
float[] spiltfloat = new float[pcords.Split(',').Length];
string[] str = pcords.Split(',').Select(x => x.Trim()).ToArray();
spiltfloat = Array.ConvertAll(str, float.Parse);
return spiltfloat;
}
procedure Edit1Exit(Sender: TObject);
begin
edit1.Text:=stringreplace(edit1.Text,'''','',[rfReplaceAll]);
if not IsValidDecimal( maskedit1.Text ) then
begin
showmessage('The Decimal entered -> '+edit1.Text+' <- is in the wrong format ');
edit1.SetFocus;
end;
end;
function IsValidDecimal(S:string):boolean;
VAR
FS: TFormatSettings;
DC: variant;
begin
//FS := TFormatSettings.Create('it-IT');
FS := TFormatSettings.Create('en-EN');
try
DC:=StrToFloat ( S, FS );
result:=true;
except
on e:exception do
result:=false;
end;
end;

Resources