Как использовать "WinHttp.WinHttpRequest.5.1" асинхронно?
Код:
var
WinHttpReq: OleVariant;
procedure TForm1.Button1Click(Sender: TObject);
begin
WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
WinHttpReq.Open('GET', 'http://stackru.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 ответа
Как сказал Стейн в своем ответе, для предотвращения отставания вашей программы используйте потоки. 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://stackru.com');
end;
end.
IWinHttpRequest довольно примитивен. Будьте осторожны с асинхронным режимом, указанным в Open()!
Если вы думаете, что можете загрузить большой файл с помощью IStream, который возвращается get_ResponseStream(), и записать данные обратно в файл небольшими порциями по мере их поступления, вы ошибаетесь.
Независимо от того, используете ли вы режим синхронизации или асинхронный режим: IWinHttpRequest всегда загружает полный ответ сервера в память, а get_ResponseStream() возвращает E_PENDING, пока ВСЯ загрузка не будет сохранена в памяти.
Этот интерфейс был разработан только для небольших файлов.
Я бы посоветовал вам узнать об объекте TThread. Создайте новый класс, который наследуется от TThread, переопределите метод Execute, вызовите CoInitialize (для включения COM) и выполните код WinHTTPRequest. Когда запрос выполнен, используйте Synchronize, чтобы передать результат обратно в основной поток. Кроме того, вы должны быть в состоянии отловить исключения в предложении try/exception в методе Execute.
Другой вариант - переключение на объект IXMLHTTPRequest, который имеет асинхронное логическое свойство. Перехват событий с поздним связыванием может быть довольно трудным, но вы можете проверять свойство состояния через регулярные промежутки времени.