EternalWindows
ActiveX コントロール / コンテナの実装

情報を参照する手段としてインターネットが使用される今日において、 より情報を見やすく分かりやすく提供することは重要な意味を持ちます。 通常、ユーザーはブラウザを通じてHTMLファイルを確認することになりますが、 情報を静的に記述するHTMLファイルでは、優れた表現を提供するには難しいものがあります。 このような問題を解決するために、ActiveXコントロールが何を行うのかというと、 ブラウザの一定の範囲に自作のウインドウを表示します。 ActiveXコントロールはブラウザにロードされるDLLであるため、 DLL内のコードはHTMLという枠に捉われず、自在にプログラミングすることができます。 つまり、作成したウインドウに図形を描画することもできますし、 タイマを使用してアニメーションを行うこともできます。 次に、ブラウザに表示されたActiveXコントロールの例を示します。

ActiveXコントロールを表示するブラウザならば、上記で文字がアニメーションしていることを確認できるはずです。 使用しているActiveXコントロールはAdobe社のFlashであり、 表示されているウインドウはFlashのDLL内で作成されたものであると考えて問題ないでしょう。 ウインドウ上で表示されるポップアップメニューが ウインドウ外の場所で表示されるポップアップメニューと異なるからも分かるように、 ActiveXコントロールはブラウザ上で自分自身のスペースを確保しているわけです。

ActiveXコントロールは、OLEにおける埋め込みという概念に基づいて作成されています。 コントロールが埋め込みをサポートしていれば、そのコントロールのイメージをコンテナ(IEなど)側で表示することができ、 そのイメージをクリックすれば、コントロールのウインドウを実際に表示できます。 しかし、コントロールは埋め込みの他にインプレースアクティベーションをサポートすることもでき、 この場合はコントロールのウインドウ(イメージではなく)が実際にコンテナ上で表示されます。 現に、上記のFlashはIEによってインプレースアクティベーションされています。 インプレースアクティベーションはいわば、OLEの埋め込みを発展させた形であるといえますが、 実際にはこのインプレースアクティベーションをさらに発展させたOLEコントロールと呼ばれる形も存在します。 OLEコントロールは、インプレースアクティベーションの他にIOleControlやオートメーション操作などをサポートし、 コンテナがコントロールをIDispatchで操作できる点が最大の特徴です。 そして、ここが特に重要な点なのですが、OLEコントロールとActiveXコントロールの意味合いは、ほとんど違いがありません。 OLEコントロールをインターネット上に配置し、さらにそれをブラウザ上で動作させた場合、 そのOLEコントロールはActiveXコントロールと呼ばれるのが一般的です。

上記の内容から分かるように、ActiveXコントロールを開発する場合はOLEコントロールに関するインターフェースの理解が必要になります。 また、OLEコントロールはインプレースアクティベーションをベースにしており、 さらにインプレースアクティベーションはOLEの埋め込みをベースにしているため、 これらの理解も必須であるといえるでしょう。 この2つについては独立した章で説明しているため、詳しくはそちらを参照するようにしてください。 本章の構成は、まず既存のActiveXコントロールを使用するコンテナの開発を行い、 その後でActiveXコントロールの開発について取り上げます。 既存のActiveXコントロールとしてはFlashを選択しているため、 最終的に作成されるコンテナはFlashの再生を目的にしています。

それではまず、ActiveXコントロールのコンテナが実装できるインターフェースを見ていきます。 ActiveXコントロールのコンテナはOLE埋め込みのコンテナでもあるため、 OLE関係のインターフェースも当然含まれることになります。

インターフェース 説明
IOleClientSite コントロールにコンテナのインターフェースを渡すために実装する。
IOleInPlaceSite インプレースアクティベーションをサポートするために実装する。
IOleControlSite コントロールのIOleControlに対応するために実装する。
IDispatch コンテナのアンビエントプロパティを公開するために実装する。
IOleContainer コンテナに埋め込まれたオブジェクトの列挙をサポートする場合に実装する。
IOleInPlaceFrame UIアクティベートをサポートする場合に実装する。

コンテナとサイトは、意味が違うことに注意してください。 サイトというのは実際にコントロールと通信するためのオブジェクトであり、 これは埋め込まれたコントロールの数だけ存在する可能性があります。 一方、コンテナは常に1つだけ存在するオブジェクトであり、 1つ以上のサイトを内包しています。 インターフェースの視点からいえば、IOleClientSiteからIDispatchまでがサイトのインターフェースであり、 IOleContainerからIOleInPlaceFrameまでがコンテナのインターフェースになります。 今回作成するサンプルでは、埋め込むコントロールが1つだけであることを前提としているため、 コンテナはサイトの役割も兼ねるようにしています。

IOleClientSiteやIOleInPlaceSiteの実装はOLE関連の章で取り上げているため、 今回はIOleControlSiteの説明を主に行います。 実際のところ、最低限必要になるインターフェースはIOleClientSiteとIOleInPlaceSiteのみであるため、 IOleControlSiteを理解する必要はありませんが、一応見ていきます。

STDMETHODIMP CActiveXContainer::OnControlInfoChanged()
{
	return E_NOTIMPL;
}

OnControlInfoChangedは、コントロールが検出を望んでいるアクセラレータ情報が変更された場合に呼ばれます。 この場合コンテナはIOleControl::GetControlInfoを呼び出して、新しいアクセラレータ情報を取得できますが、 今回のコンテナはアクセラレータの通知を行う設計になっていないので、 E_NOTIMPLを返しています。 もし、通知を行う場合は、入力されたキーがコントロールのアクセラレータと一致するかを調べ、 一致する場合にIOleControl::OnMnemonicを呼び出すようにします。

STDMETHODIMP CActiveXContainer::LockInPlaceActive(BOOL fLock)
{
	return E_NOTIMPL;
}

LockInPlaceActiveは、インプレースアクティベーションのロック状態を調整する場合に呼ばれます。 TRUEが指定された場合は、改めてFALSEが指定されるまでインプレースアクティベーションの解除を行ってはならないことを意味すると思われます。 今回はロックに対応しないということでE_NOTIMPLを返しています。

STDMETHODIMP CActiveXContainer::GetExtendedControl(IDispatch **ppDisp)
{
	*ppDisp = NULL;

	return E_NOTIMPL;
}

GetExtendedControlは、拡張コントロールを取得する際に呼ばれます。 通常、コントロールの使用はクライアント→コントロールという流れですが、 拡張コントロールを返すことで、クライアント→拡張コントロール→コントロールという流れを作ることができます。 これを満たすために拡張コントロールはコントロールを内包していなければならず、 クライアントからの要求を独自に拡張するか、あるいはコントロールに伝えなければなりません。 今回は拡張コントロールを実装しないということでE_NOTIMPLを返しています。

STDMETHODIMP CActiveXContainer::TransformCoords(POINTL *pPtlHimetric, POINTF *pPtfContainer, DWORD dwFlags)
{
	return E_NOTIMPL;
}

TransformCoordsは、コントロールの座標系をコンテナの座標系に変換したい場合、またはその逆を行いたい場合に呼ばれます。 dwFlagsにXFORMCOORDS_HIMETRICTOCONTAINERが含まれている場合は前者の変換を意味し、 pPtlHimetricにはコントロールの座標系がHIMETRIC単位で格納されています。 コンテナはこれを基に自身の座標系へ変換し、pPtfContainerに格納するようにします。 一方、dwFlagsにXFORMCOORDS_CONTAINERTOHIMETRICが含まれている場合は後者の変換を意味し、 pPtfContainerにはコンテナの座標系が格納されています。 コンテナはこれをコントロールの座標系(HIMETRIC単位)に変換し、pPtlHimetricに格納するようにします。 今回は、座標系の変換に対応しないということでE_NOTIMPLを返しています。

STDMETHODIMP CActiveXContainer::TranslateAccelerator(MSG *pMsg, DWORD grfModifiers)
{
	return E_NOTIMPL;
}

TranslateAcceleratorは、コンテナが定義するアクセラレータの入力をコントロールが検出した際に呼ばれます。 ただし、今回のコンテナはアクセラレータを定義していないため、このメソッドが呼ばれることはないはずです。

STDMETHODIMP CActiveXContainer::OnFocus(BOOL fGotFocus)
{
	return S_OK;
}

OnFocusは、コントロールのフォーカス状態が変更した場合に呼ばれます。 fGotFocusがTRUEである場合はコントロールがフォーカスを手に入れたことを意味し、 fGotFocusがFALSEである場合はフォーカスを失ったことを意味します。 特に行うことがない場合はS_OKを返すだけで構いません。

STDMETHODIMP CActiveXContainer::ShowPropertyFrame()
{
	return E_NOTIMPL;
}

ShowPropertyFrameは、コントールがプロパティダイアログを表示する段階になると呼ばれます。 プロパティダイアログを表示する段階というのは、主にコンテナがIOleObject::DoVerb(OLEIVERB_PROPERTIES)を呼び出した時であり、 ShowPropertyFrameがS_OKでない値を返した場合は、コントロールがダイアログを表示します。 この表示には、OleCreatePropertyFrameという関数が使用されます。 しかし、コンテナがダイアログに独自のページを表示したいと考えている場合は、 コンテナ自身がOleCreatePropertyFrameを呼び出すようにし、S_OKを返すようにします。 独自のページを表示するというのは、IPropertyPageを実装したオブジェクトを用意するということであり、 このオブジェクトのCLISDをOleCreatePropertyFrameに指定することで、独自のページが表示されるようになります。 OleCreatePropertyFrameには既存のページのCLISDも指定しなければなりませんが、 これはオブジェクトのISpecifyPropertyPages::GetPagesを呼び出せば取得できます。 ダイアログに独自のページを表示するつもりがない場合は、E_NOTIMPLを返せば問題ありません。

コンテナはコントロールにアンビエントプロパティを公開するために、IDispatch::Invokeを実装しておく必要があります。 アンビエントプロパティはコンテナの環境を定義しており、これをコントロールが取得することで、 コントロールの外観がコンテナに合せやすくなります。 たとえば、DISPID_AMBIENT_BACKCOLORでコンテナの背景色を取得したコントロールは、 自身の背景色をコンテナと同一にするようなことができます。 アンビエントプロパティとして定義されている定数は非常に多いため、 実装が必須とされているプロパティを取り上げます。

STDMETHODIMP CActiveXContainer::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
	if (dispIdMember == DISPID_AMBIENT_LOCALEID) {
		pVarResult->vt = VT_I4;
		pVarResult->lVal = (SHORT)GetThreadLocale();
	}
	else if (dispIdMember == DISPID_AMBIENT_USERMODE) {
		pVarResult->vt = VT_BOOL;
		pVarResult->boolVal = VARIANT_TRUE;	
	}
	else if (dispIdMember == DISPID_AMBIENT_DISPLAYASDEFAULT) {
		pVarResult->vt = VT_BOOL;
		pVarResult->boolVal = VARIANT_FALSE;	
	}
	else
		return DISP_E_MEMBERNOTFOUND;

	return S_OK;
}

DISPID_AMBIENT_LOCALEIDが指定された場合は、コンテナのローケルを返すようにします。 返すべきデータはVARIANT構造体に格納しなければならず、 データの型をVT_I4に指定した場合は、lValに返すべきデータ(ローケル)を格納するようにします。 DISPID_AMBIENT_USERMODEが指定された場合は、コンテナが現在設計モードである場合にVARIANT_FALSEを返し、 実行モードである場合にVARIANT_TRUEを返すようにします。 設計モードとはコントロールのウインドウの位置を決めたり、 イベントの関連付けを行ったりする段階のことであり、 実行モードとは実際にウインドウを表示した段階のことを意味します。 ただし、IEでは常にVARIANT_TRUEを返しているということもあり、 モードの区別を本当に行うべきかどうかなのはよく分かりません。 DISPID_AMBIENT_DISPLAYASDEFAULTが指定された場合は、 コントロールを枠付きで表示したい場合にVARIANT_TRUEを返し、 通常通りに表示したい場合にVARIANT_FALSEを返します。 たとえば、コンテナが2つのオブジェクトを埋め込んでいるとして、 その片方のオブジェクトをデフォルトで表示したい場合は、 そのオブジェクトに関連するサイトでVARIANT_TRUEを返すようにします。


戻る