EternalWindows
OLE埋め込み / IOleObjectの実装

前節では、サーバーがOLEサーバーとして動作するための設定を行いましたが、 こうした設定だけではコンテナがサーバーの機能を使用できるようにはなりません。 サーバーはオブジェクトを通じて自身の機能を公開するわけですが、 このオブジェクトがIOleObjectという既存のインターフェースを実装しなければ、 コンテナはオブジェクトを使用する手段がなくなるからです。 よって、オブジェクトはIOleObjectのメソッドを適切に実装することになります。

STDMETHODIMP CObject::SetClientSite(IOleClientSite *pClientSite)
{
	if (m_pClientSite != NULL)
		m_pClientSite->Release();

	m_pClientSite = pClientSite;

	if (m_pClientSite != NULL)
		m_pClientSite->AddRef();

	return S_OK;
}

SetClientSiteは、コンテナのサイトオブジェクトをオブジェクトに渡すために呼ばれます。 オブジェクトはサイトに対して通知を送ることになるため、引数は必ずメンバ変数に保存する必要があります。

STDMETHODIMP CObject::GetClientSite(IOleClientSite **ppClientSite)
{
	*ppClientSite = m_pClientSite;
	m_pClientSite->AddRef();

	return S_OK;
}

GetClientSiteは、保存されているサイトを第1引数に返すようにします。 既にサイトを持っているコンテナがこのメソッドを呼び出すことはないと思いますが、 外部からオブジェクトを取得したアプリケーションが呼び出す可能性はないとはいえません。 コンテナがIOleContainer::EnumObjectsを実装していれば、コンテナに埋め込まれたオブジェクトを取得できるからです。

STDMETHODIMP CObject::SetHostNames(LPCOLESTR szContainerApp, LPCOLESTR szContainerObj)
{
	int   i, nCount;
	HMENU hmenuPopupFile;
	WCHAR szBuf[256];

	hmenuPopupFile = GetSubMenu(m_hmenu, 0);
	nCount = GetMenuItemCount(hmenuPopupFile);

	for (i = 0; i < nCount; i++)
		RemoveMenu(hmenuPopupFile, 0, MF_BYPOSITION);

	wsprintf(szBuf, L"%sの更新", szContainerObj);
	InitializeMenuItem(hmenuPopupFile, szBuf, ID_UPDATE, NULL);
	InitializeMenuItem(hmenuPopupFile, TEXT("終了(&X)"), ID_EXIT, NULL);

	return S_OK;
}

SetHostNamesは、コンテナがオブジェクトに名前を設定する目的で呼ばれます。 この名前はオブジェクトの「ファイル」メニューの中に表示したいため、 まずは「ファイル」メニューの中に存在する項目を一旦削除します。 「ファイル」メニューは先頭に存在するため、GetSubMenuに0を指定すれば取得することができ、 GetMenuItemCountで取得した項目の数だけRemoveMenuを呼び出します。 削除が終了したら「オブジェクトの更新」という項目を追加し、 その後に「ファイル」メニューの既定の項目を追加します。

STDMETHODIMP CObject::Close(DWORD dwSaveOption)
{
	BOOL bDontSave;

	if (dwSaveOption == OLECLOSE_NOSAVE)
		bDontSave = TRUE;
	else
		bDontSave = FALSE;
	
	PostMessage(m_hwnd, WM_CLOSE, (WPARAM)bDontSave, 0);
	
	return S_OK;
}

Closeは、実行中のオブジェクトを休止状態にするために呼ばれます。 休止状態にするというのはプロセスを終了させることを意味するため、 WM_CLOSEをポストすることでウインドウを破棄させるようにします。 dwSaveOptionは、プロセスの終了に伴ってデータを保存するかどうかなどを表す定数が格納されます。 OLECLOSE_NOSAVEの場合はデータを保存する必要がないことを意味するため、 これをWM_CLOSEで確認できるようにWPARAMを使用しています。 dwSaveOptionにはその他の定数も指定される可能性がありますが、 基本的にはOLECLOSE_NOSAVEが指定されるはずなので、 これ以外はデータを保存するとみなしています。

STDMETHODIMP CObject::SetMoniker(DWORD dwWhichMoniker, IMoniker *pmk)
{
	return E_NOTIMPL;
}

SetMonikerは、リンクに対応するオブジェクトを実装する場合に意味を持ちます。 今回の場合はE_NOTIMPLを返すだけで構いません。

STDMETHODIMP CObject::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk)
{
	return E_NOTIMPL;
}

GetMonikerは、リンクに対応するオブジェクトを実装する場合に意味を持ちます。 今回の場合はE_NOTIMPLを返すだけで構いません。

STDMETHODIMP CObject::InitFromData(IDataObject *pDataObject, BOOL fCreation, DWORD dwReserved)
{
	return E_NOTIMPL;
}

InitFromDataは、引数のpDataObjectでオブジェクトを初期化させる目的で使用できますが、基本的に呼ばれることはないようです。 オブジェクトをクリップボードやD&Dから作成する場合は、OleCreateFromDataにIDataObjectを指定することになりますが、 OleUIInsertObjectで作成したオブジェクトには、InitFromDataで明示的にIDataObjectを指定します。

STDMETHODIMP CObject::GetClipboardData(DWORD dwReserved, IDataObject **ppDataObject)
{
	return E_NOTIMPL;
}

GetClipboardDataは、コンテナがオブジェクトのスナップショットを取得するために使用できますが、基本的に呼ばれることはないようです。 スナップショットというのは、いわばメニューからオブジェクトのコピーを選択して取得できるオブジェクトのことであり、 このオブジェクトの操作で元のオブジェクトに影響が出てはなりません。

STDMETHODIMP CObject::DoVerb(LONG iVerb, LPMSG lpmsg, IOleClientSite *pActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect)
{
	if (iVerb == OLEIVERB_OPEN || iVerb == OLEIVERB_SHOW || iVerb == OLEIVERB_PRIMARY || iVerb == 1) {
		m_pClientSite->ShowObject();
		if (lprcPosRect != NULL){
			RECT rc = *lprcPosRect;
			AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, TRUE, 0);
			SetWindowPos(m_hwnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
		}
		ShowWindow(m_hwnd, SW_SHOW);
		SetForegroundWindow(m_hwnd);
		m_pClientSite->OnShowWindow(TRUE);
	}
	else if (iVerb == OLEIVERB_HIDE) {
		ShowWindow(m_hwnd, SW_HIDE);
		m_pClientSite->OnShowWindow(FALSE);
	}
	else
		return E_FAIL;

	return S_OK;
}

DoVerbは、コンテナがオブジェクトに対して何らかの命令を与える際に呼ばれます。 iVerbがOLEIVERB_OPENやOLEIVERB_SHOW、OLEIVERB_PRIMARYである場合はオブジェクトのウインドウを表示すべきですから、 ShowWindowにSW_SHOWを指定することになります。 ただし、これに伴ってウインドウを表示することをコンテナに通知しなければならないため、 これから表示が始まることをShowObjectで通知し、表示が完了したことをOnShowWindowで通知します。 lprcPosRectがNULLでない場合は、そのサイズでウインドウを表示するべきであるため、 SetWindowPosで調整しています。 iVerbが1である場合というのは、コンテナがポップアップメニューから「開く」を選択した場合であり、 これもウインドウを表示して構いません。 OLEIVERB_HIDEが指定されることはあまりないと思われますが、 このときにはウインドウを非表示にします。

STDMETHODIMP CObject::EnumVerbs(IEnumOLEVERB **ppEnumOleVerb)
{
	return OLE_S_USEREG;
}

EnumVerbsは、コンテナがOleUIAddVerbMenuを呼び出した際に呼ばれます。 このメソッドでは、オブジェクトがサポートするVerbをIEnumOLEVERBとして返すことになりますが、 OLE_S_USEREGを返すようにすれば、OLEがレジストリの情報を基にVerbの情報を返してくれるようになります。

STDMETHODIMP CObject::Update()
{
	return E_NOTIMPL;
}

Updateは、オブジェクトを最新の状態に更新することになりますが、基本的に呼ばれることはないようです。 今回は実装しないということでE_NOTIMPLを返しています。

STDMETHODIMP CObject::IsUpToDate()
{
	return E_NOTIMPL;
}

IsUpToDateは、オブジェクトが最新の状態であるかを返すことになりますが、基本的に呼ばれることはないようです。 今回は実装しないということでE_NOTIMPLを返しています。

STDMETHODIMP CObject::GetUserClassID(CLSID *pClsid)
{
	*pClsid = CLSID_SampleObject;

	return S_OK;
}

GetUserClassIDは、オブジェクトのCLISDを返せば問題ありません。

STDMETHODIMP CObject::GetUserType(DWORD dwFormOfType, LPOLESTR *pszUserType)
{
	return OLE_S_USEREG;
}

GetUserTypeは、オブジェクトの名前を第1引数の形式で第2引数に返します。 戻り値がOLE_S_USEREGである場合は、レジストリに登録されている名前がOLEによって返されるため、 明示的に文字列を返す必要はなくなります。

STDMETHODIMP CObject::SetExtent(DWORD dwDrawAspect, SIZEL *psizel)
{
	return E_NOTIMPL;
}

SetExtentは、オブジェクトのエクステントサイズを第1引数のものに設定することになりますが、基本的に呼ばれることはないようです。 今回は実装しないということでE_NOTIMPLを返しています。

STDMETHODIMP CObject::GetExtent(DWORD dwDrawAspect, SIZEL *psizel)
{
	int nWidth, nHeight;

	GetSize(&nWidth, &nHeight);
	psizel->cx = nWidth;
	psizel->cy = nHeight;
	
	DPtoHIMETRIC(psizel);

	return S_OK;
}

GetExtentは、オブジェクトのエクステントサイズを第1引数に返します。 エクステントサイズはウインドウのクライアント領域のサイズで問題ありませんが、 これはHIMETRIC単位に変換していなければなりません。 よって、DPtoHIMETRICという自作メソッドで変換しています。

STDMETHODIMP CObject::Advise(IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
	if (m_pAdviseHolder == NULL)
		CreateOleAdviseHolder(&m_pAdviseHolder);

	return m_pAdviseHolder->Advise(pAdvSink, pdwConnection);
}

Adviseは、オブジェクトに対してアドバイズ接続を確立する目的で呼ばれます。 この場合はpAdvSinkをメンバとして保存し、必要に応じてpAdvSink->OnSaveなどで通知を送ることになるのですが、 上記ではpAdvSinkを保存せず、アドバイズホルダというオブジェクトを作成しています。 これは、アドバイズ接続を望むアプリケーションが複数存在する可能性があるからであり、 そうした複数のIAdviseSinkをアドバイズホルダで一括管理、及び一斉に通知を送れるようにしたいからです。 IOleAdviseHolder::Adviseを呼び出せば、pAdvSinkをアドバイズホルダに登録することができます。

STDMETHODIMP CObject::Unadvise(DWORD dwConnection)
{
	if (m_pAdviseHolder != NULL)
		m_pAdviseHolder->Unadvise(dwConnection);

	return S_OK;
}

Unadviseは、登録したアドバイズ接続を解除する目的で呼ばれます。 アドバイズ接続はdwConnectionで識別されており、これをIOleAdviseHolder::Unadviseに指定すれば解除が成立します。

STDMETHODIMP CObject::EnumAdvise(IEnumSTATDATA **ppenumAdvise)
{
	if (m_pAdviseHolder != NULL)
		m_pAdviseHolder->EnumAdvise(ppenumAdvise);

	return S_OK;
}

EnumAdviseは、現在確立されているアドバイズ接続を列挙するために使用できますが、基本的に呼ばれることはありません。 実装については上記で問題ないと思われます。

STDMETHODIMP CObject::GetMiscStatus(DWORD dwAspect, DWORD *pdwStatus)
{
	*pdwStatus = 0;

	return S_OK;
}

GetMiscStatusは、特定のアスペクトにおけるオブジェクトのステータスを返します。 このステータスというものは状況によって変化するものではなく、オブジェクトが存在している間は常に同じ値を返します。 返すことのできる値は豊富に定義されていますが、WordやExcelが0を返していることもあり、0を返すようにしています。

STDMETHODIMP CObject::SetColorScheme(LOGPALETTE *pLogpal)
{
	return E_NOTIMPL;
}

SetColorSchemeは、コンテナがオブジェクトにカラーセットを渡すために使用できますが、基本的に呼ばれることはないようです。 カラーセットはLOGPALETTE構造体のpalPalEntryに格納され、 第1エントリには前景色、第2エントリには背景色が格納されます。


戻る