XPCOMは、Cross-Platform Component Object Model (クロス・コンポーネント・オブジェクト・モデル) の略です。XPCOMの目的は、多くのプラットフォームにまたがるモジュラー・ソフトウェア・オブジェクト (コンポーネント) を作成するためのメカニズムを提供することです。
このシリーズの前回までの記事 (「参考文献」を参照) では、XPCOMの初期化の方法とその既存コンポーネントの使用法について述べました。今回の記事と次回の記事では、コンポーネントの作成方法について説明します。
コードの作成を実際に開始できる程度にコンポーネントを定義するには、5つのステップが必要です。それぞれのXPCOMコンポーネントについて、以下の作業を行う必要があります。
- クラスID UUIDを生成する
- 各インターフェースごとにインターフェースUUIDを生成する
- コントラクトIDと上記のクラスIDとを関連付けるように指定する
- 使用するインターフェースをIDLに定義する
- そのIDLをコンパイルし、生成したスタブを使用してコンポーネントをインプリメントする
それでは、これらの5つのステップを詳しく見てみましょう。まず、クラスIDを生成します。GUIDGENまたは他のGUID/UUID生成ツールを使用してください。GUIDGENは、結果をクリップボードに入れるので、ユーザーがそれらの値を直接自分のIDLファイルに貼り付けでき、便利です。さらにUUID生成機能を使うことにより、次はステップ2です。コンポーネントがインプリメントする予定のインターフェースごとにもう1つずつUUIDを生成してください。ステップ3では、簡潔な、しかし意味をつかみやすい (または少なくとも記憶しやすい) コントラクトIDを作ります。コントラクトIDはコンポーネント名をテキストで記述したものであり、数値としては扱われません。ステップ4では、テキスト・エディターを開き、インターフェースID、クラスID、およびコントラクトIDを貼り付けてから、インターフェース定義を作成します。最後のステップでは、IDLファイルをXPIDLコンパイラーへ送ってその正確性を調べます。
コンパイラーは、すべてのタイプ・ライブラリー情報を含んだXPTファイルを、オプションのC++ スタブ・ファイルと一緒に作成します。IDLファイルがXPIDLコンパイラーをエラー無しでパスしたら、生成されたC++ スタブを調べて、IDLの指定時に意図した内容が、正しくC++ のコードに変換されていることを確認します。適切なC++ スタブのセットができたら、いよいよコーディングを開始することができます。
例として、XPCOMアプリケーションとグローバル・ポジショニング・システム・レシーバー (略称 "GPSレシーバー") の間のインターフェースとして使用できる架空のコンポーネントについて説明します。GPSレシーバーは、コンピューターに接続できるアクセサリーで、現在の時刻や地球上での現在位置、時としては標高などのいくつかの情報を提供します。
この架空のコンポーネントにもう少し具体的なイメージを持たせるため、あなたはクロス・プラットフォーム用の便利な地図表示ツールを持っていて、画面上で2次元の地図データをサイズ変更したり回転したりできることにしましょう。この地図データは、SVGと呼ばれるXML形式のベクトル・リストで示され、プログラミング・ロジックと画面レイアウトは、XULを使ってインプリメントされています(ヒント: XULがMozillaブラウザーをベースにしていて、必ずMozillaと一緒に使用します。そう言えば、この地図データがXPCOMコンポーネントで使用できることはすぐわかりますね。)あなたの任務は、GPSレシーバーで受け取ったリアルタイムの位置データを入力に使って、このツールを動く地図の表示に変えることです。もう少し状況を複雑にするために、それぞれが独自のプロトコルを持つ3つの異なるタイプのGPSレシーバーをサポートしなければならないことにしましょう。この3つのプロトコルの実際に使用されている例については、「参考文献」を参照してください。
XULは、XMLベースのメソッドで、ユーザー・インターフェースの指定に使用します。特定のオペレーティング・システムのGUI専用に、パネル、ボタン、テキスト・ラベル、チェック・ボックス、リストなどを作成したり、それらをツリー表示で制御したりする固有なコードをいちいち作成する代わりに、各種のGUIエレメントのレイアウト指示をするXMLタグの入ったテキスト・ファイルを作成すればいいのです。Mozilla GUIエンジンの利点を活用してMozillaベースのアプリケーションを作成しようとすると、設計の一部にXULを使用することになります。
では、このコンポーネントに対する戦略として、共通インターフェースを作ってから、さまざまなタイプのGPSレシーバーのそれぞれについて別個にインプリメンテーションを作成することにしましょう。それぞれのインプリメンテーションは、この1つの共通インターフェースを共用します。つまり、地図アプリケーションのコードは、このインターフェースを使用することで、実際に接続されている装置の仕様を知る必要がなくなります。
新しいコンポーネント・プロジェクトのディレクトリーは、nsSample コンポーネントと別のディレクトリーに入れておいた方が良いでしょう。(このシリーズの第3回記事「lizardの作成」に説明されているMozillaソースのインストールと作成に関するセクションも参照してください。)
わたしのラップトップでは、nsSample コンポーネントのディレクトリーは/mozilla/xpcom/sample です。そこで、このGPSの例には、/mozilla/xpcom/gps というパスの新規のディレクトリーを作成しました。3つのインプリメンテーションは、すべて同じモジュール内にコーディングする予定なので、必要なディレクトリーは1つだけです。リスト1は、GPSレシーバーのモデル定義に使用するインターフェースを示しています。
リスト1. GPSレシーバーをモデル定義に使用するサンプル・インターフェース
[scriptable, uuid(1BDC2EE0-E92A-11d4-BCC0-0060089296CB)]
interface mozIGPS : nsISupports
{
boolean Open(in string strDevice);
boolean Close();
string Reason(in boolean bClear);
readonly attribute double latitude;
readonly attribute double longitude;
readonly attribute double elevation;
readonly attribute double gpstime;
};
|
IGPSインターフェースのインターフェースIDが直接IDLファイルにコーディングされています。XPIDLでは、'%' タグを使用してテキストを直接C++ 生成コードに挿入できますので、この技法を使えば、念のため、選択したクラスIDとコントラクトIDを同一IDLソース・ファイルに入れておくことができます。
XPIDLコンパイラーはリスト1のIDLテキストをXPTファイルとC++ ヘッダー・ファイルに変換します。C++ ヘッダー・ファイルには、#if 0 ...#endif でコメント化されたスタブC++ クラス定義が入っています。この時点で、するべきことがいくつかあります。
- スタブを別の .cppにコピーします。これがインプリメンテーション・ファイルのベースになります。
- 空に近いクラス宣言を持つC++ ヘッダー・ファイルを作成します。このヘッダー・ファイルは、XPIDLによって生成されたC++ ヘッダー・ファイルで定義されたインターフェースからパブリックに継承したものです。
- エディット中のファイルに、生成されたファイルの
#includeを必ず追加してください。インプリメンテーション・ファイルには、ここでエディットしたヘッダー・ファイルを#includeする必要があります。
生成されたヘッダー・ファイルをエディットしないでください。IDLソースを変更すると、消去されてしまうからです。安全に編集できる自分用のヘッダー・ファイルを別個に作成するのは、この理由からなのです。読んでわかるとおり、この時点までは、すべてのものがXPIDLコンパイラーによって生成されます。これ以降、スタブされたメンバー機能を実際に動くコードで埋めていく作業は、プログラマーが行います。リスト2 は、生成されたコードを示しています。.
リスト2で生成されたファイルを見ると、NS_IMPL_ISUPPORTS マクロが、QueryInterface、AddRef、およびRelease をインプリメントしているのがわかります。もちろん、わたしたちはまだぜんぜんコードを書いていませんから、コンパイラーが代りに書いたというわけです。もうひとつ注目すべきポイントは、IDLコンパイラーによってOpen およびClose メソッドにIDL定義がマップされた内容と、属性Latitude およびLongitude がアクセス機能メソッドGetLatitude およびGetLongitude にマップされた内容です。いずれの場合も、スタブ・インプリメンテーションはNS_ERROR_NOT_IMPLEMENTED を戻しているだけです。
XPIDLで生成されたテキストの#if 0 から#endif までを、別のファイルにコピーしてインプリメンテーション・ソース・ファイルにすることができます。このコンポーネントが内部で行なっている処理についての情報を他の関連コンポーネントと共用する必要がなければ、クラス宣言をインプリメンテーション・ソース・ファイル内に残しておいてかまいません。つまり、インプリメンテーション・ファイル内で、IDL生成のヘッダー・ファイルのincludeをし、その直後に、ヘッダー・ファイル内のC++ クラスから継承したC++ クラスを定義します。他のC++ クラスがインプリメンテーション・クラスの内部処理について知る必要がある場合は、そのクラスのC++ ヘッダー・ファイルにインプリメンテーション・クラス定義をコピーします。それとは別に、幾つかの#define シンボルをインプリメンテーションのソース・ファイルに追加し、コントラクトID、クラスID、およびオプションのクラス名を定義することになります。
この記事では、同一インターフェースに3つの異なるインプリメンテーションを作成する方法を説明していますので、それぞれのインプリメンテーションには独自のクラスIDとコントラクトIDが必要になります。その例は、リスト3 をご覧ください。
リスト4 は、この3つのインプリメント・クラスに対応する1つのヘッダー・ファイルの例です。このコードでしていることは、XPIDL生成ヘッダー・ファイルをincludeして、この3つのインプリメント・クラスを定義することだけです。それぞれのインプリメント・クラスは、コンストラクターと仮想デストラクターを定義し、2つのマクロを拡張します。NS_DECL_ISUPPORTS およびNS_DECL_MOZIGPS マクロは、実際に、各クラスがnsISupports およびmozIGPS インターフェースをインプリメントすることを宣言します。
この時点では、実際のインプリメント・コードが作成されていないため、3つのクラスのそれぞれが、リスト5に示されているような同じスタブ・インプリメンテーションを使用します。
リスト5. インプリメンテーション・ファイル
#include "mozGPS.h"
/* NMEA Implementation */
NS_IMPL_ISUPPORTS1(mozGPSNMEAImpl, mozIGPS)
mozGPSNMEAImpl::mozGPSNMEAImpl()
{
NS_INIT_ISUPPORTS();
/* member initializers and constructor code */
}
mozGPSNMEAImpl::~mozGPSNMEAImpl()
{
/* destructor code */
}
/* boolean Open (in string strDevice); */
NS_IMETHODIMP mozGPSNMEAImpl::Open(const char *strDevice, PRBool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* boolean Close (); */
NS_IMETHODIMP mozGPSNMEAImpl::Close(PRBool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute double latitude; */
NS_IMETHODIMP mozGPSNMEAImpl::GetLatitude(double *aLatitude)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute double longitude; */
NS_IMETHODIMP mozGPSNMEAImpl::GetLongitude(double *aLongitude)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute double elevation; */
NS_IMETHODIMP mozGPSNMEAImpl::GetElevation(double *aElevation)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute double gpstime; */
NS_IMETHODIMP mozGPSNMEAImpl::GetGpstime(double *aGpstime)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* string Reason (in boolean bClear); */
NS_IMETHODIMP mozGPSNMEAImpl::Reason(PRBool bClear, char **_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
|
これで、3つのコンポーネントをインプリメントする完全なXPCOMモジュールができ上がりました。これらのコンポーネントを実際に使用できるコンポーネントに仕上げるために残された作業は、return NS_ERROR_NOT_IMPLEMENTED; ステートメントを実際のコードと置き換えることだけです。
わたしたちはこれまでに多くの領域をカバーしてきました。まもなく最終目的地に到着します。すでに、サンプル・コンポーネント用に問題のスペースを定義し、インターフェースのドラフトを作成し、実際にコーディングも開始しました。このシリーズの最終回になる次回の記事では、「構築可能な」コンポーネントを作成するコーディングを完成させ、その結果のデバッグ方法について検討します。
- この記事は、XPCOMに関する連載5回シリーズの第4回です。他の回は、以下のとおりです。
-
Trimble TSIP GPSプロトコル ダウンロード
-
Trimble TAIP GPSプロトコル マニュアル
-
NMEAプロトコル
-
XMLベースのユーザー・インターフェース言語 (XUL)
-
SVG (Scalable Vector Graphics)
Rick Parrish氏は、高校生の頃からコンピューター・サイエンスに興味を持ち始めましたが、エレクトロニクスにはもっと早くから関心を持っていました。最初は電気工学の教育を受けていましたが、ソフトウェアの場合は、ハードウェアとは違って、塩化第二鉄溶液の嫌な臭いをかぐ必要はなく、ただ設計変更のためだけに指にやけどをする危険を冒す必要もないことに気付きました。Rickは、長い間C/C++ のプログラミングに携わってきましたが、VBとDelphi(Pascal) による仕事も多く、アセンブリー言語の仕事も少なくありません。今でも、熱いハンダを必要とする1つか2つのプロジェクトに何とか携わっています。Windows 2000は素晴らしいけれど、Ogg Vorbisもクールだし、IPTableのあるLinux 2.4カーネルは本当にタフだ! というのが彼の現在の持論です。現在の関心事は、ソフトウェア・モデリング設計用のツールを開発するオープン・ソース・プロジェクトを始めることです。Rickの連絡先は、 rfmobile@swbell.net です。