EternalWindows
ActiveX コントロール / コントロールとHTML

IEのようなブラウザ上で動作するコントロールを開発する場合は、 コントロールの情報がHTMLファイルでどのように記述されるかを理解しておく必要があります。 次に、コントロールの表示情報を記述したHTMLファイルの例を示します。

<html>
<title>ActiveX コントロール サンプル</title>
<body>
<p>ActiveX コントロールの表示ここから</p>

<object classid="clsid:E4A5C7BC-BDE7-43e8-96DC-98FEA02F1A18" width="320" height="240">
<param name="color" value="red">
</object>

<p>ActiveX コントロールの表示ここまで</p>
</body>
</html>

<object>から</object>までが、コントロールの情報になります。 classid属性にはコントロールのCLSIDを指定するようにし、 このコントロールがローカルコンピュータ上にインストールされていれば、 コントロールは表示されることになるはずです。 width属性とheight属性は必須ではありませんが、 これらを使用するとコントロールの初期サイズを決定できます。 paramタグにはコントロールに設定したいプロパティを指定することができ、 name属性には設定対象となるプロパティの名前、 value属性にはプロパティに設定する値を指定できます。 つまり上記のHTMLファイルの作成者は、 コントロールのcolorプロパティがredになった状態で表示されることを望んでいることになります。

タグに記述された情報がコントロールに反映されるために、 コンテナはコントロールの適切なメソッドを呼び出すことになります。 サイズが指定されていた場合は、IOleObject::SetExtentが呼ばれます。

STDMETHODIMP CActiveXControl::SetExtent(DWORD dwDrawAspect, SIZEL *psizel)
{
	SIZEL sizel = *psizel;
	RECT  rc;

	HIMETRICtoDP(&sizel);

	if (m_hwnd != NULL) {
		GetClientRect(m_hwnd, &rc);
		SetRect(&rc, rc.left, rc.top, rc.left + sizel.cx, rc.top + sizel.cy);
		MoveWindow(m_hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
	}
	else
		m_sizeInit = sizel;

	return S_OK;
}

SetExtentで指定されるサイズはHIMETRIC単位になっているため、 まずはHIMETRICtoDPという自作メソッドでデバイス単位に変換します。 既にウインドウが作成されている場合は、そのサイズを基にクライアント領域を調整すればよいのですが、 作成されていない場合はサイズを保存するようにします。 そして、IOleObject::GetExtentでこのサイズを返すようにします。

STDMETHODIMP CActiveXControl::GetExtent(DWORD dwDrawAspect, SIZEL *psizel)
{
	RECT rc;
	
	if (m_hwnd != NULL) {
		GetClientRect(m_hwnd, &rc);
		psizel->cx = rc.right - rc.left;
		psizel->cy = rc.bottom - rc.top;
	}
	else
		*psizel = m_sizeInit;

	DPtoHIMETRIC(psizel);

	return S_OK;
}

ウインドウが作成されていない時に返したm_sizeInitが、コントロールの初期サイズになります。 HTMLファイルでサイズを指定しなかった場合は、IOleObject::SetExtentでm_sizeInitが初期化されないことになりますが、 このm_sizeInitはコンストラクタで100に初期化されているため、サイズが指定されなかった場合でも問題なく動作します。 GetExtentではサイズをHIMETRIC単位で返さなければならないため、DPtoHIMETRICという自作メソッドを呼び出しています。

コントロールがparamタグに指定されたプロパティを取得したい場合は、IPersistPropertyBagを実装しておくことになります。 これにより、プロパティをロードすべき段階でIPersistPropertyBag::Loadが呼ばれます。

STDMETHODIMP CActiveXControl::Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog)
{
	VARIANT var;

	var.vt = VT_BSTR;
	pPropBag->Read(L"color", &var, NULL);

	if (lstrcmpW(var.bstrVal, L"red") == 0)
		m_crEllipse = RGB(255, 0, 0);
	else if (lstrcmpW(var.bstrVal, L"green") == 0)
		m_crEllipse = RGB(0, 255, 0);
	else if (lstrcmpW(var.bstrVal, L"blue") == 0)
		m_crEllipse = RGB(0, 0, 255);
	else
		m_crEllipse = RGB(255, 255, 255);
	
	m_nState = READYSTATE_COMPLETE;

	return S_OK;
}

引数として渡されるIPropertyBagは、1つの以上のプロパティを格納したプロパティバッグを識別しています。 IPropertyBag::Readを呼び出せばバッグからプロパティを取得できますが、 取得するデータの形式は常にVT_BSTRでなければならないように思えます。 paramタグのname属性にcolorが指定されていればIPropertyBag::Readは成功することになり、 value属性に指定していた文字列がvar.bstrValに格納されることになります。 paramタグが指定されなかった場合はIPropertyBag::InitNewが呼ばれます。

STDMETHODIMP CActiveXControl::InitNew(VOID)
{
	m_crEllipse = RGB(255, 255, 255);
	m_nState = READYSTATE_COMPLETE;

	return S_OK;
}

InitNewではIPropertyBagが渡されることはありませんから、プロパティはデフォルトの値で初期化することになります。 LoadにせよInitNewにせよ、制御を返した場合はIDispatch::Invokeでコントロールの状態が要求されるため、 ここでコントロールの状態をREADYSTATE_COMPLETEにしておきます。 これ以外の値を指定すると、IEのタブにはロード中であることを示すアイコンが表示され続けます。

コントロールがIPropertyBagを実装しており、さらにInitNewではなくLoadが呼び出す必要がある場合は、 IEによって次のダイアログが表示されることがあります。

IPropertyBag::Loadが呼ばれるということは、paramタグに指定された値がコントロールに渡されるということですが、 ここにはある1つの危険が入る余地があります。 それは、意図しない値でコントロールを初期化したことにより、 コントロールが不正な動作を行ってしまう可能性があるというものです。 もし、コントロールがどのようなデータを受け取っても適切に初期化できるのであれば、 コントロールは自身に対して安全マークを付けておくべきです。 これにより、IEはそのコントロールに値を渡しても問題ないと判断するため、 上記のようなダイアログが表示されることはなくなります。 ちなみに、上記のダイアログで「はい」を選択した場合はLoadが呼ばれ、 「いいえ」を選択した場合はInitNewが呼ばれます。

コントロールに安全マークを設定するには、IObjectSafetyを実装する方法とカテゴリマネージャを使用する方法がありますが、 推奨されているのは前者の方法です。 IObjectSafetyには、次に示す2つのメソッドが含まれています。

STDMETHODIMP CActiveXControl::GetInterfaceSafetyOptions(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
{
	*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA | INTERFACESAFE_FOR_UNTRUSTED_CALLER;
	
	if (IsEqualIID(riid, IID_IPersistPropertyBag))
		*pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
	else if (IsEqualIID(riid, IID_IDispatch))
		*pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
	else
		*pdwEnabledOptions = 0;

	return S_OK;
}

STDMETHODIMP CActiveXControl::SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
{
	if (dwOptionSetMask == 0 && dwEnabledOptions == 0)
		return S_OK;
	
	if (IsEqualIID(riid, IID_IPersistPropertyBag)) {
		if (INTERFACESAFE_FOR_UNTRUSTED_DATA == dwOptionSetMask && INTERFACESAFE_FOR_UNTRUSTED_DATA == dwEnabledOptions)
			return S_OK;
	}
	else if (IsEqualIID(riid, IID_IDispatch)) {
		if (INTERFACESAFE_FOR_UNTRUSTED_CALLER == dwOptionSetMask && INTERFACESAFE_FOR_UNTRUSTED_CALLER == dwEnabledOptions)
			return S_OK;
	}
	else
		;

	return E_FAIL;
}

GetInterfaceSafetyOptionsが呼ばれた場合は、まずpdwSupportedOptionsにサポートしているオプションを格納します。 ここで言うオプションとは、安全であるとマークできる情報のことであり、 今回の場合はINTERFACESAFE_FOR_UNTRUSTED_DATA(データの初期化)とINTERFACESAFE_FOR_UNTRUSTED_CALLER(スクリプトの実行)の2つになります。 IID_IPersistPropertyBagが指定された場合は、データの初期化が安全であるかを確認しようとしていることを意味するため、 pdwEnabledOptionsにINTERFACESAFE_FOR_UNTRUSTED_DATAを格納すればよいでしょう。 また、IID_IDispatchが指定された場合はスクリプトの実行の確認になるため、 INTERFACESAFE_FOR_UNTRUSTED_CALLERを格納するようにします。 SetInterfaceSafetyOptionsでは、戻り値を通じて安全であるかどうかを返します。 IID_IPersistPropertyBagが指定された場合は、 確認するオプションがINTERFACESAFE_FOR_UNTRUSTED_DATAでなければならないため、 dwOptionSetMaskとdwEnabledOptionsがこれと一致するかを調べ、 一致する場合に安全であることを示すS_OKを返します。

コントロールを作成したら、それが正しく動作するかを確認しなければません。 ローカルコンピュータでコントロールをテストする場合は、 コマンドプロント上で次の文字列を入力します。

regsvr32 C:\sample.dll (登録の解除時は regsvr32 /u C:\sample.dll のようにする)

これによりDLLのDllRegisterServerが呼ばれ、コントロールの情報がレジストリに書き込まれます。 この状態で既に示したHTMLファイルをIEにオープンさせれば、 IE上でコントロールが表示されることが確認できるはずです。 次節で取り上げるように、インターネット上からコントロールをダウンロードする場合は、 IEによってDllRegisterServerが呼ばれることになります。

DllRegisterServerでは、次に示すキーを作成しておく必要があります。

HKEY_CLASSES_ROOT
  CLISD
    <独自のCLSID> (既定) = コントロールの名前
      InprocServer32 (既定) = コントロールのフルパス
      InprocServer32 ThreadingModel = Apartment
      Control

独自のCLSIDのキーには、コントロールの名前を指定します。 ここで指定した名前は、たとえばIEの「アドオンの管理」から確認できます。 Controlというキーは必須ではありませんが、 このキーが作成されていれば、そのDLLがOLEコントロールまたはActiveXコントロールという目印になります。


戻る