3

I am using Delphi along with WinHTTP to do an HTTP request to download some files from the internet, and I can do the request but I don't know how to get the IStream from the OleVariant that is returned from ResponseStream. I have spent a lot of time googling but I can't figure out how to do it. Here is what I have tried:

var
  req: IWinHTTPRequest;
  instream: IStream;
begin
  req := CoWinHTTPRequest.Create;

  req.Open('GET', 'http://google.com', false);
  req.Send('');

  if req.Status <> 200 then
  begin
    ShowMessage('failure'#10 + req.StatusText);

    FreeAndNil(req);

    Application.Terminate;
  end;

  instream := req.ResponseStream as IStream;

  ShowMessage('success');

  FreeAndNil(instream);
  FreeAndNil(req);

end;

But I get the error [DCC Error] main.pas(45): E2015 Operator not applicable to this operand type (line 45 is instream := req.ResponseStream as IStream;).

How do I scare the IStream out of an OleVariant?

Okey
  • 105
  • 5
  • Your assumption that execution halts when you call `Application.Terminate` is incorrect. That just posts a `WM_QUIT` message, but your function will continue executing. – David Heffernan Feb 08 '11 at 21:53

1 Answers1

9

Try this

instream := IUnknown(req.ResponseStream) as IStream;

Edit 1 You must not call FreeAndNil on an interface. FreeAndNil can only be passed an object instance. Failure to do so results in an exception. Since interfaces are reference counted anyway you can simply let them go out of scope and they will be cleaned up. So, you need to remove:

  FreeAndNil(instream);
  FreeAndNil(req);

Edit2: A try to explain what is going on

Please feel free to edit or complement if you think this is not accurate or if it can be explained better.

req.ResponseStream is an OleVariant. The as keyword is doing a call to QueryInterface and that is not implemented by OleVariant.

OleVariant has a built in type conversion from OleVariant to IUnknown so you need to first cast the OleVariant to IUnknown and then use the as operator to do a QueryInterface in order to get the IStream interface.

You can not cast a OleVariant directly to a IStream because there is no built in type conversion from OleVariant to IStream.

Mikael Eriksson
  • 136,425
  • 22
  • 210
  • 281
  • It's not so much that you should do this to something that is reference counted, it's more that they are not subclasses of TObject and so calling Free on them is meaningless. – David Heffernan Feb 08 '11 at 21:52
  • @David Heffernan- you get an access violation when the procedure goes out of scope if you do. I guess it is the call to Release after procedure end that is causing that. – Mikael Eriksson Feb 08 '11 at 21:55
  • @Mikael You can't call `Free` on an interface. You can only call `Free` on an object. Reference counting is irrelevant. The only way you get away with `FreeAndNil` is that takes an untyped parameter. Removing the part that nils the reference it is `procedure FreeAndNil(var obj); begin TObject(obj).Free; end;` – David Heffernan Feb 08 '11 at 21:59
  • @David - You can create your own code by importing typelibrary for "Microsoft WinHTTP Services". – Mikael Eriksson Feb 08 '11 at 22:12
  • @Mikael I've edited your edit! I think it's more clear now. I really don't understand what's going on though because `IWinHTTPRequest.ResponseStream` is an IStream according to MSDN. – David Heffernan Feb 08 '11 at 22:14
  • @Mikael Thanks, I've been trying to work out which typelib it was! – David Heffernan Feb 08 '11 at 22:14
  • @Mikael `property ResponseStream: OleVariant` Weird! MSDN says it is "A Variant that receives a pointer to an IUnknown interface that can be queried for an IStream interface." It's too hard for me!! – David Heffernan Feb 08 '11 at 22:19
  • 2
    @David The technique to get a Interface from an OleVariant is described here. http://www.techvanguards.com/stepbystep/comdelphi/client.asp . About 2/3 down in the document. Something about "extract a vtable interface pointer". To late for me now to figure out why it needs to be done that way. – Mikael Eriksson Feb 08 '11 at 22:29
  • @mikael thanks. Your answer would be improved with an explanation of the trick and that link. As is it is feels a bit magical! Anyway good work and yet again I learn something new here! – David Heffernan Feb 08 '11 at 22:47
  • @David - Tried to explain in answer what is going on and why you need to cast to `IUnknown` before using `as IStream`. I can not explain the inner workings of `OleVariant` and how implicit type conversion for OleVariant works. – Mikael Eriksson Feb 09 '11 at 13:43
  • @Mikael I think I will spend a little time trying to understand this idiom and if so then I'll maybe edit your answer further. It would be nice to capture this trick somewhere and it may actually be present in another answer to another Q here already. – David Heffernan Feb 09 '11 at 14:08
  • @Mikael I've been pondering this, reading up about it and I think your latest edit gets the message across quite nicely. – David Heffernan Feb 09 '11 at 22:55