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;
Related
I'm using a TMemoryStream and I need to pass it to var TStream procedure (external lib). I just cast my TMemoryStream to TStream like this:
var
myStream: TMemoryStream;
begin
//...
ExternalProcedure(TStream(myStream)); //procedure ExternalProcedure(var AStream: TStream);
end;
Is this something to avoid? Does it take a lot of extra memory (the stream in question can be several MB)? My guess is no, but not 100% sure in Delphi.
MBo is right: this kind of cast will only instruct the compiler to assume that the type is the one you specify. This is essentially a cast that will make no additional code being generated. (But there are many exceptions when you cast value types.)
However, I generally would avoid this kind of unsafe cast. Here is an example of what can go wrong:
type
TAnimal = class
DNASequence: string;
end;
TSpider = class(TAnimal)
MoultCount: Integer;
end;
procedure ChangeAnimal(var AAnimal: TAnimal);
begin
FreeAndNil(AAnimal);
AAnimal := TAnimal.Create;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Spider: TSpider;
begin
Spider := TSpider.Create;
try
Spider.DNASequence := 'CTGA...';
Spider.MoultCount := 3;
ChangeAnimal(TAnimal(Spider));
ShowMessage(Spider.MoultCount.ToString); // Oops! The `TSpider` variable
// points to a `TAnimal` object!
finally
Spider.Free;
end;
end;
Casting does not create new objects and correpondingly does not get new memory. It is just instruction for compiler to provide type compatibility
How can I get source code from WebBrowser component?
I want to get source code of active page on WebBrowser component and write it to a Memo component.
Thanks.
You can use the IPersistStreamInit Interface and the save method to store the content of the Webbrowser in a Stream.
Uses
ActiveX;
function GetWebBrowserHTML(const WebBrowser: TWebBrowser): String;
var
LStream: TStringStream;
Stream : IStream;
LPersistStreamInit : IPersistStreamInit;
begin
if not Assigned(WebBrowser.Document) then exit;
LStream := TStringStream.Create('');
try
LPersistStreamInit := WebBrowser.Document as IPersistStreamInit;
Stream := TStreamAdapter.Create(LStream,soReference);
LPersistStreamInit.Save(Stream,true);
result := LStream.DataString;
finally
LStream.Free();
end;
end;
That works well too:
uses MSHTML;
function GetHTML(w: TWebBrowser): String;
Var
e: IHTMLElement;
begin
Result := '';
if Assigned(w.Document) then
begin
e := (w.Document as IHTMLDocument2).body;
while e.parentElement <> nil do
begin
e := e.parentElement;
end;
Result := e.outerHTML;
end;
end;
This has been asked and answered many times in the Embarcadero forums, with plenty of code examples posted. Search the archives.
The gist of it is that you Navigate() to the desired URL and wait for the OnDocumentComplete event to fire, then QueryInterface() the Document property for the IPersistStreamInit interface and call its save() method. Create a TStream object instance, such as a TMemoryStream, wrap it in a TStreamAdapter object, and then pass the adapter to save(). You can then load the TStream into the TMemo as needed.
Why not Quick and Dirty:
OnNavigateComplete2()
Form1.RichEdit1.Text:=(WebBrowser1.OleObject.Document.documentElement.outerhtml);
I am trying to find a safe/deterministic way to release an interface which is encapsulated in an OleVariant.
AFAICS Delphi releases interface references at the end of a procedure, but in my case I have to do it earlier, because I have to shut down COM.
procedure Test;
var
LLibrary: OleVariant;
begin
CoInitialize(nil);
try
LLibrary := Null;
try
LLibrary := CreateOleObject(LibraryName);
finally
LLibrary := Unassigned; // <-- I would like to release the interface here
end;
finally
CoUninitialize; // <-- Shutdown of COM
end;
end; // <-- The compiler releases the interface here
I though of putting the OleVariant in an extra class instance that I can free before I call CoUninitialize.
procedure Test;
var
Container: TLibraryContainer; // Holds the OleVariant
begin
CoInitialize(nil);
try
Container := TLibraryContainer.Create;
try
{...}
finally
Container.Free;
end;
finally
CoUninitialize;
end;
end;
Is this solution safe or is there a better solution I have overlooked?
The compiler is clearly using an implicit local interface variable for the return value from CreateOleObject. This is then released at the end of the routine, too late for you.
There are a couple of ways to defeat this. First of all you could be explicit about the IDispatch interface reference returned by CreateOleObject. This allows you to control its lifetime.
procedure Test;
var
intf: IDispatch;
LLibrary: OleVariant;
begin
CoInitialize(nil);
try
intf := CreateOleObject(LibraryName);
try
LLibrary := intf;
finally
VarClear(LLibrary);
intf := nil;
end;
finally
CoUninitialize;
end;
end;
An alternative would be to move the code that called CreateOleObject into a separate routine with its own scope.
procedure DoWork;
var
LLibrary: OleVariant;
begin
LLibrary := CreateOleObject(LibraryName);
//do stuff with LLibrary
end;
procedure Test;
begin
CoInitialize(nil);
try
DoWork;
finally
CoUninitialize;
end;
end;
Since the implicit local reference is within the scope of DoWork it is released at the end of DoWork and therefore before you run CoUninitialize.
My recommendation is to use the second option which is cleaner and forces the compiler to do the work on your behalf.
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;
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;