The Mystery of IStream

Max Kleiner
4 min readDec 9, 2023

For clarity and safety, the IStream object defined in the Delphi Visual Component Library (VCL) is a dynamic object that doesn’t have any methods, procedures, functions, events etc… until these methods are defined in your code. The IStream Object will publish these methods correctly for external COM objects expecting an IStream interface.

For example you want to convert an OleVariant from an external COM to a memory stream, its kind of a mystery stream:

function getmemstreamfromIStream(avariant: variant): TMemorystream;
var instream: IStream; astream: TStream;
begin
instream:= IUnknown(avariant) as IStream;
astream:= TOleStream.Create(instream);
result:= astream as TMemorystream;
end;

That will not work because its an invalid class typecast exception, the solution will be a copy from:

function getMemStreamfromIStream2(avariant: variant): TMemorystream;
var instream: IStream; ostream: TStream;
begin
instream:= IUnknown(avariant) as IStream;
ostream:= TOleStream.Create(instream);
result:= TMemorystream.Create;
try
result.CopyFrom(OStream, OStream.Size);
finally
OStream.Free;
end;
end;

You could use most any TStream derived object within your own code implementation, including THandleStream or TFileStream. THandleStream provides access to the file handle variable that Windows API require for many core file read/write API calls.

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.

As another solution is the load of an IStream from an OLE response stream as unknown variant type to a well known TMemoryStream in order to save the response stream to a file (in our example a binary QR-code image file as a png graphic):

Const
URLGoogleQRCODE='https://chart.apis.google.com/chart?chs=%dx%d&cht=qr&chld=%s&chl=%s';
AFILENAME= 'mX5QRCode5.png';
QDATA= 'https://maxbox4.wordpress.com/';

Type TQrImage_ErrCorrLevel=(L,M,Q,H);

Function QRCcodeOle(Wid,Hei:Word; C_Level,apath:string; const Data:string): string;
var
httpReq,hr: Olevariant; instream: IStream;
jo: TJSON; strm :TMemoryStream;
begin
httpReq:= CreateOleObject('WinHttp.WinHttpRequest.5.1');
//jo:= TJSON.Create();
hr:= httpReq.Open('GET',
format(URLGoogleQRCODE,[Wid,Hei,C_Level,HTTPEncode(Data)]))
httpReq.setRequestheader('content-type','application/octet-stream');
//httpReq.setRequestheader('Authorization','Bearer '+ CHATGPT_APIKEY2);
if hr= S_OK then HttpReq.Send();
strm:= TMemoryStream.create;
If HttpReq.Status = 200 Then begin
try
strm:= getMemStreamfromIStream2(HttpReq.responsestream);
//getmemStreamfromIStream2file(hrstream, apath);
writeln('responsestream size: '+itoa(strm.size));
strm.savetoFile(apath)
openFile(apath);
except
writeln('EHTTPex: '+ExceptiontoString(exceptiontype, exceptionparam));
finally
strm.free;
httpreq:= unassigned;
end;
end;
end;
//https://stackoverflow.com/questions/15441014/how-do-i-load-an-istream-into-a-tmemorystream

And the call of the function, but it doesn’t returns anything for the moment, cause we store the result direct in a file (better would be to get back a boolean of success or fail):

//6. Call of the OLE WinHttp Class
writeln('back of OLE call: '+
QRCcodeOle(150,150,'Q',ExePath+'\examples\'+AFILENAME, QDATA));
writeln('SHA1 '+Sha1(ExePath+'examples\'+AFILENAME)); //}

Another option is to write a TStream-derived class that accesses the IStream internally (similar to how the RTL's TStreamAdapter class wraps a TStream so it can be passed around as an IStream).

Using the Google Chart Tools / Image Charts (aka Chart API) you can easily generate QR codes, this kind of images are a special type of two-dimensional barcodes. They are also known as hardlinks or physical world hyperlinks.

QDATA= ‘https://maxbox4.wordpress.com/';

I was probably not aware of TOleStream at the time I wrote this answer. Looking at TOleStream now, I notice that it does not support 64-bit streams. This code does. Other than that, this code is almost identical to the code that TOleStream uses, with one only exception being that this code's implementation of the Size property getter is more optimized than TOleStream's implementation is, and this code implements the Size property setter whereas TOleStream does not.

So we can combine the invoke call from HttpReq.responsestream to get a file in one function:

function getmemStreamfromIStream2File(avariant: variant; 
apath: string): Tmemorystream;
var instream: IStream; ostream: TStream;
begin
instream:= IUnknown(avariant) as IStream;
ostream:= TOleStream.Create(instream);
result:= Tmemorystream.Create;
try
result.CopyFrom(OStream, OStream.Size);
result.SaveToFile(apath)
finally
OStream.Free;
end;
end;
maXbox5 QRCode Tutorial
Get Images as Response Stream

Conclusion: There are probably better ways, but I would create a TOleStream, which is designed as an IStream wrapper; then you can use the CopyFrom() method of your MemoryStream to load and save the data!

compare hybrid code in mX5

RIP Niklaus Wirth died on the 1.th of January 2024

Travel Ravel

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Max Kleiner
Max Kleiner

Written by Max Kleiner

Max Kleiner's professional environment is in the areas of OOP, UML and coding - among other things as a trainer, developer and consultant.

No responses yet

Write a response