File MD5 checksum - delphi

In this question is mentioned the wcrypt2.
What I need is simply calculate the MD5 of a file. It would be perfect if I could calculate it without having to save it because it is a downloaded file in stream format.
I would like to have the most straightforward way to do that.
Thanks!

Here is a working code for Indy 10:
function MD5File(const FileName: string): string;
var
IdMD5: TIdHashMessageDigest5;
FS: TFileStream;
begin
IdMD5 := TIdHashMessageDigest5.Create;
FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Result := IdMD5.HashStreamAsHex(FS)
finally
FS.Free;
IdMD5.Free;
end;
end;
Regards,
OscaR1

Based on #dummzeuch answere I wrote this function:
function getMD5checksum(s: TStream): string;
var
md5: TIdHashMessageDigest5;
hash : T4x4LongWordRecord;
begin
md5 := TIdHashMessageDigest5.Create;
s.Seek(0,0);
hash := md5.HashValue(s);
result := IntToHex(Integer(hash[0]), 4) +
IntToHex(Integer(hash[1]), 4) +
IntToHex(Integer(hash[2]), 4) +
IntToHex(Integer(hash[3]), 4);
end;

Indy comes with functions for calculating several hashes, MD5 is one of them. Indy is included in all versions of Delphi since at least Delphi 2006 and available as a free download for older versions.

What about:
function GetFileMD5(const Stream: TStream): String; overload;
var MD5: TIdHashMessageDigest5;
begin
MD5 := TIdHashMessageDigest5.Create;
try
Result := MD5.HashStreamAsHex(Stream);
finally
MD5.Free;
end;
end;
function GetFileMD5(const Filename: String): String; overload;
var FileStream: TFileStream;
begin
FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Result := GetFileMD5(FileStream);
finally
FileStream.Free;
end;
end;

As you mentioned, the post you linked to talks about wcrypt2, which is a library of cryptographic routines, including MD5. The post you linked to also seems to indicate that it is available for Delphi 7 since the asker includes output labeled "Delphi 7." You have tagged this question delphi7, so I assume that's the version you're using, too. So what's stopping you from using wcrypt2?
The question links to a copy of wcrypt2.pas, and the copyright dates in that file appear to indicate that the unit was available by the time Delphi 7 was released. Check your installation; you might already have it. If not, then the unit also says that it was obtained via Project Jedi, so you could try looking there for the unit as well.
The answers to your referenced question include example Delphi code and the names of units that come with Delphi for doing MD5. They come with Delphi 2009, so you should check whether they're also available for your version.

Take a look at this implementation of MD5SUM in Delphi. It requires a string for input, but I imagine you can easily make it work with a stream.

MessageDigest_5 would work for this as well.

I use the following function in Delphi 7 with Indy 10.1.5
uses IdHashMessageDigest, idHash, Classes;
...
function cc_MD5File(const p_fileName : string) : string;
//returns MD5 has for a file
var
v_idmd5 : TIdHashMessageDigest5;
v_fs : TFileStream;
v_hash : T4x4LongWordRecord;
begin
v_idmd5 := TIdHashMessageDigest5.Create;
v_fs := TFileStream.Create(p_fileName, fmOpenRead OR fmShareDenyWrite) ;
try
v_hash := v_idmd5.HashValue(v_fs);
result := v_idmd5.AsHex(v_hash);
finally
v_fs.Free;
v_idmd5.Free;
end;
end;

If you use Overbyte http://www.overbyte.eu/frame_index.html just add unit and call function FileMD5 with name of file
uses OverbyteIcsMd5;
....
function GetMd5File:String;
begin
Result := FileMD5(FileName);
end;

Related

TStringStream issue in Japanese localization

I have the following setup:
- Windows system language is English.
- I use Delphi 10.1 Berlin.
- In Windows Region & Language/Country set to Japan.
- Region/Administrative/Language for non-Unicode programs set to Japanese (Japan).
I have implemented communication client/server using strings.
Let's skip the question 'why not bytes' for now. I want to show the issue and find the reason why.
I write 2 things into TStringStream:
Header: which includes its size of Int64 (8 bytes), object size of Int64 (8 bytes) and class name: Header length - 2*SizeOf(Int64).
object (TComponent descendant)
procedure ComponentToStream(AComponent: TComponent; AStream: TStream; out HL,OL: Int64);
var
CN: TBytes;
MS1: TMemoryStream;
begin
MS1 := TMemoryStream.Create;
try
CN := TEncoding.Unicode.GetBytes(AComponent.ClassName);
SaveComponentToStream(MS1, AComponent);
OL := MS1.Size;
MS1.Position := 0;
HL := SizeOf(HL) + SizeOf(OL) + Length(CN);
AStream.Write(HL,SizeOf(HL));
AStream.Write(OL,SizeOf(OL));
AStream.Write(CN[0], Length(CN));
MS1.SaveToStream(AStream);
finally
FreeAndNil(MS1);
end;
end;
function PrepareDataBeforeSend(Component: TComponent): string;
var
HL, OL: Int64;
SS: TStringStream;
begin
SS := TStringStream.Create('', TEncoding.Unicode);
try
ComponentToStream(Component, SS, HL, OL);
Result := SS.DataString;
SS.SaveToFile('Orginal stream data.debug');
finally
FreeAndNil(SS);
end;
The result of this method saved in file here
click.
To verify the data I used code below right after calling of one above.
SS := TStringStream.Create({PrepareDataBeforeSend result}, TEncoding.Unicode);
SS.SaveToFile('New stream data.debug');
SS.Free;
Saved binary can be found here Click
And now 2 problems:
If I don't specify explicitly TEncoding.Unicode encoding in constructor of TStringStream, then TEncoding.Default will be used. But for Japanese code page it is ANSII and for English it is Unicode. As a result object size I read later
SS.Read(OL, SizeOf(OL));
is wrong.
Here's the binary to compare. See 8-15 bytes Click
OK, issue 1 was resolved, but still the binary I saved for verification does not match the original one: there is 1 byte missing at the end.
Can anyone tell where is a problem?
Important: there is no issues if I have English localization!!

Copy a file to clipboard in Delphi

I am trying to copy a file to the clipboard. All examples in Internet are the same. I am using one from, http://embarcadero.newsgroups.archived.at/public.delphi.nativeapi/200909/0909212186.html but it does not work.
I use Rad Studio XE and I pass the complete path. In mode debug, I get some warnings like:
Debug Output:
Invalid address specified to RtlSizeHeap( 006E0000, 007196D8 )
Invalid address specified to RtlSizeHeap( 006E0000, 007196D8 )
I am not sure is my environment is related: Windows 8.1 64 bits, Rad Studio XE.
When I try to paste the clipboard, nothing happens. Also, seeing the clipboard with a monitor tool, this tool shows me error.
The code is:
procedure TfrmDoc2.CopyFilesToClipboard(FileList: string);
var
DropFiles: PDropFiles;
hGlobal: THandle;
iLen: Integer;
begin
iLen := Length(FileList) + 2;
FileList := FileList + #0#0;
hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
SizeOf(TDropFiles) + iLen);
if (hGlobal = 0) then raise Exception.Create('Could not allocate memory.');
begin
DropFiles := GlobalLock(hGlobal);
DropFiles^.pFiles := SizeOf(TDropFiles);
Move(FileList[1], (PChar(DropFiles) + SizeOf(TDropFiles))^, iLen);
GlobalUnlock(hGlobal);
Clipboard.SetAsHandle(CF_HDROP, hGlobal);
end;
end;
UPDATE:
I am sorry, I feel stupid. I used the code that did not work, the original question that somebody asked, in my project, while I used the Remy's code, the correct solution, here in Stackoverflow. I thought that I used the Remy's code in my project. So, now, using the Remy's code, everything works great. Sorry for the mistake.
The forum post you link to contains the code in your question and asks why it doesn't work. Not surprisingly the code doesn't work for you any more than it did for the asker.
The answer that Remy gives is that there is a mismatch between ANSI and Unicode. The code is for ANSI but the compiler is Unicode.
So click on Remy's reply and do what it says: http://embarcadero.newsgroups.archived.at/public.delphi.nativeapi/200909/0909212187.html
Essentially you need to adapt the code to account for characters being 2 bytes wide in Unicode Delphi, but I see no real purpose repeating Remy's code here.
However, I'd say that you can do better than this code. The problem with this code is that it mixes every aspect all into one big function that does it all. What's more, the function is a method of a form in your GUI which is really the wrong place for it. There are aspects of the code that you might be able to re-use, but not factored like that.
I'd start with a function that puts an known block of memory into the clipboard.
procedure ClipboardError;
begin
raise Exception.Create('Could not complete clipboard operation.');
// substitute something more specific that Exception in your code
end;
procedure CheckClipboardHandle(Handle: HGLOBAL);
begin
if Handle=0 then begin
ClipboardError;
end;
end;
procedure CheckClipboardPtr(Ptr: Pointer);
begin
if not Assigned(Ptr) then begin
ClipboardError;
end;
end;
procedure PutInClipboard(ClipboardFormat: UINT; Buffer: Pointer; Count: Integer);
var
Handle: HGLOBAL;
Ptr: Pointer;
begin
Clipboard.Open;
Try
Handle := GlobalAlloc(GMEM_MOVEABLE, Count);
Try
CheckClipboardHandle(Handle);
Ptr := GlobalLock(Handle);
CheckClipboardPtr(Ptr);
Move(Buffer^, Ptr^, Count);
GlobalUnlock(Handle);
Clipboard.SetAsHandle(ClipboardFormat, Handle);
Except
GlobalFree(Handle);
raise;
End;
Finally
Clipboard.Close;
End;
end;
We're also going to need to be able to make double-null terminated lists of strings. Like this:
function DoubleNullTerminatedString(const Values: array of string): string;
var
Value: string;
begin
Result := '';
for Value in Values do
Result := Result + Value + #0;
Result := Result + #0;
end;
Perhaps you might add an overload that accepted a TStrings instance.
Now that we have all this we can concentrate on making the structure needed for the CF_HDROP format.
procedure CopyFileNamesToClipboard(const FileNames: array of string);
var
Size: Integer;
FileList: string;
DropFiles: PDropFiles;
begin
FileList := DoubleNullTerminatedString(FileNames);
Size := SizeOf(TDropFiles) + ByteLength(FileList);
DropFiles := AllocMem(Size);
try
DropFiles.pFiles := SizeOf(TDropFiles);
DropFiles.fWide := True;
Move(Pointer(FileList)^, (PByte(DropFiles) + SizeOf(TDropFiles))^,
ByteLength(FileList));
PutInClipboard(CF_HDROP, DropFiles, Size);
finally
FreeMem(DropFiles);
end;
end;
Since you use Delphi XE, strings are Unicode, but you are not taking the size of character into count when you allocate and move memory.
Change the line allocating memory to
hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
SizeOf(TDropFiles) + iLen * SizeOf(Char));
and the line copying memory, to
Move(FileList[1], (PByte(DropFiles) + SizeOf(TDropFiles))^, iLen * SizeOf(Char));
Note the inclusion of *SizeOf(Char) in both lines and change of PChar to PByte on second line.
Then, also set the fWide member of DropFiles to True
DropFiles^.fWide := True;
All of these changes are already in the code from Remy, referred to by David.

How to convert IStream to TStreamAdapter?

How to convert IStream to TStreamAdapter on Delphi 7?
On Delphi XE2 I may write:
var
aStream: IStream;
aStreamAdapter: TStreamAdapter;
begin
...
aStreamAdapter := aStream as TStreamAdapter;
...
end;
But Delphi 7 writes:
Error: Operator not applicable to this operand type
That code works because of a new feature introduced in D2010, namely the ability to recover a reference to the object that implements an interface. Note though that if the IStream is implemented by something other than your Delphi code, then the cast will fail.
If you need to refer to the implementing object in older versions of Delphi then you will need to use one of the various hacks to recover it. For example:
Hallvard Vassbotn's classic approach.
Barry Kelly's more recent variant.
However, you should not need to get back to the implementing object. The fact that you do want to is a very strong indication that your design is wrong.
The unit AxCtrls has an TOleStream object to do just that.
var
aStream: IStream;
bStream: TStream;
begin
bStream := TOleStream.Create(aStream);
try
//
finally
bStream.Free;
end;
end;
You need to use the Create method like
var StreamAdapter:TStreamAdapter;
begin
StreamAdapter := TStreamAdapter.Create(aStream);
...
Sample code:
var
aFileStream: TFileStream;
iStr: TStreamAdapter;
iRes , iRes1, iRes2: Largeint;
aStreamStat: TStatStg;
aStreamContent: IStream;
begin
aFileStream := TFileStream.Create('<...>', fmCreate);
try
aStreamContent := <...> as IStream;
aStreamContent.Seek(0, 0, iRes);
iStr := TStreamAdapter.Create(aFileStream, soReference);
aStreamContent.Stat(aStreamStat, 1);
aStreamContent.CopyTo(iStr, aStreamStat.cbSize , iRes1, iRes2);
finally
aFileStream.Free;
end;
end;

Delphi reading non-text file (binary)

how in Delphi could I open binary file in non-text mode?
Like C function fopen(filename,"rb")
There are a few options.
1. Use a file stream
var
Stream: TFileStream;
Value: Integer;
....
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Stream.ReadBuffer(Value, SizeOf(Value));//read a 4 byte integer
finally
Stream.Free;
end;
2. Use a reader
You would combine the above approach with a TBinaryReader to make the reading of the values simpler:
var
Stream: TFileStream;
Reader: TBinaryReader;
Value: Integer;
....
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Reader := TBinaryReader.Create(Stream);
try
Value := Reader.ReadInteger;
finally
Reader.Free;
end;
finally
Stream.Free;
end;
The reader class has lots of functions to read other data types. And you can go in the opposite direction with a binary writer.
3. Old style Pascal I/O
You can declare a variable of type File and use AssignFile, BlockRead, etc. to read from the file. I really don't recommend this approach. Modern code and libraries almost invariably prefer the stream idiom and by doing the same yourself you'll make your code easier to fit with other libraries.
You have different options, two of them are:
Use the old school approach, like the C function you pointed out:
var
F: File;
begin
AssignFile(F, 'c:\some\path\to\file');
ReSet(F);
try
//work with the file
finally
CloseFile(F);
end
end;
Use a more modern approach to create a TFileStream based on the file:
var
F: TFileStream;
begin
F := TFileStream.Create('c:\some\path\to\file', fmOpenRead);
try
//work with the file
finally
F.Free;
end;

Binary to Base64 (Delphi)

How can I get content of an exe file and convert it into Base64 encoding ?
Edit
I use D2010 and I want to know how is it possible exactly ?
open an exe file
convert its content into base64
In Delphi 2009/2010/XE there is unit EncdDecd.pas (Soap.EncdDecd.pas for Delphi XE2) containing the functions EncodeBase64 and DecodeBase64. You can load the exe file into a memorystream and then call EncodeBase64.
function EncodeFile(const FileName: string): AnsiString;
var
stream: TMemoryStream;
begin
stream := TMemoryStream.Create;
try
stream.LoadFromFile(Filename);
result := EncodeBase64(stream.Memory, stream.Size);
finally
stream.Free;
end;
end;
In ancient Delphi versions, you can use synapse (link here)
Just put synacode.pas in your uses e call EncodeBase64/EncodeBase64.
Cheers
As also mentioned in the comments, since Delphi XE8 you can use the System.NetEncoding.TNetEncoding.Base64 class property.
It also returns a string instead of an AnsiString:
function TryEncodeFile(const AFileName: string; out ABase64string: string): Boolean;
var
MemStream: TMemoryStream;
begin
MemStream := TMemoryStream.Create;
try
MemStream.LoadFromFile(AFileName);
ABase64string :=
TNetEncoding.Base64.EncodeBytesToString(MemStream.Memory, MemStream.Size);
Result := True;
finally
MemStream.Free;
end;
end;

Resources