The Mystery of IStream
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.

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;


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!


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