DOM プログラミングレッスン
第4回:MSXMLの非同期読み込み機能を使用する
(株)日本ユニテック
太田 純
<この記事はDigital
Xpress 2001 Vol.4(8-9月号)に掲載されたものです>
今回は、XMLファイルを非同期に読み込む方法について扱っていきたいと思います。 |
DOMDocumentオブジェクトのonreadystatechange イベント
MSXMLにおいて、load実行を非同期で行うかどうかを指定するためにasyncプロパティを使うことができます。この連載の今までの記事においては、常にasyncプロパティにfalseを設定して、読み込みが完了してから次の行に進むようなコードを記述してきました。このasyncプロパティをtrueに設定しておけば、非同期に読み込みが実行されます。非同期読み込みを行えば次のような利点があります。
- ただちにユーザーに処理を返すことができる
- 読み込みの待ち時間中に他の処理を行うことができる
- 読み込みのパフォーマンスを上げることができる
たとえば2つのファイルを読み込む場合、async = falseの設定で1つずつ読み込む場合より、async = trueにして、同時に2つのファイルを読み込むほうが両方を読み終える時間が短くなる場合が多くあります。このパフォーマンスは同時に読み込むファイル数やファイルのサイズとも関係しますので、実際に読み込むファイルを使用して処理時間を計測してみることをお勧めします。
さて、asyncプロパティを設定してloadするだけでは、読み込みがいつ完了したのかわかりません。MicrosoftのXML
SDKのドキュメントを見ますと、非同期に読み込んだ場合、onreadystatechange イベントが発生することになっており、このイベントの説明の項に非同期実行するためのコーディング例が載せられています。
Visual Basicの場合はリスト1のようになります。Dim WithEventsステートメントを使用してDOMDocument変数を宣言しておくと、ロード終了時に「変数名_onreadystatechange」
プロシージャが呼ばれるので、そこでreadystateプロパティをチェックすればよいことがわかります。
JavaScriptの場合はリスト2のようになります。DOMDocumentのonreadystatechangeプロパティに、このイベントが発生した時に呼ぶ関数名CheckStateを渡しておくと、イベント発生時に呼ばれるようになります。
C++を使う場合についてもコーディング例は紹介されているものの、行数が多いですし、わかりにくく感じる方も少なくないでしょう。 図.1
イベント処理用のATLオブジェクトを作成する
ここでは、onreadystatechangeイベントを処理するためのATLオブジェクトの作成方法を紹介します。XML処理用にATLのプロジェクトを作成してDLLを構築すれば、処理を適切に切り分けることができるでしょう。
「ATLオブジェクトの新規作成」を使用してシンプルオブジェクトを作成してから、次の2つのメソッドを追加することにします。
- loadASync(BSTR URL):URLで指定されたXMLファイルの非同期読み込みを開始する。
- OnReadyStateChange():onreadystatechangeイベントが発生した時に、MSXML側から呼び出してもらうためのメソッド。このメソッドを呼び出し元アプリケーションが使うことはないので、attributeに「hidden」を追加しておきます。
2つのメソッドを追加すると、IDLファイル中の該当するインターフェースに関する記述はリスト3のような形になっているはずです。ここで、OnReadyStateChangeメソッドのIDを、MSXMLが呼び出す時に使用するIDに変えるため、IDLファイルを編集します。編集した結果はリスト4の状態になります。OnReadyStateChange
メソッドのIDをDISPID_READYSTATECHANGEに書き直し、ファイルの先頭にはこのIDの定義を利用するため
#includeを追加しました。
ヘッダーファイルとcppファイルをそれぞれリスト5およびリスト6に示しました。
まず、FinalConstruct関数をオーバーライドして 、ここでDOMDocumentオブジェクトを作成します。FinalConstruct関数は、このオブジェクトが作成された時にコンストラクタの直後のタイミングで呼び出されます。HRESULT値によって処理が成功したかどうかを知らせることができますので、エラーが発生する可能性のある処理はコンストラクタではなく、FinalConstruct関数内で行うほうがよいでしょう。
loadASyncメソッド内では次のように処理を行っています。
- asyncプロパティを設定(リスト6の(A))。実際は、asyncプロパティの既定値はtrueなので、呼び出さなくてもかまいません。
- AtlAdvise関数の呼び出し(リスト6の(B))。
AtlAdviseの引数は以下のとおりです。
第1引数:クライアントが接続しようとしているオブジェクト。この場合DOMDocumentオブジェクトです。
第2引数:クライアントオブジェクト。この場合は自分が今実装しているクラスのオブジェクトです。GetUnknown関数によってIUnknownポインタを取得して使用します。
第3引数:接続先オブジェクトがイベント送出に使用するインターフェースのGUID。
第4引数:接続を一意に識別するための数値。接続するときに受け取り、あとで接続を切り離すときに使用します。
こうして接続してからloadメソッドを呼び出せば、あとはonreadystatechangeイベントが来るのを待つだけです。イベントハンドラ、すなわちこの場合のOnReadyStateChangeメソッド内ではDOMDocumentのreadystateプロパティをチェックします。プロパティの値がREADYSTATE_COMPLETE
(4) になればロード完了です。エラーが発生した場合もREADYSTATE_COMPLETEが示されるので、ここでチェックを行うことができます。
さて、このようにしてコンポーネントをビルドしたら、使用するアプリケーション側でリスト7のようにしてloadASyncメソッドを呼び出すことができます。
ATLコンポーネントを使用して、XMLファイルの非同期読み込みを行う方法について扱いました。ATLの機能を使用することにより、XML
SDKのドキュメントに書かれた方法よりはるかに簡単に実装を行うことができます。ぜひ活用してみてください。
リスト1 Visual Basicで非同期処理を行う
Dim WithEvents xmldoc As DOMDocument
Private Sub Form_Load()
Set xmldoc = New DOMDocument
xmldoc.Load "http://www.mimimomi.org/shinryouin.xml"
End Sub
Private Sub xmldoc_onreadystatechange()
If xmldoc.readyState = 4 Then
' 終了時の処理
End If
End Sub
|
リスト2 JavaScriptで非同期処理を行う
var xmldoc;
function Load()
{
xmldoc = new ActiveXObject("Microsoft.XMLDOM");
xmldoc.onreadystatechange = CheckState;
xmldoc.load("http://www.mimimomi.org/shinryouin.xml");
}
function CheckState()
{
var state = xmldoc.readyState;
if (state == 4)
{
// 終了時の処理
}
} |
リスト3 IDLファイル(ATLウィザードが生成したもの:関係する部分のみ)
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(A7CBB97B-A858-42C8-81B6-745A97642D5C),
dual,
helpstring("ISample04Obj Interface"),
pointer_default(unique)
]
interface ISample04Obj : IDispatch
{
[id(1), helpstring("メソッド loadASync")] HRESULT
loadASync(BSTR URL);
[id(2), helpstring("メソッド OnReadyStateChange"),
hidden] HRESULT OnReadyStateChange();
}; |
リスト4 IDLファイル(編集後)
import "oaidl.idl";
import "ocidl.idl";
#include <olectl.h>
[
object,
uuid(A7CBB97B-A858-42C8-81B6-745A97642D5C),
dual,
helpstring("ISample04Obj Interface"),
pointer_default(unique)
]
interface ISample04Obj : IDispatch
{
[id(1), helpstring("メソッド loadASync")] HRESULT
loadASync(BSTR URL);
[id(DISPID_READYSTATECHANGE),
helpstring("メソッド OnReadyStateChange"), hidden]
HRESULT OnReadyStateChange();
}; |
リスト5 ヘッダーファイル
// Sample04Obj.h : CSample04Obj の宣言
#ifndef __SAMPLE04OBJ_H_
#define __SAMPLE04OBJ_H_
#include "resource.h" // メイン シンボル
/////////////////////////////////////////////////////////////////////////////
// CSample04Obj
class ATL_NO_VTABLE CSample04Obj :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CSample04Obj, &CLSID_Sample04Obj>,
public IDispatchImpl<ISample04Obj, &IID_ISample04Obj,
&LIBID_SAMPLE04ATLLib>
{
public:
CSample04Obj()
:
m_dwCookie(0)
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_SAMPLE04OBJ)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CSample04Obj)
COM_INTERFACE_ENTRY(ISample04Obj)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
// ISample04Obj
protected:
CComPtr<IXMLDOMDocument>
m_pDoc;
DWORD
m_dwCookie;
public:
HRESULT
FinalConstruct();
STDMETHOD(OnReadyStateChange)();
STDMETHOD(loadASync)(BSTR URL);
};
#endif //__SAMPLE04OBJ_H_
|
リスト6 cppファイル
HRESULT CSample04Obj::FinalConstruct()
{
return m_pDoc.CoCreateInstance(__uuidof(DOMDocument));
}
STDMETHODIMP CSample04Obj::loadASync(BSTR URL)
{
HRESULT
hr = m_pDoc->put_async(VARIANT_TRUE); (A)
if(FAILED(hr))
return hr;
hr
= AtlAdvise(m_pDoc, GetUnknown(), __uuidof(XMLDOMDocumentEvents),
&m_dwCookie); (B)
if(FAILED(hr))
return hr;
VARIANT_BOOL f;
m_pDoc->load(CComVariant(URL), &f);
return S_OK;
}
STDMETHODIMP CSample04Obj::OnReadyStateChange()
{
long lState = 0;
m_pDoc->get_readyState(&lState);
if(lState
== READYSTATE_COMPLETE)
{
AtlUnadvise(m_pDoc,
__uuidof(XMLDOMDocumentEvents), m_dwCookie);
m_dwCookie = 0;
// ロード終了時の処理をここに記述
}
return S_OK;
}
|
リスト7 クライアントアプリケーション
#import "..\sample04ATL\Debug\sample04ATL.dll"
raw_interfaces_only
using namespace SAMPLE04ATLLib;
ISample04ObjPtr pObj;
pObj.CreateInstance(__uuidof(Sample04Obj));
pObj->loadASync(L"http://www.mimimomi.org/shinryouin.xml");
|
(注1) アプリケーションのEXE内にATLオブジェクトを追加する方法もありますが、次の点に留意してください。MFCプロジェクトにATLオブジェクトを追加した場合にエラーが発生することが報告されています。イベント処理用のATLオブジェクトを作成する場合、このエラーは無視して再度ATLオブジェクトを作成すればビルドできる状態になります。
http://support.microsoft.com/support/kb/articles/Q198/5/37.ASP
「BUG: "Sorry! An error occurred when generating the object."」を参照してください。
(注2)FinalConstruct関数は仮想関数ではないので、「オーバーライド」という表現は正確ではありません。しかしATLの実装では、この名前の関数を実装すれば、仮想関数をオーバーライドしたのと同様に呼び出されるようになっています。
(注3)GetUnknown関数はMSDNライブラリでは明記されていないものの、多くのサンプルで使用されています。
(注4)#importを使わずに、ATLコンポーネントをビルドしたときに作られるヘッダファイルとlibファイルを使用することもできます。ATLコンポーネントのプロジェクト名がsample04ATLであれば、ヘッダファイルがsample04ATL.h、libファイルがsample04ATL.libです。
関連サービス
IT技術およびIT製品の可用性調査・検証業務
関連キーワード: MSXML
関連キーワード: プログラミング
|