2006 年 7 月 20 日 22 時 26 分

XMLHttpRequest を作成する


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


WSH では XMLHttpRequest が組み込みクラスではないので、
XMLHttpRequest を利用するためには、
少し面倒な手順を踏む必要がある。

Windows では XMLHttpRequest は、
MSXML ライブラリが提供する、COM オブジェクトである。

JScript (Microsoft の拡張した JavaScript) には、
ActiveXObject というクラスがあり、
COM オブジェクトをそのまま利用することができる。

ActiveXObject は、ProgID(プログラム ID)を元に、
COM オブジェクトを作成することができる。
なので、オブジェクトを作成するだけなら簡単だ。

しかし、MSXML には XMLHttpRequest クラスはない。
あくまでも IXMLHttpRequest というインタフェースであり、
IXMLHttpRequest の機能を持つクラスは幾つか存在する。
以下に その ProgID を列挙してみよう。

・Msxml2.XMLHTTP
・Microsoft.XMLHTTP
・Msxml2.ServerXMLHTTP
・Microsoft.ServerXMLHTTP

以上のクラスが、「XMLHttpRequest」として機能する。

COM の ProgID はバージョンに依存しないようになっており、
同じ ProgID を持つ複数のバージョンが共存可能である。

しかしながら、MSXML は上記の仕組みに準じず、
バージョン 2 で ProgID を変えてしまった。
Microsoft で始まるものは初期(バージョン 1)の実装だ。
Msxml2 で始まるものは(バージョン 2)以降なのだ。

そして、もう 1 つ ProgID に関わる問題もある。

ProgID を指定して COM オブジェクトを作成した場合は、
一般的に、環境で利用できる最新版が使用されるのだが、
MSXML では最新版が使用されるとは限らない。

俺の環境には、MSXML バージョン 6 が入ってるのだが、
ProgID ベースでオブジェクトを作成すると、
バージョン 3 のものがロードされるのである。

恐らく互換性を考慮した結果なのだろうが、
MSXML のバージョン管理の仕組みにも混乱があるようだ。

本末転倒なのだが、MSXML の最新版を利用するためには、
バージョンを明示した ProgID を利用した方が安全だ。
具体的には、「Msxml2.XMLHTTP.6.0」のような指定となる。

そして、それ以外にも話をややこしくしていることがある。

MSXML は XMLHttpRequest の実装を 2 種類用意している。
それは、「XMLHTTP」と「ServerXMLHTTP」だ。

XMLHTTP は、WinInet と呼ばれる、
従来から長く使われている API を基盤とした実装だ。
WinInet はキャッシュや Cookie の管理機能も提供するので、
Internet Explorer などをはじめとする、
数多くのプログラムは WinInet を利用している。

問題は、WinInet を使うプログラムが、
キャッシュや Cookie を共有していることだ。

XMLHTTP を使って自動化プログラムを作成した場合、
利用者が意図しないところで、
Cookie や履歴の情報を使用してしまう危険があるし、
逆に自動化プログラムがアクセスした情報が、
利用者の履歴として残ってしまう問題もある。
(まあ、ある程度は制御できるのだが)

ServerXMLHTTP は、比較的新しい
WinHTTP と呼ばれる API を基盤とする実装だ。
WinHTTP は、当初はサーバ間の HTTP 通信のために作成され、
WinInet よりもセキュリティ面で安全なように、
Cookie やキャッシュなどの情報は一切共有しない。

最近の Windows Update も WinHTTP を使っている。
修正プログラムなどのダウンロードは WinHTTP で行うので、
利用者のインターネット履歴や Cookie などに影響しない。

以上のように、MSXML 周りは混沌としている。


さて、そろそろ実際の利用を考えてみよう。

自動化プログラムを作るにあたっては、
幅広い環境で利用できるのが好ましい。

でも、Windows や Internet Explorer のバージョンによって
環境に存在する MSXML のバージョンはまちまちだ。

ただ、どのバージョンのどのオブジェクトも、
全く同じインタフェースを持っている事には変わりないので、
利用したい ProgID を用意し、
優先順位を決めて順番に試す

まず、自動化プログラムなので、
「ServerXMLHTTP」が存在するならそれを利用したい。
できれば最新のものが良い。
使えないなら「XMLHTTP」を利用する。
これも、できれば最新のものが良い。

以上をコードにするとこうなる。

========== XMLHttpRequest.js ==========

function XMLHttpRequest() {

    var progIDs = [
        "Msxml2.ServerXMLHTTP.6.0",
        "Msxml2.ServerXMLHTTP.5.0",
        "Msxml2.ServerXMLHTTP.4.0",
        "Msxml2.ServerXMLHTTP.3.0",
        "Msxml2.ServerXMLHTTP",
        "Microsoft.ServerXMLHTTP",
        "Msxml2.XMLHTTP.6.0",
        "Msxml2.XMLHTTP.5.0",
        "Msxml2.XMLHTTP.4.0",
        "Msxml2.XMLHTTP.3.0",
        "Msxml2.XMLHTTP",
        "Microsoft.XMLHTTP" // カンマなし
    ];

    var i;
    for (i = 0; i < progIDs.length; ++i) {
        try {
            return new ActiveXObject(progIDs[i]);
        } catch (e) {
            if (i == progIDs.length - 1) {
                throw e; // 全部失敗
            }
        }
    }
    // 到達しない
}

========== end of XMLHttpRequest.js ==========

progIDs 変数に、ProgID の配列を用意しておく。
ProgID は、試したい順に並べてある。
まずはバージョンを明記した ProgID を試し、
そして、バージョン非依存の ProgID、
そして、初期版の「Microsoft.~」の順に試すのだ、

ちなみに、大括弧に囲むのは配列リテラルの表記法であり、
[ ~ ] は、new Array(~) とほぼ同じである。
最後に余計なカンマを入れてしまうと、
値が undefined である余分な要素が追加されるので注意。

COM オブジェクトの作成に失敗したら例外が発生するので、
try ~ catch 構文で例外処理を入れておく。
最後の希望である、「Microsoft.XMLHTTP」もだめなら、
あきらめて例外を上位に伝達することにしよう。

では、以下の行を最後に入れて実行してみる。

var client = XMLHttpRequest();

オブジェクトが生成できた場合は、エラーは発生しないが、
ProgID がどれも利用できない場合は、
「オートメーション サーバーはオブジェクトを作成できません。」というエラーが発生する。

最近の環境なら、ServerXMLHTTP を持つ MSXML を含むので、
上記のコードはほぼ確実に成功するはずである。

長くなったので、XMLHttpRequest の利用は明日にしよう。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.