EternalWindows
ActiveX コントロール / メソッドとイベント

前節ではコントロールのインプレースアクティベーションを行い、 コントロールをコンテナのウインドウ上に表示しました。 しかし、今回対象となるコントロールはFlashであるため、 実際に何らかのファイルを再生しないことには、 Flashのウインドウが表示されているようには見えないでしょう。 この再生というのはFlash独自のメソッドになりますから、 コントロールが実装する標準的なインターフェース(IOleControlなど)には、 このようなメソッドは含まれていません。 それではどのようにして再生を行えばよいのかというと、オートメーションの仕組みを利用します。 つまり、IDispatchを通じてコントロールのメソッドを呼び出します。

コントロールが実装しているメソッドやプロパティは、タイプライブラリを通じて確認できます。 Flashのタイプライブリは'%windir%\system32\Macromed\Flash\Flash10b.ocxに存在すると思われるため、 これをOleView.exeで開けばよいでしょう。 OleView.exeは、%ProgramFiles%\Microsoft SDKs\Windows\v6.0\Bin以下に存在すると思われます。

FlashのコントロールはShockwaveFlashというクラスで表すことができ、 このクラスはIShockwaveFlashを継承しています。 このインターフェースはFlashを制御するためのメソッドやプロパティを持っており、 たとえばMovieという名前のプロパティを呼び出せば、再生したいファイルをFlashに渡すことができます。 また、Playというメソッドを呼び出せば実際にファイルを再生できます。 次に、これらをIDispatchで使用する例を示します。

HRESULT CActiveXContainer::PlayMovie(BSTR bstr)
{
	HRESULT   hr;
	VARIANT   var;
	IDispatch *pDispatch;
	
	m_pOleObject->QueryInterface(IID_PPV_ARGS(&pDispatch));
	
	var.vt = VT_BSTR;
	var.bstrVal = bstr;
	hr = Invoke(pDispatch, L"Movie", DISPATCH_PROPERTYPUT, &var, 1, NULL);
	if (FAILED(hr)) {
		pDispatch->Release();
		return hr;
	}

	hr = Invoke(pDispatch, L"Play", DISPATCH_METHOD, NULL, 0, NULL);

	pDispatch->Release();

	return hr;
}

Invokeという自作メソッドは、IDispatch::Invokeの呼び出しをラッピングしています。 第1引数は呼び出したいプロパティまたはメソッド名であり、 まずはファイルを設定するMovieを呼び出しています。 先に示した図から分かるように、Movieの第1引数にはBSTR型の変数を指定しなければならないため、 VARIANT.vtにVT_BSTRを指定することでbstrValを初期化します。 そしてこれをInvokeの第4引数に指定し、 引数が1個ということで第5引数には1を指定します。 Movieが成功したら次はPlayというメソッドを呼び出します。 Playは引数が存在しないため、第4引数と第5引数はNULLと0で問題ありません。 Playが成功した場合は、Movieで設定したファイルが再生されるはずです。

コンテナがコントロールからイベントを受信したい場合は、 IDispatchを実装したイベント受信用のオブジェクトが必要になります。 一般にこのオブジェクトはイベントシンクと呼ばれ、 今回のサンプルではCEventSinkという型を定義しています。 CActiveXContainerもIDispatchを実装しているため、 このオブジェクトがイベントシンクとして機能すればよいようにも思えますが、 これは不適切であると思われます。 CActiveXContainerのIDispatch ::Invokeは、コントロールにアンビエントプロパティを提示するために存在しているため、 このメソッドでイベントの受信を行うようにすると、どちらの目的でIDispatch ::Invokeが呼ばれたのかが分かりにくくなります。

Flashから送れるイベントを把握したい場合は、メソッドを調べるときと同じようにタイプライブラリを確認します。

_IShockwaveFlashEventsには、イベントとして通知されるメソッドが含まれています。 CEventSink::Invokeが呼ばれて、たとえば第1引数が0x96であればFSCommandに関する通知であることが分かります。 ただし、そのイベントがどのような意味を持つのか、 あるいはそのイベントが送られた際に何をすればよいのかは、 実際にコントロールのリファレンスなどを確認しなければ分からないでしょう。 これは、メソッドやプロパティの呼び出しにも共通することです。

コントロールのイベントの値が予め定義されている値に準拠している場合は、 イベントの意味を理解できる可能性があります。 たとえば、CEventSink::Invokeの第1引数がDISPID_DBLCLICKである場合は、 コントロールをダブルクリックした際の通知であると考えて問題ないでしょう。 コントロールの開発者は、通知の意味合いが既存の定数と一致する場合はそれを使用するべきであり、 現にOnReadyStateChangeのDISPIDはDISPID_READYSTATECHANGEという既存の定数に一致します。 このイベントは、コントロールの現在の状態を通知する目的で送られます。

STDMETHODIMP CEventSink::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
	if (dispIdMember == DISPID_READYSTATECHANGE) {
		int nState = pDispParams->rgvarg[0].iVal;

		if (nState == READYSTATE_UNINITIALIZED) // データは初期化されていない。
			MessageBox(NULL, TEXT("UNINITIALIZED"), TEXT("OK"), MB_OK);
		else if (nState == READYSTATE_LOADING) // データをロード中である。
			MessageBox(NULL, TEXT("LOADING"), TEXT("OK"), MB_OK);
		else if (nState == READYSTATE_LOADED) // データのロードを完了した。
			MessageBox(NULL, TEXT("LOADED"), TEXT("OK"), MB_OK);
		else if (nState == READYSTATE_INTERACTIVE) // 完全に初期化はされていないが、ユーザーとの対話は可能。
			MessageBox(NULL, TEXT("INTERACTIVE"), TEXT("OK"), MB_OK);
		else if (nState == READYSTATE_COMPLETE) // コントロールは完全に初期化された。
			MessageBox(NULL, TEXT("COMPLETE"), TEXT("OK"), MB_OK);
		else
			;
	}
	else {
		TCHAR szBuf[256];
		wsprintf(szBuf, TEXT("%x"), dispIdMember);
		MessageBox(NULL, szBuf, TEXT("Invoke"), MB_OK);
	}

	return S_OK;
}

DISPID_READYSTATECHANGEが指定された場合は、pDispParams->rgvarg[0]にコントロールの現在の状態が格納されています。 各情報の意味についてはコメントが示す通りであり、 READYSTATE_COMPLETEが指定された場合はコントロールを問題なく使用できることを意味します。


戻る