C++BuilderによるActiveX / ActiveForm
ActiveFormはActiveXコントロールの拡張であり、それ自身でウインドウハンドルを持っている。これによってWebブラウザに複雑なコントロールを持ったActiveFormを貼りつけることが可能である。
C++Builderによって作成できるActiveFormは通常のフォームデザイナを使用してアプリケーションの作成と同じ手順で 行うことができる。
■ ActiveFormの作成準備
新規作成からActiveFormを選択する。ウィザードが開くので、適切なオプションをチェックする。
生成されるコードは、TActiveFormクラスと、ActiveXとしてインプリメントするためのラッパークラスである。
クライアントへ提供するActiveXのインターフェースはラッパークラスがパブリッシュしている。
ActiveFormはTFormをラップする形で作られ、フォーム上にコントロールを任意に配置できる。ラッパーからFormへアクセスするためには m_VclCtl というオブジェクト名を使用する。
プロジェクトマネージャを開いて、ooooooo.tlbをダブルクリックするとタイプライブラリエディタが表示される。
プロパティ、メソッド、イベントの追加はこのエディタを使用する。エディタを開いて見ると、2種類のインターフェースを持っていることが解る。
ひとつはプロパティとメソッドにためのインターフェースで、もうひとつはイベントのためのインターフェースであ
る。
タイプライブラリエディタで編集を行った後は、ソースコードの更新ボタンを押してアップデートさせる。
■ プロパティの追加手順
ActiveXのプロパティはBuilderのように__Propertyがないので、get、setを使ってアクセスされる。このためプロパティを追加すると二つのメソッドが追加されることになる。
(1)タイプライブラリエディタで、プロパティの追加ボタンを押す。
(2)プロパティはget用とset用の二つのメソッドができる。ここで名前を編集する。(MyProp)
(3)プロパティの型を指定する。
(4)戻り値は標準インターフェースの場合、HRESULTである。
(5)ActiveFornクラスへプロパティに対応する変数を宣言する。
long
FMyProp;
__Property long MyProp={read=FMyProp,write=FMyProp};
(6)ラッパークラスにはget、setメソッドのスケルトンが作られているので、コードを実装する。
STDMETHODIMP
TActiveXXXImpl::get_MyProp(long* Value)
{
try
{
*Value=m_VclCtl->MyProp;
}
catch(Exception &e)
{
return Error(e.Message.c_str(),
IID_IActiveXXX);
}
return S_OK;
};
STDMETHODIMP
TActiveXXXImpl::set_MyProp(long Value)
{
try
{
m_VclCtl->MyProp=Value;
}
catch(Exception &e)
{
return Error(e.Message.c_str(),
IID_IActiveXXX);
}
return S_OK;
};
青=スケルトン、赤=実装コード。
プロパティの追加は以上のように簡単に行える。ただしAnsiString型などのBuilder特有の型は使えないので、実装コードで対応しなければならない。
■ メソッドの追加
メソッドの追加は基本的にプロパティの追加と同じ様なものである。ActiveXのメソッドの戻り値はHRESULTにしなければならないので、戻り値が必要な場合は結果を入れる変数を同時に 渡すことになる。もしくはプロパティに出力する。
(1)タイプライブラリエディタを使用してメソッドの追加ボタンを押す。
(2)メソッドはひとつだけ追加されるので、ここで名前を編集する。
(3)メソッドの型を指定する。型はコンボボックスで指定できる。
(4)戻り値の型を指定する。標準インターフェースではHRESULTである。
(5)ActiveFormクラスへ対応するメソッドを追加する。
public:
void __fastcall MyMethod(void);
(6)ラッパークラスにメソッドのスケルトンが作られているので、コードを実装する。
STDMETHODIMP
TActiveXXXImpl::MyMethid(void)
{
try
{
m_VclCtl->MyMethod();
}
catch(Exception &e)
{
return Error(e.Message.c_str(),
IID_IActiveXXX);
}
return S_OK;
};
メソッドの追加は以上である。
■ イベントの追加
イベントはイベント用インターフェースへメソッドを追加した後、ラッパークラスへコードを追加することで完了 する。イベントしてして設定するメソッドの戻り値はvoidである。
(1)ActiveFormクラスにVCL用のイベントを定義する。
typedef void __fastcall (__closure *TDeviceReadEvent)(System::TObject *Sender,int Address,int &Data);
private:
TDeviceReadEvent FOnRead;
//ポインターを入れる変数を定義。
public:
__property TDeviceReadEvent OnRead={read=FOnRead,write=FOnRead};
//プロパティとして定義。
(2)ラッパークラスへクライアント用のハンドラを呼び出すメソッドを定義する。
void __fastcall ReadEvent(TObject *Sender,int Address,int &Data);
(3)このメソッドをActiveFormクラスのイベントハンドラとして接続する。
ラッパークラスの InitializeControl
メソッドの中に追加す
る。
public:
void InitializeControl()
{
m_VclCtl->OnActivate = ActivateEvent;
m_VclCtl->OnClick = ClickEvent;
m_VclCtl->OnRead
= ReadEvent;
}
(4)定義したメソッドを実装する。スケルトンは生成されないので、手でコードを追加する。
void
__fastcall TActiveXXXImpl::ReadEvent(TObject *Sender,int
Address,int &Data)
{
int TempData;
TempData=Data;
Fire_OnRead(Address,&TempData);
Data=TempData;
}
クライアントのイベントハンドラの呼び出しは Fire_
で修飾されたメソッドを呼び出すことで行われる。
このメソッドの定義はタイプライブラリのヘッダファイルに存在している。
(5)イベントの呼び出し
ユーザーのイベントハンドラのポインターはActiveFormクラスで定義したプロパティに入っている。
例ではOnReadである。従ってイベント発生させる場合はこのポインターを使用する。
if(
OnRead )
{ OnRead(this,Address,*Data);
}
パラメータは宣言した型に従って渡したい値を入れる。
ユーザーによるイベントハンドラの実装は必須ではないので、未定義の場合にアクセス例外を出さないために
if
によってポインタの設定を確認する。
■ コンポーネントパレットへのアップデート
VCLをラップしたActiveXをさらにVCLでラップするのは変な話だが、コンポーネントメニューでActiveXの取り込み を選ぶとコンポーネントパレットへ登録できる。ただ困ったことに、すでにコンポーネントパレットへ登録されている場合に、新しく追加したプロパティやメソッド がBuilderに反映されないのだ。何らかの方法があると思うのだが、今のところパッケージから一旦削除し、再度インスト ールすることで対応している。これは結構面倒なので解決方法を探ろうと思う。