Как использовать "WinHttp.WinHttpRequest.5.1" асинхронно? - PullRequest
4 голосов
/ 17 декабря 2011

Код:

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;

Примечание: я не хочу импортировать mshttp.dll и использовать TLB.Я хочу использовать его через позднюю привязку.Я также хотел бы обрабатывать исключения, если таковые имеются.

РЕДАКТИРОВАТЬ: Я принимаю ответ TLama, потому что он дает мне хорошую альтернативу тому, что я первоначально просил.плюс у него хороший пример источника.

Вот очень хорошая реализация WinHTTPRequest Wrapper с IConnectionPoint for Events (исходный код прилагается).

Ответы [ 3 ]

3 голосов
/ 18 декабря 2011

Как сказал Стейн в своем ответе, для предотвращения задержки вашей программы используйте потоки. IWinHttpRequest.Open также имеет возможность асинхронной конфигурации, но было бы очень трудно перехватить события, и IWinHttpRequest.WaitForResponse застрянет в вашей программе.

Вот простой пример того, как получить текст ответа в диалоговом окне формы. Обратите внимание, что в следующем примере используется синхронный режим, и что вы можете дополнительно изменить значения времени ожидания, используя IWinHttpRequest.SetTimeouts. Если вы хотите использовать асинхронный режим, как у вас в вопросе, вам придется ждать результата с помощью метода IWinHttpRequest.WaitForResponse.

///////////////////////////////////////////////////////////////////////////////
/////   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 голосов
/ 03 июля 2013

IWinHttpRequest довольно примитивен. Будьте осторожны с асинхронным режимом, указанным в Open ()!

Если вы считаете, что вы можете загрузить большой файл с помощью IStream, который возвращается get_ResponseStream (), и записать данные обратно в файл небольшими порциями по мере их поступления, вы ошибаетесь.

Независимо от того, используете ли вы режим синхронизации или асинхронный режим: IWinHttpRequest всегда загружает весь ответ сервера в память, а get_ResponseStream () возвращает E_PENDING, пока ВСЯ загрузка не будет сохранена в памяти.

Этот интерфейс был разработан только для небольших файлов.

1 голос
/ 18 декабря 2011

Я бы посоветовал вам узнать об объекте TThread. Создайте новый класс, который наследуется от TThread, переопределите метод Execute, вызовите CoInitialize (для включения COM) и выполните код WinHTTPRequest. Когда запрос выполнен, используйте Synchronize, чтобы передать результат обратно в основной поток. Кроме того, вы должны иметь возможность перехватывать исключения в предложении try / exception в методе Execute.

Другой вариант - переключение на объект IXMLHTTPRequest, который имеет асинхронное логическое свойство. Перехват событий с поздним связыванием может быть довольно сложным, но вы можете регулярно проверять свойство состояния.

...