EternalWindows
Silverlight / Silverlightのホスト

今回は、Silverlightを実際にホストする方法について説明します。 前節で述べたように、Silverlightの正体はActiveXコントロールであるため、 ActiveXコントロールのコンテナの実装方法を理解していれば、 Silverlightのホストはそれほど難しいことではありません。 次に示すメソッドは、Silverlightの作成からインプレースアクティベーションまでを行います。

BOOL CXcpControlHost::Create()
{
	RECT              rc;
	HRESULT           hr;
	IOleInPlaceObject *pOleInPlaceObject;

	hr = CoCreateInstance(CLSID_XcpControl, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pOleObject));
	if (FAILED(hr))
		return FALSE;

	m_pOleObject->SetClientSite(static_cast<IOleClientSite *>(this));
	InitNew();

	SetRectEmpty(&rc);
	hr = m_pOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, static_cast<IOleClientSite *>(this), 0, m_hwnd, &rc);
	if (FAILED(hr))
		return FALSE;

	m_pOleObject->QueryInterface(IID_PPV_ARGS(&pOleInPlaceObject));
	SetRect(&rc, 100, 100, 400, 400);
	pOleInPlaceObject->SetObjectRects(&rc, &rc);
	pOleInPlaceObject->Release();

	return TRUE;
}

まず、CoCreateInstanceを呼び出してSilverlightのCOMオブジェクトを作成します。 このとき、第1引数にはSilverlightのCLSIDを指定し、第3引数はCLSCTX_INPROC_SERVERを指定します。 オブジェクトをIOleObjectで識別したら、SetClientSiteを呼び出して自身のアドレスをSilverlightに渡します。 これが終わればInitNewという自作メソッドでオブジェクトの初期化し、 初期化が完了したらDoVerbを呼び出してインプレースアクティベーションを行います。 これにより、Silverlightのウインドウはホストアプリケーション上に表示されます。 DoVerbのRECT構造体では位置を決定できないようなので、 IOleInPlaceObject::SetObjectRectsで明示的に位置を決定するようにします。 なお、ActiveXコントロールのホストではGetMiscStatusを呼び出して情報ステータスを取得していましたが、 Silverlightの場合はGetMiscStatusで常に同じ値が返るため、動的に考慮するようにはしていません。

Silverlightを初期化するというのは、Silverlightに特定のプロパティをロードさせることを意味します。 SilverlightはIPersistPropertyBagを実装しているので、これのLoadメソッドを呼び出すことになります。

BOOL CXcpControlHost::InitNew()
{
	IPersistPropertyBag *pPersistPropertyBag;
	IPropertyBag        *pPropertyBag = new CPropertyBag;
	
	m_pOleObject->QueryInterface(IID_PPV_ARGS(&pPersistPropertyBag));
	pPersistPropertyBag->Load(pPropertyBag, NULL);
	pPersistPropertyBag->Release();

	return TRUE;
}

Silverlightは、Loadの第1引数に指定されたオブジェクトからプロパティを取得しようとします。 プロパティの取得はIPropertyBag::Readを通じて行われるため、 CPropertyBagはIPropertyBagを実装しておくことになります。

STDMETHODIMP CPropertyBag::Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog)
{
	VARIANT var;
	BSTR bstr;

	if (lstrcmpW(pszPropName, L"Source") == 0) {
		pVar->vt = VT_BSTR;
		pVar->bstrVal = SysAllocString(L"sample.xaml");
	}
	else if (lstrcmpW(pszPropName, L"Background") == 0) {
		pVar->vt = VT_BSTR;
		pVar->bstrVal = SysAllocString(L"gray");
	}
	else if (lstrcmpW(pszPropName, L"Windowless") == 0) {
		pVar->vt = VT_BOOL;
		pVar->boolVal = VARIANT_FALSE;
	}
	else
		return E_FAIL;

	return S_OK;
}

取得されることになるプロパティは上記の3つです。 Sourceは、使用するXAMLファイルまたはxapファイルを指定します。 Backgroundは、背景色の色を指定します。 Windowlessは、親ウインドウにSilverlightのウインドウを表示しないかどうかです。 通常はVARIANT_FALSEを指定し、ウインドウが表示されるようにします。

Silverlightをホストするアプリケーションは、Silverlightから要求に応えるために、 一定のインターフェースを実装したクラスを定義しなければなりません。 今回の場合、CXcpControlHostがそれであり、 このクラスはIOleClientSite、IOleInPlaceSite、IDispatch、IServiceProvider、IXcpControlHost2を継承するようにしています。 この内、最初の3つのクラスについては、ActiveXコントロールの章で取り上げた内容と同じであるため、詳しくはそちらを参照してください。 IServiceProviderはIXcpControlHost2を照会する目的で呼ばれるため、 必ずIXcpControlHost2を返すようにします。 IXcpControlHost2は、Silverlightの動作に関する通知を受け取るインターフェースであり、 想像できるようにIXcpControlHostを継承しています。 よって、クラスはIXcpControlHostのメソッドとIXcpControlHost2のメソッドを実装することになります。

STDMETHODIMP CXcpControlHost::GetBaseUrl(BSTR* pbstrUrl)
{
	int   nLen;
	WCHAR szCurrentDirectory[MAX_PATH];

	GetCurrentDirectoryW(MAX_PATH, szCurrentDirectory);

	nLen = lstrlenW(szCurrentDirectory);
	szCurrentDirectory[nLen] = '\\';
	szCurrentDirectory[nLen + 1] = '\0';
	
	*pbstrUrl = SysAllocString(szCurrentDirectory);

	return S_OK;
}

GetBaseUrlは、ホストアプリケーションのカレントディレクトリを返すことになります。 Silverlightで扱われるファイルは全てこのパスをベースにするため、 必ず存在するディレクトリを返さなければなりません。

STDMETHODIMP CXcpControlHost::NotifyError(BSTR bstrError, BSTR bstrSource, long nLine, long nColumn)
{
	return S_OK;
}

NotifyErrorは、Silverlight内で何らかのエラーが発生した場合に呼ばれます。 第1引数はエラーの番号を示した文字列であり、第2引数はエラーの原因となったソースが格納されます。 たとえば、存在しないXAMLファイルをIPropertyBag::Readで返した場合は、 そのXAMLファイルの名前が第2引数に指定されます。

STDMETHODIMP CXcpControlHost::GetHostOptions(DWORD* pdwOptions)
{
	*pdwOptions = 0;

	return S_OK;
}

GetHostOptionsは、ホストアプリケーションのオプションを取得する目的で呼ばれます。 0を返せば問題ありませんが、たとえばXcpHostOption_DisableScriptCallouts(0x80)を返すと、 Silverlight内でイベントが発生しなくなります。

STDMETHODIMP CXcpControlHost::NotifyLoaded()
{
	return S_OK;
}

NotifyLoadedは、Silverlightが何らかのファイルをロードした場合に呼ばれます。 今回は特に行うことがないので、S_OKを返すだけにしています。

STDMETHODIMP CXcpControlHost::GetControlVersion(UINT *puMajorVersion, UINT *puMinorVersion)
{
	*puMajorVersion = 3;
	*puMinorVersion = 0;

	return S_OK;
}

GetControlVersionは、インストールされているSilverlightのバージョンを返すようにします。

IXcpControlについて

今回のサンプルではSilverlightをIOleObjectで識別していますが、 SilverlightはIXcpControlというインターフェースも実装しています。 このインターフェースのput_Sourceを呼び出せば、XAMLファイルをSilverlightにロードさせることができます。

IXcpControl *pXcpControl;

m_pOleObject->QueryInterface(IID_IXcpControl, (void **)&pXcpControl);
pXcpControl->put_Source(L"sample.xaml");
pXcpControl->Release();

上記のような呼び出しを行った場合、IPropertyBag::ReadでSourceを返す必要はなくなります。



戻る