How to use “WinHttp.WinHttpRequest.5.1” asynchronously?

旧巷老猫 提交于 2019-12-22 09:12:14

问题


The code:

var
  WinHttpReq: OleVariant;

procedure TForm1.Button1Click(Sender: TObject);    
begin
  WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
  WinHttpReq.Open('GET', 'http://stackoverflow.com', TRUE); // asynchronously
  WinHttpReq.setRequestHeader('User-Agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0');
  WinHttpReq.Send();
  // HOW to set a callback procedure here and get the response?
end;

Note: I do not want to import mshttp.dll and use TLB. I want to use it via late binding. I also would like to handle exceptions if any.

EDIT: I'm accepting TLama's answer becouse it gives me a good alternative to what I initially was asking. plus it has a good example source.

Here is a very nice implementation of WinHTTPRequest Wrapper with IConnectionPoint for Events (source code is attached).


回答1:


As Stijn said in his answer, to prevent your program to lag, use the threads. IWinHttpRequest.Open has the asynchronous configuration capability too but it would be very difficult to catch the events and IWinHttpRequest.WaitForResponse would stuck your program even so.

Here is the simple example of how to get the response text into the form's memo box. Please note that the following example uses the synchronous mode and that you can additionally modify the timeout values using IWinHttpRequest.SetTimeouts. If you want to use the asynchronous mode as you have in your question then you'll have to wait for the result with IWinHttpRequest.WaitForResponse method.

///////////////////////////////////////////////////////////////////////////////
/////   WinHttpRequest threading demo unit   //////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

unit WinHttpRequestUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ActiveX, ComObj, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

///////////////////////////////////////////////////////////////////////////////
/////   THTTPRequest - TThread descendant for single request   ////////////////
///////////////////////////////////////////////////////////////////////////////

type
  THTTPRequest = class(TThread)
  private
    FRequestURL: string;
    FResponseText: string;
    procedure Execute; override;
    procedure SynchronizeResult;
  public
    constructor Create(const RequestURL: string);
    destructor Destroy; override;
  end;

///////////////////////////////////////////////////////////////////////////////
/////   THTTPRequest.Create - thread constructor   ////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// RequestURL - the requested URL

constructor THTTPRequest.Create(const RequestURL: string);
begin
  // create and start the thread after create
  inherited Create(False);
  // free the thread after THTTPRequest.Execute returns
  FreeOnTerminate := True;
  // store the passed parameter into the field for future use
  FRequestURL := RequestURL;
end;

///////////////////////////////////////////////////////////////////////////////
/////   THTTPRequest.Destroy - thread destructor   ////////////////////////////
///////////////////////////////////////////////////////////////////////////////

destructor THTTPRequest.Destroy;
begin
  inherited;
end;

///////////////////////////////////////////////////////////////////////////////
/////   THTTPRequest.Execute - thread body   //////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

procedure THTTPRequest.Execute;
var
  Request: OleVariant;
begin
  // COM library initialization for the current thread
  CoInitialize(nil);
  try
    // create the WinHttpRequest object instance
    Request := CreateOleObject('WinHttp.WinHttpRequest.5.1');
    // open HTTP connection with GET method in synchronous mode
    Request.Open('GET', FRequestURL, False);
    // set the User-Agent header value
    Request.SetRequestHeader('User-Agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0');
    // sends the HTTP request to the server, the Send method does not return
    // until WinHTTP completely receives the response (synchronous mode)
    Request.Send;
    // store the response into the field for synchronization
    FResponseText := Request.ResponseText;
    // execute the SynchronizeResult method within the main thread context
    Synchronize(SynchronizeResult);
  finally
    // release the WinHttpRequest object instance
    Request := Unassigned;
    // uninitialize COM library with all resources
    CoUninitialize;
  end;
end;

///////////////////////////////////////////////////////////////////////////////
/////   THTTPRequest.SynchronizeResult - synchronization method   /////////////
///////////////////////////////////////////////////////////////////////////////

procedure THTTPRequest.SynchronizeResult;
begin
  // because of calling this method through Synchronize it is safe to access
  // the VCL controls from the main thread here, so let's fill the memo text
  // with the HTTP response stored before
  Form1.Memo1.Lines.Text := FResponseText;
end;

///////////////////////////////////////////////////////////////////////////////
/////   TForm1.Button1Click - button click event   ////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// Sender - object which invoked the event

procedure TForm1.Button1Click(Sender: TObject);
begin
  // because the thread will be destroyed immediately after the Execute method
  // finishes (it's because FreeOnTerminate is set to True) and because we are
  // not reading any values from the thread (it fills the memo box with the
  // response for us in SynchronizeResult method) we don't need to store its
  // object instance anywhere as well as we don't need to care about freeing it
  THTTPRequest.Create('http://stackoverflow.com');
end;

end.



回答2:


IWinHttpRequest is quite primitive. Caveat with the Async mode specified in Open()!

If you think that you can download a big file using an IStream that is returned by get_ResponseStream() and write the data back to a file in small chunks as it arrives, you are wrong.

No matter if you use Sync or Async mode: IWinHttpRequest always loads the entire server response into memory and get_ResponseStream() returns E_PENDING until the ENTIRE download has been stored in memory.

This interface has been designed only for small files.




回答3:


I would suggest you learn about the TThread object. Create a new class that inherits from TThread, override the Execute method, call CoInitialize (to enable COM) and perform the WinHTTPRequest code. When the request is done, use Synchronize to pass the result back to the foreground thread. Also you should be able to catch exceptions in a try/except clause in the Execute method.

Another option is switching to the IXMLHTTPRequest object, which has an async boolean property. Catching events with late-binding may be pretty difficult, but you could check the state property at regular intervals.



来源:https://stackoverflow.com/questions/8547188/how-to-use-winhttp-winhttprequest-5-1-asynchronously

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!