第59回 派生元をCAxWindowからCAxHostWindowに変える (タブブラウザーを作る)

tabbrowser269.gif
今回から数回に分けてフォーカス関連のバグ修正をする。現状ではドロップダウンリストが正常に選択できなくなったり、アドレスバーにフォーカスを置いたままだとIEコントロールの描画が更新されないなどなどの不具合が多い。これらをなんとか治す。

これまで、ビューウインドウはATL/WTLアプリケーションウイザードの生成したCAxWindow派生のものを利用していた。このままだと修正が大変なので、派生元をCAxHostWindowに変更する。

CAxWindowはIEコントロールなどのActiveXコントロールをホストするための簡単な仕組みが備わっているのみだが、CAxHostWindowはかなり多くの実装がされていて便利なためだ。
class CTabBrowser100View : public CComObject<CAxHostWindow>	//■変更
	,public IDispEventImpl<SINKID_EVENTS, CTabBrowser100View, &DIID_DWebBrowserEvents2>
	,public CIEUtility
{

tabbrowser270.gif CAxHostWindowはCWindowImpl派生になっているため、このままではビューウインドウ内でメッセージマップが利用できない。そのためメッセージをCAxHostWindowよりも先に取得してビューウインドウに流仕込むためのウインドウCReflectWndを作成する。
	//■追加
	//CAxHostWindowの場合、メッセージハンドラが使えなくなるので
	//このウインドウをサブクラス化してメッセージハンドラが使えるようにする
	class	CReflectWnd : public CWindowImpl<CReflectWnd>
	{
		CTabBrowser100View*	_pView;
	public:
		CReflectWnd(CTabBrowser100View* pView)
		{
			_pView = pView;
		}
		BEGIN_MSG_MAP(CReflectWnd)
			CHAIN_MSG_MAP_MEMBER((*_pView))
		END_MSG_MAP()
	};

	CReflectWnd	_wndReflect;		//■追加

public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())	//CAxHostWindow::ではなくCAxWindow::のままなことに注意


	CTabBrowser100View(CTabView* pTabView)
		: _wndReflect(this)						//■追加
	{
		_pTabView = pTabView;
		_bPageError = false;
	}


	BEGIN_SINK_MAP(CTabBrowser100View)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW2, OnNewWindow2)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_STATUSTEXTCHANGE, OnStatusTextChange)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_DOWNLOADCOMPLETE, OnDownloadComplete)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_COMMANDSTATECHANGE, OnCommandStateChange)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN, OnDownloadBegin)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_PROGRESSCHANGE, OnProgressChange)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_PROPERTYCHANGE, OnPropertyChange)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_TITLECHANGE, OnTitleChange)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2, OnNavigateComplete2)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_NAVIGATEERROR, OnNavigateError)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_PRIVACYIMPACTEDSTATECHANGE, OnPrivacyImpactedStateChange)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW3, OnNewWindow3)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_SETSECURELOCKICON, OnSetSecureLockIcon)
	END_SINK_MAP()

	BEGIN_MSG_MAP(CTabBrowser100View)
		MESSAGE_HANDLER(WM_DNP_SCRIPTERROR, OnDnpScriptError)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		MESSAGE_HANDLER(WM_DNP_SHOWSCRIPTERROR, OnShowScriptError)
		MESSAGE_HANDLER(WM_DNP_SHOWPRIVACYREPORT, OnShowPrivacyReport)
		MESSAGE_HANDLER(WM_DNP_SHOWPSECURITYREPORT, OnShowSecurityReport)
		MESSAGE_HANDLER(WM_DNP_SHOWZONECONFIGURE, OnShowZoneConfigure)
		CHAIN_MSG_MAP(CAxHostWindow)		//■追加
	END_MSG_MAP()

tabbrowser271.gif
そしてCreate()の処理を変更する。CAxHostWindow::Create()は使わずに、CWindow::Createを、CAxWindow(CAxHostWindowではない)のクラス名を使って呼び出してウインドウ生成をする。

このままだとCAxHostWindowにIEコントロールが入らないので、AttachControlとSetSiteで作成したIEコントロールをCAxHostWindowに割り当てる。
	//■変更
	//Create関数のオーバーライド。内部でIEの取得、接続などを行う
	HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
			DWORD dwStyle = 0, DWORD dwExStyle = 0,
			_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
	{
		HWND	hWnd;

		//__super::ではなく、CWindow::のCreateをCAxWindow::GetWndClassName()指定で利用
		hWnd = CWindow::Create(CAxWindow::GetWndClassName(), hWndParent, rect, szWindowName, dwStyle, dwExStyle, MenuOrID, lpCreateParam);
		if(hWnd == NULL)
			return	NULL;			//Create失敗

		_wndReflect.SubclassWindow(m_hWnd);		//メッセージマップが使えるようにこのウインドウをサブクラス化

		//CWindow::Createで作れたコントロールをこのウインドウにアタッチ
		{
			CComPtr<IUnknown>	pIUnknown;

			AtlAxGetControl(m_hWnd,&pIUnknown);
			if(pIUnknown)
				AttachControl(pIUnknown,m_hWnd);

			QueryControl(IID_IWebBrowser2,(void**)&_pIWebBrowser2);		//_pIWebBrowser2にこのビューに関連づいているIEをセットする
			if(_pIWebBrowser2 == NULL)
				return	hWnd;			//WebBrowser取得失敗

			//サイトも設定
			pIUnknown = NULL;
			QueryHost(&pIUnknown);
			SetSite(pIUnknown);
		}

		Advise(_pIWebBrowser2);		//IEとの接続


		//jacascriptや画像ダウンロードのコントロール設定
		{
			CComPtr<IOleObject>		pIOleObject;
			CComObject<CAmbientControl>*		pCAmbientControl;
			CComPtr<IAxWinAmbientDispatchEx>	pIAxWinAmbientDispatchEx;

			//ダウンロードコントロールの準備
			CComObject<CAmbientControl>::CreateInstance(&pCAmbientControl);
			if(pCAmbientControl)
				pCAmbientControl->QueryInterface(&_pIAmbientControl);

//■削除CAxHostWindowを使うのでセットしない
//			//IEコントロールへのセット
//			_pIWebBrowser2->QueryInterface(&pIOleObject);
//			if(pIOleObject && pCAmbientControl)
//				pIOleObject->SetClientSite(pCAmbientControl);

			//デフォルトのダウンロードコントロールを設定
			//本来ならゾーン変化をチェックしてゾーンに応じて決定すべき?
			SetAmbientDownloadControl(DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_BGSOUNDS);
		}

		return	hWnd;
	}

tabbrowser272.gif CAxHostWidnowにはQueryHostが実装されておらず、ちょっとだけ不便なのでCAxWindowからコピーして実装しておく。
	//■追加
	//CAxWindowのソースからコピー。ホスト取得用関数
	template <class Q>
	HRESULT QueryHost(Q** ppUnk)
	{
		return QueryHost(__uuidof(Q), (void**)ppUnk);
	}
	HRESULT QueryHost(REFIID iid, void** ppUnk)
	{
		ATLASSERT(ppUnk != NULL);
		if (ppUnk == NULL)
			return E_POINTER;
		HRESULT hr;
		*ppUnk = NULL;
		CComPtr<IUnknown> spUnk;
		hr = AtlAxGetHost(m_hWnd, &spUnk);
		if (SUCCEEDED(hr))
			hr = spUnk->QueryInterface(iid, ppUnk);
		return hr;
	}

tabbrowser273.gif
これでとりあえず今回は終了だ。まだCAxWindowからCAxHostWindowに切り替えただけでバグは修正されていない。

次回はバグ修正のためにフォーカスがIEコントロールにあるのか、それともそれ以外にあるのかを検出する処理を実装する。

プロジェクトファイルをダウンロード


カテゴリー「タブブラウザーを作る」 のエントリー