2011-12-17 9 views
4

コード:"WinHttp.WinHttpRequest.5.1"を非同期で使用するには?

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 with IConnectionPoint for Events(ソースコードが添付されています)の非常に優れた実装です。

答えて

3

Stijnが答えたところでは、プログラムの遅延を防ぐために、スレッドを使用してください。 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. 
+0

とてもいいコードTLama。しかし、OnResponseDataAvailable、OnErrorなどを実装するために飛び回っていました.btwは、コードがメインスレッドで実行されている場合、 'CoInitialize'する必要がありますか? – kobik

+0

COMレイトバインディングを使用する場合は、イベント処理を実装するのはかなり複雑です(http://www.techvanguards.com/com/concepts/events.asp)。もちろん、['CoInitialize'](http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543%28v=vs.85%29.aspx)を呼び出す必要があります。なぜなら、' THTTPRequest '私の例では、メインスレッドではなく、ワーカースレッド(' TThread'子孫)です(同じユニットですが、 'TForm1'はメインスレッドで、それぞれのTHTTPRequestは別のワーカースレッドです) 。そして、各スレッドのCOMライブラリを初期化する必要があります。 – TLama

+0

私が言ったのは、私が最初に投稿したコードのようなスレッドを使わないとしても、CoInitializeを呼び出す必要がありますか?EDITセクションに、WinHTTPRequestを使ってイベントをシンクする方法を示した非常に素晴らしいデモを追加しました。 – kobik

1

私はあなたがTThreadオブジェクトについて学ぶことをお勧めします。 TThreadから継承する新しいクラスを作成し、Executeメソッドをオーバーライドし、CoInitialize(COMを有効にする)を呼び出してWinHTTPRequestコードを実行します。要求が完了したら、Synchronizeを使用して結果をフォアグラウンドスレッドに戻します。また、Executeメソッドのtry/except節で例外をキャッチすることができます。

もう1つのオプションは、非同期ブール値プロパティを持つIXMLHTTPRequestオブジェクトに切り替えることです。レイトバインディングでイベントを捕捉するのはかなり難しいかもしれませんが、定期的に状態プロパティをチェックすることができます。

2

IWinHttpRequestは非常にプリミティブです。 Open()で非同期モードが指定されていると警告!

get_ResponseStream()によって返されたIStreamを使用して大きなファイルをダウンロードし、そのデータが到着したときに小さな塊でファイルに書き戻すことができると思われる場合は間違っています。

同期モードまたは非同期モードを使用する場合でも、IWinHttpRequestは常にサーバー応答をメモリにロードし、ENTIREのダウンロードがメモリに格納されるまで、get_ResponseStream()はE_PENDINGを返します。

このインターフェイスは、小さなファイル用に設計されています。

+0

これを指摘してくれてありがとう。他に提案されている代替案がありますか? – kobik

+0

大きなファイルをダウンロードする方法はWinInet.dllにあります:HttpOpenRequest()などを参照してください。 – Elmue

関連する問題