前の10件 8  9  10  11  12  13  14  15  16  17  18

記事一覧

2009年04月19日

第63回 ソースコードを少し整理する (タブブラウザーを作る)

これまでの作業でまたソースコードが煩雑化し、まったく利用されていない部分も多く残っている。今回は機能の追加などはせずに少しソースコードを整理する。



過去の「お気に入り」関連処理を削除

「お気に入り」メニューはIShellMenuにより表示しているが、IShellMenuによる実装の前にはフォルダからインターネットショートカットを検索して直にメニューを生成/表示していた。それらの処理はもう利用しなくなったので削除する。

まず「FavoritesMenu.h」と「DnpFolderMenu.h」の2つのファイルをプロジェクトから取り除く。プロジェクトからの削除に加えて、ファイル自体も削除してしまってかまわない。

そして「MainFrm.h」の中にある「#include "FavoritesMenu.h"」や「CHAIN_MSG_MAP_MEMBER(_cFovMenu)」、関数「CMainFrame::OnMenuSelect」や「CMainFrame::OnFavorite」を削除する。



現在の「お気に入り」関連処理を整理

「CShellMenuCallback」を独立したヘッダーファイルに移動する。

そしてCMainFrame::PopupMenuの引数に「HWND hWnd」を追加する。この関数の内部処理では「m_hWnd」を参照する部分を「hWnd」に変えて、ウインドウを引数で渡せるようにする。
	bool	PopupMenu(int nX,int nY,HWND hWnd)

次はこれまでメニューリソースの「お気に入り」メニューの位置をソースコードの中で「4」と固定して指定していた。それを簡単に変更できるようにする。 そのためにまず「MainFrm.h」の先頭に以下の宣言を追加する。
#define FAVORITE_MENU_POSITION	4		//メインウインドウ用リソースでの「お気に入り」メニューの位置

そして「CMainFrame::PopupMenu」、「CCommandBarCtrl2::DoPopupMenu」、「CMainFrame::OnMenuSelectForPopup」内の「4」というメニュー位置の指定をすべて「FAVORITE_MENU_POSITION」に置き換える。


最後に「お気に入り」関連処理を「CMainFrame」から離した場所に移動する。
独立したヘッダーファイルにクラス「CIShellMenuPopup」を新しく作り、
「CMainFrame::PopupMenu」、「_pIMenuBand」、
「CMainFrame::TranslatePopupMessage」をそのクラスの中に移動する。そして「CMainFrame」の派生元に「CIShellMenuPopup」を追加する。



IEツールバー関連処理を整理

「CMainFrame」内に実装しているインストールされているIEツールバーを検索する処理などを離した場所に移動する。

独立したヘッダーファイルにクラス「CIEToolbarManager」を新しく作り、CMainFrame内の以下のメンバ関数、クラス、変数をその中に移動する。
・CDummyWnd
・CAvailableIEToolbar
・_acAvailableIEToolbar、
・CLSIDtoUIString
・RegEnumKeyName
・EnumAvailableIEToolbar、

そして「CMainFrame」の派生元に「CIEToolbarManager」を追加する。



その他

・CAmbientControl
・CTabBrowser100View::CIEToolbarInfo
を独立したヘッダーファイルへ移動する。


・CTabBrowser100View::GetZone
を「CIEUtility」へ移動する。

「CTabBrowser100View」と「CMainFrame」内のメンバ関数やメンバ変数にprotectedやpublicの指定を追加する。

・「CTabBrowser100View」内の変数の記述位置を前後に移動

・その他、少しだけコメントを追加





今回は以上の細かなソースコード整理をした。次回は現在のページを開いたままリンクをバックグラウンドのタブで開けるようにする。

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

第62回 IShellMenuによる「お気に入り」にメニュー項目を追加する (タブブラウザーを作る)

tabbrowser284.gif
今回は再び「お気に入り」メニュー関連項目を実装する。

「お気に入り」メニューの表示方法をIShellMenuに変えてから、「お気に入りに追加」などの項目が表示されなくなった。今回はこれらの項目を再び表示、使えるようにする。

IShellMenuへのメニュー追加はIShellMenu::SetMenuで行える。ここにリソースから読みだしたHMENUを渡すだけでいい。内部でDestroyMenuされるため、m_Cmdbar.GetMenu()などの引数は渡せない。
	bool	PopupMenu(int nX,int nY)
	{
		LPITEMIDLIST	pidl;

		CComPtr<IShellFolder>		pIShellFolder;

		if(_pIMenuBand)
		{
			//すでにメニューが開いていたら、まずはそのメニューを閉じる

			CComPtr<IOleCommandTarget>	pIOleCommandTarget;

			_pIMenuBand->QueryInterface(&pIOleCommandTarget);
			if(pIOleCommandTarget)
				pIOleCommandTarget->Exec(&CLSID_MenuBand,22,0,NULL,NULL);
		}


		//「お気に入り」フォルダのPIDLとIShellFolderを取得
		{
			CComPtr<IShellFolder>	pIShellFolderDesktop;

			::SHGetDesktopFolder(&pIShellFolderDesktop);
			if(pIShellFolderDesktop == NULL)
				return	false;
			::SHGetSpecialFolderLocation(NULL,CSIDL_FAVORITES,&pidl);
			pIShellFolderDesktop->BindToObject(pidl,NULL,IID_IShellFolder,(void **)&pIShellFolder);
		}



		CComPtr<IDeskBand>	pIDeskBand;

		//PIDLとIShellFolderの示すIShellMenu(IDeskBand)を取得
		{
			HRESULT		hr;
			CRegKey		cRegOrder;
			CComPtr<IShellMenu>	pIShellMenu;
			CComPtr<IDeskBar>	pIDeskBar;
			CComPtr<IMenuPopup>	pIMenuPopup;

			//並び順用レジストリ。必要ならCreateしてもいいが今回は「お気に入り」のみなのでopen
			cRegOrder.Open(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder\\Favorites"));

			::CoCreateInstance(CLSID_MenuBand,NULL,CLSCTX_INPROC_SERVER,IID_IShellMenu,(void**)&pIShellMenu);

			//■追加
			//「お気に入りに追加」などのメニュー項目をメニューリソースから追加
			//IShellMenu::Initializeよりも先にIShellMenu::SetMenuを使わないとだめなことに注意
			if(pIShellMenu)
			{
				HMENU	hMenu;
				HMENU	hSubMenu = NULL;

				hMenu = ::LoadMenu(::_Module.m_hInst,MAKEINTRESOURCE(IDR_MAINFRAME));
				if(hMenu)
					hSubMenu = ::GetSubMenu(hMenu,4);			//■メニュー位置固定

				if(hSubMenu)
					pIShellMenu->SetMenu(hSubMenu,m_hWnd,SMSET_TOP);
				//if(hMenu)
				//	::DestroyMenu(hMenu);		//不要
			}

tabbrowser285.gif そのままでは追加したメニュー項目にデフォルトのアイコンが表示されてしまう。アイコンが表示されないように、SMC_GETINFOを受けとったときに、SMIM_ICONで-1を指定して、アイコンを消す。
	//普通のメニュー項目に関する情報
	HRESULT	OnSMC_GETINFO(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		SMINFO*		pSmInfo = (SMINFO*)lParam;

		if(pSmInfo->dwMask & SMIM_FLAGS)
			pSmInfo->dwFlags |= SMIF_DROPCASCADE | SMIF_TRACKPOPUP;

		//■追加
		if(pSmInfo->dwMask & SMIM_ICON)
			pSmInfo->iIcon = -1;			//追加メニューのアイコンを非表示にする。iIcon==0のときのみ-1にすべき?

		return	S_OK;
	}

tabbrowser286.gif
またIShellMenuを使う前にお気に入りが開いたことを検出するために「dummy」というメニュー項目を用意していた。それはもう不要なのでリソースから消しておく。

tabbrowser287.gif
これでビルド/実行すると「お気に入りの整理」などのメニュー項目が追加され、利用できるようになった。

だんだんソースコードが荒れてきて利用されていない関数も多くなってきた。そのため次回は再びソースコードの整理をする。

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

第61回 CAxHostWindowのInvokeを上書きする (タブブラウザーを作る)

tabbrowser281.gif
今回は画像表示やスクリプト実行のON/OFFなどを制御するダウンロードコントロールが正常に働くようにする。

とはいうものの前回の作業でほとんどの部分が実装されているのでInvokeを上書きするだけでいい。
	//■追加
	STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
			VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
	{
		HRESULT	hr = E_FAIL;
		CComPtr<IDispatch>	pIDispatch;

		//ダウンロードコントロール用のInvoke
		if(_pIAmbientControl)
			_pIAmbientControl->QueryInterface(&pIDispatch);
		if(pIDispatch)
			hr = pIDispatch->Invoke(dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
		if(SUCCEEDED(hr))
			return	hr;

		//IEコントロールのイベント用のInvoke
		hr = IDispEventImpl<SINKID_EVENTS, CTabBrowser100View, &DIID_DWebBrowserEvents2>::Invoke(dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
		if(SUCCEEDED(hr))
			return	hr;

		//デフォルト(CAxHostWindow)用のInvoke
		return CComObject<CAxHostWindow>::Invoke(dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
	}

tabbrowser283.gif これは関係ないが、IEツールバーホスト用のクラスの中のフォーカス変更処理は前回の作業で不要になったので削除する。
	//IInputObjectSite
	STDMETHOD(OnFocusChangeIS)(IUnknown *punkObj,BOOL fSetFocus)
	{
		//■内部処理削除
		//HRESULT	hr = E_FAIL;
		//CComPtr<IOleInPlaceObject> pIOleInPlaceObject;

		//if(_pIDispatch)
		//	_pIDispatch->QueryInterface(&pIOleInPlaceObject);
		//if(pIOleInPlaceObject == NULL)
		//	return	S_OK;

		//if(fSetFocus)
		//{
		//	hr = pIOleInPlaceObject->UIDeactivate();
		//	pIOleInPlaceObject->InPlaceDeactivate();
		//}
		//else
		//{
		//	HWND hWnd;
		//	hr = pIOleInPlaceObject->GetWindow(&hWnd);
		//	if(SUCCEEDED(hr) && ::IsWindow(hWnd))
		//		::ShowWindow(hWnd,SW_NORMAL);
		//}

		return	S_OK;
	}

tabbrowser282.gif
これでビルド/実行すると画像表示のON/OFFなどのダウンロード関連機能が再び使えるようになった。

次回は「お気に入り」メニューに「お気に入りの整理」などのメニュー項目を追加する。

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

第60回 フォーカス変更を検出して処理する (タブブラウザーを作る)

tabbrowser274.gif
前回はフォーカス関連のバグ修正のためビューウインドウをCAxWindowからCAxHostWindowに移行した。今回はIEコントロールがフォーカスを受け取ったことを検出して、正常に処理されるようにする。

まずはCMainFrameのPreTranslateMessage内で、ビューウインドウのPreTranslateMessageが呼ばれるようにする。ATL/WTLアプリケーションウイザードでは標準で同じ様な処理が実装されているが、ウイザードのバグにより正常にビューウインドウのPreTranslateMessageが呼ばれていない。次のバージョンでは修正してもらいたいものだ。。。
	virtual BOOL PreTranslateMessage(MSG* pMsg)
	{
		//■追加
		{
			CTabBrowser100View*	pView;

			pView = GetActivePageView();
			if(pView && pView->PreTranslateMessage(pMsg))
				return	TRUE;
		}

		if(TranslatePopupMessage(pMsg->hwnd,pMsg->message,pMsg->wParam,pMsg->lParam,pMsg))
			return	TRUE;

		if(_wndAddressBar.IsChild(::GetFocus()) == FALSE)		//アドレスバーにフォーカスがあるときはPreTranslateMessageを回さない(アドレスバーでCtrl+Cなどを使えるようにするため)
		{
			if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
				return TRUE;
		}

		return m_view.PreTranslateMessage(pMsg);
	}

tabbrowser275.gif
そしてビューウインドウ内にPreTranslateMessageを追加する。ビューに対してWM_FORWARDMSGを投げると、CAxHostWindow内で自動的にTranslateAcceleatorが呼び出される。

また、フォーカスの変更を取得するためのウインドウCSubclassIEWndを追加する。今回はWM_SETFOCUSもしくはWM_KILLFOCUSがIEコントロールに届いたらそれをビューウインドウへ送るようにした。
	//■追加
	//「Internet Explorer_Server」をサブクラス化
	class	CSubclassIEWnd : public CWindowImpl<CSubclassIEWnd>
	{
		CTabBrowser100View*	_pView;
	public:
		CSubclassIEWnd(CTabBrowser100View* pView)
		{
			_pView = pView;
		}
		BEGIN_MSG_MAP(CSubclassIEWnd)
			if(uMsg == WM_SETFOCUS || uMsg == WM_KILLFOCUS)// || uMsg == WM_MOUSEACTIVATE)
			{
				if(_pView)
					_pView->SendMessage(uMsg,wParam,lParam);
			}

			if(uMsg == WM_DESTROY)
			{
				UnsubclassWindow();
			}
		END_MSG_MAP()
	};

	//■追加
	CSubclassIEWnd	_wndIEServer;

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


	CTabBrowser100View(CTabView* pTabView)
		: _wndReflect(this)
		,_wndIEServer(this)
	{
		_pTabView = pTabView;
		_bPageError = false;
	}


	//■追加
	BOOL PreTranslateMessage(MSG* pMsg)
	{
		if((pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) &&
		   (pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST))
		{
			return FALSE;
		}

		BOOL bRet = FALSE;

		//IOleInPlaceActiveObject::TranslateAccelerator(pMsg)の呼び出し
		if(pMsg->hwnd == m_hWnd || IsChild(pMsg->hwnd))
			bRet = (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);

		return bRet;
	}

tabbrowser276.gif IEコントロールが持つ「Internet Explorer_Server」ウインドウはドキュメントがそろったときに生成される。そのためOnDocumentComplete内でサブクラス化する。
	//HTMLページが読み終わったときに呼ばれる処理
	void	_stdcall OnDocumentComplete(IDispatch* pDisp, VARIANT* vURL)
	{
		if(pDisp != _pIWebBrowser2)
			return;			//フレームなどの読み込み終了は無視

		//■追加
		//IEをサブクラス化
		if(_wndIEServer.IsWindow() == FALSE && _pIWebBrowser2)
		{
			HWND	hWnd;
			HRESULT	hr = E_FAIL;
			CComPtr<IDispatch>	pIDispatch;
			CComPtr<IOleWindow>	pIOleWindow;

			_pIWebBrowser2->get_Document(&pIDispatch);
			if(pIDispatch)
				pIDispatch->QueryInterface(&pIOleWindow);
			if(pIOleWindow)
				hr = pIOleWindow->GetWindow(&hWnd);
			if(SUCCEEDED(hr))
				_wndIEServer.SubclassWindow(hWnd);
		}

		ChangeUIHandler();			//UIHandlerを変更する

		HICON		hIcon;
		CAtlString	strFile;

tabbrowser278.gif そしてビューウインドウに届いたWM_SETFOCUSなどをメッセージマップで処理する。
	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)
		MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)	//■追加
		MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)	//■追加
		CHAIN_MSG_MAP(CAxHostWindow)
	END_MSG_MAP()


private:

	//■追加
	LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		ATLTRACE("●OnSetFocus\n");

		SetFocusChange(true);
		bHandled = FALSE;
		return	0;
	}

	//■追加
	LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		ATLTRACE("●OnKillFocus\n");

		SetFocusChange(false);
		bHandled = FALSE;
		return	0;
	}

tabbrowser277.gif これまでアドレスバーの中でフォーカスの検出をしていた。この処理はもう不要なので削除する。クラス自体必要性がなくなってしまうのだが、とりあえずこのまま残しておく。
//アドレスバー用クラス
class	CAddressBarCtrl	: public CWindowImpl<CAddressBarCtrl,CComboBoxEx>
{
	//■内部処理削除
	class	CInnerComboBox	: public CWindowImpl<CInnerComboBox>
	{
	public:
		BEGIN_MSG_MAP(CInnerComboBox)
			//MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
			//MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
		END_MSG_MAP()


		////マウスによるフォーカス取得時の処理
		//LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		//{
		//	GetTopLevelParent().SendMessage(WM_DNP_CHANGEFOCUS,(WPARAM)TRUE);		//メインウインドウへフォーカスを受け取ったことを通知
		//	bHandled = FALSE;
		//	return	0;
		//}

		//LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		//{
		//	GetTopLevelParent().SendMessage(WM_DNP_CHANGEFOCUS,(WPARAM)FALSE);		//メインウインドウへフォーカスを失ったことを通知
		//	bHandled = FALSE;
		//	return	0;
		//}
	};

tabbrowser279.gif 最後に今回のバグの元凶となっていたSetFocusChange内の処理を変更する。
	//■変更
	//IEコントロールがフォーカスを受け取ったときbGetFocus=true、
	//IEコントロールがフォーカスを失ったときbGetFocus=falseとして呼び出す
	bool	SetFocusChange(bool bGetFocus)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;

		if(bGetFocus)
		{
		}
		else
		{
			CComPtr<IOleInPlaceObject> pIOleInPlaceObject;

			if(_pIWebBrowser2)
				_pIWebBrowser2->QueryInterface(&pIOleInPlaceObject);
			if(pIOleInPlaceObject)
				pIOleInPlaceObject->UIDeactivate();
		}

		return	true;
	}

tabbrowser280.gif
これでとりあえず、フォーカス関連のバグは修正された。しかし今回のバグ修正作業によりスクリプトの実行や画像の表示のON/OFF機能が使えなくなってしまっている。

次回はその部分を修正する。

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

第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コントロールにあるのか、それともそれ以外にあるのかを検出する処理を実装する。

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

2009年04月17日

第58回 IShellMenuCallbackにより「お気に入り」をビューで開く (タブブラウザーを作る)

tabbrowser264.gif
前回は「お気に入り」メニューをIShellMenuベースに置き換えた。今回はIShellMenu用のコールバックインターフェースを用意して、選択した「お気に入り」がビューで開くようにする。

IShellMenuCallbackがコールバック用インターフェースで、各種イベント時にIShellMenuCallback::CallbackSMが呼ばれる。ここでは「お気に入り」が選択されたときにそのフルパスを独自メッセージでメインウインドウへ返すようにする。
//■追加
//IEツールバーをホストするためのインターフェース
MIDL_INTERFACE("50AFAFED-815E-4ec4-9A78-231C95958716")
IShellMenuHost : public IUnknown
{
public:
	STDMETHOD (put_IShellFolder)(IShellFolder* pIShellFolder) = 0;
	STDMETHOD (put_NotifyWindow)(HWND hWnd,UINT nMessageID) = 0;
};


//■追加
class	CShellMenuCallback : 
	public CComObjectRootEx<CComSingleThreadModel>
	,public IShellMenuHost
	,public IShellMenuCallback
{
	UINT	_nNotifyMessage;
	HWND	_hWndNotify;

	CComPtr<IShellFolder>	_pIShellFolder;

public:
	BEGIN_COM_MAP(CShellMenuCallback)
		COM_INTERFACE_ENTRY(IShellMenuHost)
		COM_INTERFACE_ENTRY(IShellMenuCallback)
	END_COM_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()

	CShellMenuCallback()
	{
		_nNotifyMessage = 0;
		_hWndNotify = NULL;
	}

	//
	STDMETHOD (put_IShellFolder)(IShellFolder* pIShellFolder)
	{
		_pIShellFolder = NULL;
		if(pIShellFolder == NULL)
			return	E_POINTER;

		return	pIShellFolder->QueryInterface(IID_IShellFolder,(void**)&_pIShellFolder);
	}

	STDMETHOD (put_NotifyWindow)(HWND hWnd,UINT nMessageID)
	{
		if(hWnd == NULL || ::IsWindow(hWnd) == FALSE || nMessageID == 0)
			return	E_FAIL;

		_hWndNotify = hWnd;
		_nNotifyMessage = nMessageID;

		return	S_OK;
	}




	HRESULT	OnSMC_INITMENU(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_FALSE;
	}

	//メニューが作られた
	//処理は不要
	HRESULT	OnSMC_CREATE(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_FALSE;
	}

	HRESULT	OnSMC_EXITMENU(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}


	//普通のメニュー項目に関する情報
	HRESULT	OnSMC_GETINFO(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		SMINFO*		pSmInfo = (SMINFO*)lParam;

		if(pSmInfo->dwMask & SMIM_FLAGS)
			pSmInfo->dwFlags |= SMIF_DROPCASCADE | SMIF_TRACKPOPUP;

		return	S_OK;
	}

	//IShellFolder項目に関する情報
	//処理必要なし
	HRESULT	OnSMC_GETSFINFO(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_GETOBJECT(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		REFIID	iid = (REFIID)wParam;
		void**	ppv = (void**)lParam;

		//処理していない
		return	S_FALSE;
	}

	HRESULT	OnSMC_GETSFOBJECT(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		REFIID	iid = (REFIID)wParam;
		void**	ppv = (void**)lParam;

		//処理していない
		return	S_FALSE;
	}

	//選択された項目を開く
	HRESULT	OnSMC_SFEXEC(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		TCHAR	pszFile[MAX_PATH];
		TCHAR	pszPath[MAX_PATH];
		int		nFind;
		CAtlString	strName;

		*pszPath = NULL;
		::SHGetPathFromIDList(psmd->pidlItem,pszFile);
		::SHGetPathFromIDList(psmd->pidlFolder,pszPath);

		strName = pszFile;
		nFind = strName.ReverseFind(_T('\\'));
		if(nFind >= 0)
			strName = strName.Right(strName.GetLength() - nFind);
		strName = pszPath + strName;

		//ウインドウへメッセージを送る
		if(*pszPath && _hWndNotify && ::IsWindow(_hWndNotify) && _nNotifyMessage)
			::SendMessage(_hWndNotify,_nNotifyMessage,(WPARAM)(LPCTSTR)strName,NULL);

		return	S_OK;
	}

	HRESULT	OnSMC_SFSELECTITEM(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_REFRESH(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_DEMOTE(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_PROMOTE(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_DEFAULTICON(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_NEWITEM(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_CHEVRONEXPAND(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_DISPLAYCHEVRONTIP(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_SETSFOBJECT(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_SHCHANGENOTIFY(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_CHEVRONGETTIP(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	On(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}

	HRESULT	OnSMC_SFDDRESTRICTED(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		return	S_OK;
	}




	STDMETHOD(CallbackSM)(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		switch(uMsg)
		{
		// The callback is called to init a menuband
		case	SMC_INITMENU:
			return	OnSMC_INITMENU(psmd,uMsg,wParam,lParam);

		case	SMC_CREATE:
			return	OnSMC_CREATE(psmd,uMsg,wParam,lParam);

		// The callback is called when menu is collapsing
		case	SMC_EXITMENU:
			return	OnSMC_EXITMENU(psmd,uMsg,wParam,lParam);

		// The callback is called to return DWORD values
		case	SMC_GETINFO:
			return	OnSMC_GETINFO(psmd,uMsg,wParam,lParam);

		// The callback is called to return DWORD values
		case	SMC_GETSFINFO:
			return	OnSMC_GETSFINFO(psmd,uMsg,wParam,lParam);

		// The callback is called to get some object
		case	SMC_GETOBJECT:
			return	OnSMC_GETOBJECT(psmd,uMsg,wParam,lParam);

		// The callback is called to get some object
		case	SMC_GETSFOBJECT:
			return	OnSMC_GETSFOBJECT(psmd,uMsg,wParam,lParam);

		// The callback is called to execute an shell folder item
		case	SMC_SFEXEC:
			return	OnSMC_SFEXEC(psmd,uMsg,wParam,lParam);

		// The callback is called when an item is selected
		case	SMC_SFSELECTITEM:
			return	OnSMC_SFSELECTITEM(psmd,uMsg,wParam,lParam);

		// Menus have completely refreshed. Reset your state.
		case	SMC_REFRESH:
			return	OnSMC_REFRESH(psmd,uMsg,wParam,lParam);

		// Demote an item
		case	SMC_DEMOTE:
			return	OnSMC_DEMOTE(psmd,uMsg,wParam,lParam);

		// Promote an item, wParam = SMINV_* flag
		case	SMC_PROMOTE:
			return	OnSMC_PROMOTE(psmd,uMsg,wParam,lParam);

		// Returns Default icon location in wParam, index in lParam
		case	SMC_DEFAULTICON:
			return	OnSMC_DEFAULTICON(psmd,uMsg,wParam,lParam);

		// Notifies item is not in the order stream.
		case	SMC_NEWITEM:
			return	OnSMC_NEWITEM(psmd,uMsg,wParam,lParam);

		// Notifies of a expansion via the chevron
		case	SMC_CHEVRONEXPAND:
			return	OnSMC_CHEVRONEXPAND(psmd,uMsg,wParam,lParam);

		// S_OK display, S_FALSE not.
		case	SMC_DISPLAYCHEVRONTIP:
			return	OnSMC_DISPLAYCHEVRONTIP(psmd,uMsg,wParam,lParam);

		// Called to save the passed object
		case	SMC_SETSFOBJECT:
			return	OnSMC_SETSFOBJECT(psmd,uMsg,wParam,lParam);

		// Called when a Change notify is received. lParam points to SMCSHCHANGENOTIFYSTRUCT
		case	SMC_SHCHANGENOTIFY:
			return	OnSMC_SHCHANGENOTIFY(psmd,uMsg,wParam,lParam);

		// Called to get the chevron tip text. wParam = Tip title, Lparam = TipText Both MAX_PATH
		case	SMC_CHEVRONGETTIP:
			OnSMC_CHEVRONGETTIP(psmd,uMsg,wParam,lParam);
			break;

		// Called requesting if it's ok to drop. wParam = IDropTarget.
		case	SMC_SFDDRESTRICTED:
			OnSMC_SFDDRESTRICTED(psmd,uMsg,wParam,lParam);
			break;

		default:
			break;
		}

		return	S_FALSE;
	}
};

tabbrowser265.gif そしてメインウインドウで独自メッセージが届いたときにビューでURLが開くようにする。
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelectForPopup)
//		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
		MESSAGE_HANDLER(WM_DNP_NAVIGATEFAVORITE, OnDnpNavigateFavorite)	//■追加
		MESSAGE_HANDLER(WM_PARENTNOTIFY, OnParentNotify)
//		COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)	//■削除
		CHAIN_MSG_MAP_MEMBER(_cFovMenu)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()


	//■追加
	LRESULT OnDnpNavigateFavorite(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bool		ret;
		CAtlString	strURL;
		LPCTSTR	pszFile = (LPCTSTR)wParam;

		if(pszFile == NULL || *pszFile == NULL)
			return	0;

		ret = GetInternetShortcut(pszFile,strURL);
		if(ret == false)
			return	0;

		//タブとしてURLを開く
		{
			CTabBrowser100View* pView;

			pView = GetActivePageView();
			if(pView == NULL)
			{
				//新しいタブを作成
				pView = CreateNewTab(_T("about:blank"),NULL);
			}

			//URLを開く
			if(pView)
				pView->Navigate(strURL);
		}

		return	0;
	}

tabbrowser266.gif IShellMenu::Initializeで先ほど作成したコールバック用インターフェースを渡す。
	bool	PopupMenu(int nX,int nY)
	{
		LPITEMIDLIST	pidl;

		CComPtr<IShellFolder>		pIShellFolder;

		if(_pIMenuBand)
		{
			//すでにメニューが開いていたら、まずはそのメニューを閉じる

			CComPtr<IOleCommandTarget>	pIOleCommandTarget;

			_pIMenuBand->QueryInterface(&pIOleCommandTarget);
			if(pIOleCommandTarget)
				pIOleCommandTarget->Exec(&CLSID_MenuBand,22,0,NULL,NULL);
		}


		//「お気に入り」フォルダのPIDLとIShellFolderを取得
		{
			CComPtr<IShellFolder>	pIShellFolderDesktop;

			::SHGetDesktopFolder(&pIShellFolderDesktop);
			if(pIShellFolderDesktop == NULL)
				return	false;
			::SHGetSpecialFolderLocation(NULL,CSIDL_FAVORITES,&pidl);
			pIShellFolderDesktop->BindToObject(pidl,NULL,IID_IShellFolder,(void **)&pIShellFolder);
		}



		CComPtr<IDeskBand>	pIDeskBand;

		//PIDLとIShellFolderの示すIShellMenu(IDeskBand)を取得
		{
			HRESULT		hr;
			CRegKey		cRegOrder;
			CComPtr<IShellMenu>	pIShellMenu;
			CComPtr<IDeskBar>	pIDeskBar;
			CComPtr<IMenuPopup>	pIMenuPopup;

			//並び順用レジストリ。必要ならCreateしてもいいが今回は「お気に入り」のみなのでopen
			cRegOrder.Open(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder\\Favorites"));

			::CoCreateInstance(CLSID_MenuBand,NULL,CLSCTX_INPROC_SERVER,IID_IShellMenu,(void**)&pIShellMenu);

			//■処理変更
			if(pIShellMenu)
			{
				CComPtr<IShellMenuHost>			pIShellMenuHost;
				CComPtr<IShellMenuCallback>		pIShellMenuCallback;

				//コールバック生成
				pIShellMenuCallback = new CComObject<CShellMenuCallback>;
				if(pIShellMenuCallback)
					pIShellMenuCallback->QueryInterface(&pIShellMenuHost);
				if(pIShellMenuHost)
				{
					pIShellMenuHost->put_IShellFolder(pIShellFolder);
					pIShellMenuHost->put_NotifyWindow(m_hWnd,WM_DNP_NAVIGATEFAVORITE);
				}

				//IShellMenuの初期化
				pIShellMenu->Initialize(pIShellMenuCallback,-1,ANCESTORDEFAULT,SMINIT_TOPLEVEL | SMINIT_VERTICAL);

				#ifndef	SMSET_USEBKICONEXTRACTION
					#define SMSET_USEBKICONEXTRACTION	0x00000008
				#endif

				//メニューへのフォルダ割り当て
				hr = pIShellMenu->SetShellFolder(pIShellFolder,pidl,cRegOrder.m_hKey,SMSET_BOTTOM | SMSET_USEBKICONEXTRACTION);	//以下も指定したいが値が不明 | SMSET_HASEXPANDABLEFOLDERS)

				if(SUCCEEDED(hr))
					cRegOrder.Detach();	//レジストリのCloseはIShellMenuに任せる
				ILFree(pidl);
				pidl = NULL;
			}

			if(pIShellMenu)
				pIShellMenu->QueryInterface(&pIMenuPopup);
			if(pIMenuPopup)
				pIMenuPopup->QueryInterface(&pIDeskBar);
			if(pIDeskBar)
				pIDeskBar->QueryInterface(&pIDeskBand);
			if(pIDeskBand == NULL)
				return	false;
		}


		//IShellMenu(IDeskBand)をポップアップメニューで表示
		{
			POINTL	ptl;
			GUID	rclsid;
			CComPtr<IUnknown>			pIUnknown;
			CComPtr<IBandSite>			pIBandSite;
			CComPtr<IMenuPopup>			pIMenuPopup;

			//メニュー用のDeskBar「Menu Desk Bar」
			::CLSIDFromString(L"{ECD4FC4F-521C-11D0-B792-00A0C90312E1}",&rclsid);
			::CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&pIUnknown);

			//メニュー用のBandSite
			::CoCreateInstance(CLSID_MenuBandSite,NULL,CLSCTX_INPROC_SERVER,IID_IBandSite,(void**)&pIBandSite);

			if(pIUnknown)
				pIUnknown->QueryInterface(&pIMenuPopup);	//メニュー用DeskBarだからIMenuPopupを持っている
			if(pIMenuPopup)
				pIMenuPopup->SetClient(pIBandSite);			//DeskBarにIBandSiteを割り当て
			if(pIBandSite)
				pIBandSite->AddBand(pIDeskBand);			//IBandSiteに表示したいIShellMenuを割り当てる

			//位置を指定してメニュー表示
			ptl.x = nX;
			ptl.y = nY;
			if(pIMenuPopup)
				pIMenuPopup->Popup(&ptl,NULL,MPPF_POS_MASK | MPPF_FORCEZORDER | MPPF_RIGHT | MPPF_BOTTOM);
		}


		//メニューの_pIMenuBandを保存
		{
			_pIMenuBand = NULL;
			pIDeskBand->QueryInterface(&_pIMenuBand);
			if(_pIMenuBand == NULL)
				return	false;
		}

		return	true;
	}

tabbrowser267.gif 最後に「stdafx.h」内に独自メッセージを定義しておく。
#define	WM_DNP_NAVIGATEFAVORITE		(WM_APP + 13)	//■追加

tabbrowser268.gif
これで「お気に入り」を選択したときにそのURLがビューで開くようになった。また「お気に入り」の表示順序をドラッグアンドドロップにより変更できるようになった。

次回は少しフォーカス関連のバグ修正をする。

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

第57回 IShellMenuにより「お気に入り」を表示する (タブブラウザーを作る)

tabbrowser258.gif
今回は「お気に入り」の表示方法を一新する。

これまでは「お気に入り」フォルダから保存されているインターネットショートカットを列挙して独自にメニューを生成/表示していた。これをInternet Explorerの「お気に入り」や「スタート」メニューと同じようにシェルメニュー(IShellMenu)ベースのものに変える。

Windows Vista以降であればITrackShellMenuを利用することで非常に簡単にIShellMenuをメニューとして表示できる。しかし2009年4月現在Windows Vistaはまだまだ普及率が悪い。そのためITrackShellMenuは使わずに力技でIShellMenuを表示することにした。

CMainFrameにIShellMenuを利用した「お気に入り」表示関数を追加する。

	CComPtr<IMenuBand>	_pIMenuBand;


	bool	PopupMenu(int nX,int nY)
	{
		LPITEMIDLIST	pidl;

		CComPtr<IShellFolder>		pIShellFolder;

		if(_pIMenuBand)
		{
			//すでにメニューが開いていたら、まずはそのメニューを閉じる

			CComPtr<IOleCommandTarget>	pIOleCommandTarget;

			_pIMenuBand->QueryInterface(&pIOleCommandTarget);
			if(pIOleCommandTarget)
				pIOleCommandTarget->Exec(&CLSID_MenuBand,22,0,NULL,NULL);
		}


		//「お気に入り」フォルダのPIDLとIShellFolderを取得
		{
			CComPtr<IShellFolder>	pIShellFolderDesktop;

			::SHGetDesktopFolder(&pIShellFolderDesktop);
			if(pIShellFolderDesktop == NULL)
				return	false;
			::SHGetSpecialFolderLocation(NULL,CSIDL_FAVORITES,&pidl);
			pIShellFolderDesktop->BindToObject(pidl,NULL,IID_IShellFolder,(void **)&pIShellFolder);
		}



		CComPtr<IDeskBand>	pIDeskBand;

		//PIDLとIShellFolderの示すIShellMenu(IDeskBand)を取得
		{
			HRESULT		hr;
			CRegKey		cRegOrder;
			CComPtr<IShellMenu>	pIShellMenu;
			CComPtr<IDeskBar>	pIDeskBar;
			CComPtr<IMenuPopup>	pIMenuPopup;

			//並び順用レジストリ。必要ならCreateしてもいいが今回は「お気に入り」のみなのでopen
			cRegOrder.Open(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder\\Favorites"));

			::CoCreateInstance(CLSID_MenuBand,NULL,CLSCTX_INPROC_SERVER,IID_IShellMenu,(void**)&pIShellMenu);

			if(pIShellMenu)
			{
				//とりあえずCallback指定していない
				pIShellMenu->Initialize(NULL,-1,ANCESTORDEFAULT,SMINIT_TOPLEVEL | SMINIT_VERTICAL);

				#ifndef	SMSET_USEBKICONEXTRACTION
					#define SMSET_USEBKICONEXTRACTION	0x00000008
				#endif

				//メニューへのフォルダ割り当て
				hr = pIShellMenu->SetShellFolder(pIShellFolder,pidl,cRegOrder.m_hKey,SMSET_BOTTOM | SMSET_USEBKICONEXTRACTION);	//以下も指定したいが値が不明 | SMSET_HASEXPANDABLEFOLDERS)

				if(SUCCEEDED(hr))
					cRegOrder.Detach();	//レジストリのCloseはIShellMenuに任せる
				ILFree(pidl);
				pidl = NULL;
			}

			if(pIShellMenu)
				pIShellMenu->QueryInterface(&pIMenuPopup);
			if(pIMenuPopup)
				pIMenuPopup->QueryInterface(&pIDeskBar);
			if(pIDeskBar)
				pIDeskBar->QueryInterface(&pIDeskBand);
			if(pIDeskBand == NULL)
				return	false;
		}


		//IShellMenu(IDeskBand)をポップアップメニューで表示
		{
			POINTL	ptl;
			GUID	rclsid;
			CComPtr<IUnknown>			pIUnknown;
			CComPtr<IBandSite>			pIBandSite;
			CComPtr<IMenuPopup>			pIMenuPopup;

			//メニュー用のDeskBar「Menu Desk Bar」
			::CLSIDFromString(L"{ECD4FC4F-521C-11D0-B792-00A0C90312E1}",&rclsid);
			::CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&pIUnknown);

			//メニュー用のBandSite
			::CoCreateInstance(CLSID_MenuBandSite,NULL,CLSCTX_INPROC_SERVER,IID_IBandSite,(void**)&pIBandSite);

			if(pIUnknown)
				pIUnknown->QueryInterface(&pIMenuPopup);	//メニュー用DeskBarだからIMenuPopupを持っている
			if(pIMenuPopup)
				pIMenuPopup->SetClient(pIBandSite);			//DeskBarにIBandSiteを割り当て
			if(pIBandSite)
				pIBandSite->AddBand(pIDeskBand);			//IBandSiteに表示したいIShellMenuを割り当てる

			//位置を指定してメニュー表示
			ptl.x = nX;
			ptl.y = nY;
			if(pIMenuPopup)
				pIMenuPopup->Popup(&ptl,NULL,MPPF_POS_MASK | MPPF_FORCEZORDER | MPPF_RIGHT | MPPF_BOTTOM);
		}


		//メニューの_pIMenuBandを保存
		{
			_pIMenuBand = NULL;
			pIDeskBand->QueryInterface(&_pIMenuBand);
			if(_pIMenuBand == NULL)
				return	false;
		}

		return	true;
	}

tabbrowser259.gif そしてメッセージハンドラに「お気に入り」メニューを表示するための処理を入れる。今回はWM_MENUSELECTが届いたときに表示することにした。
		COMMAND_ID_HANDLER(ID_IE_ADDFAVORITE, OnIEAddFavorite)
		COMMAND_ID_HANDLER(ID_IE_ORGANIZE_FAVORITE, OnIEOrganizeFavorite)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelectForPopup)		//■追加
//		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)		//■削除
		MESSAGE_HANDLER(WM_PARENTNOTIFY, OnParentNotify)
		COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)
		CHAIN_MSG_MAP_MEMBER(_cFovMenu)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()


	//■追加
	LRESULT OnMenuSelectForPopup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;

		UINT	nMenuItem = LOWORD(wParam);
		HMENU	hMenu = (HMENU)lParam;

		if(HIWORD(wParam) & MF_POPUP)
			return	0;
		if(nMenuItem != 4)							//メニュー位置
			return	0;

		RECT	rect;

		m_CmdBar.GetItemRect(nMenuItem,&rect);
		m_CmdBar.MapWindowPoints(NULL,&rect);

		PopupMenu(rect.left,rect.bottom);

		return	0;
	}

tabbrowser262.gif もともとメニューリソースに用意されているメニューが表示されてしまわないように、CCommandBarCtrlのメニュー表示部分を上書きする。
//■追加
class	CCommandBarCtrl2	: public CCommandBarCtrlImpl<CCommandBarCtrl2>
{
public:
	BEGIN_MSG_MAP(CCommandBarCtrl2)
			CHAIN_MSG_MAP(__super)
		ALT_MSG_MAP(1)   // Parent window messages
			CHAIN_MSG_MAP_ALT(__super,1)
		ALT_MSG_MAP(2)   // MDI client window messages
			CHAIN_MSG_MAP_ALT(__super,2)
		ALT_MSG_MAP(3)   // Message hook messages
			CHAIN_MSG_MAP_ALT(__super,3)
	END_MSG_MAP()


	void DoPopupMenu(int nIndex, bool bAnimate)
	{
		if(nIndex == 4)		//メニュー位置固定
		{
			HWND	hWnd = ::GetAncestor(m_hWnd,GA_ROOT);
			::SendMessage(hWnd,WM_MENUSELECT,MAKEWPARAM(nIndex,MF_HILITE),(LPARAM)GetMenu().m_hMenu);
			return;
		}

		__super::DoPopupMenu(nIndex,bAnimate);
	}
};


class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>,
		public CMessageFilter, public CIdleHandler
{
	CAddressBarCtrl	_wndAddressBar;			// アドレスバー用コンボボックス


public:
	DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)

	CTabView m_view;
	CCommandBarCtrl2 m_CmdBar;

tabbrowser260.gif さらにIShellMenuがきちんとメッセージを処理できるようにIMenuBand::TranslateMenuMessageを呼び出す処理を追加する。
	virtual BOOL PreTranslateMessage(MSG* pMsg)
	{
		//■追加
		if(TranslatePopupMessage(pMsg->hwnd,pMsg->message,pMsg->wParam,pMsg->lParam,pMsg))
			return	TRUE;

		if(_wndAddressBar.IsChild(::GetFocus()) == FALSE)		//アドレスバーにフォーカスがあるときはPreTranslateMessageを回さない(アドレスバーでCtrl+Cなどを使えるようにするため)
		{
			if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
				return TRUE;
		}

		return m_view.PreTranslateMessage(pMsg);
	}



	//■追加
	//
	//ポップアップメニューのメッセージ処理
	//
	//pMsg == NULLのときは開いているメニューを閉じるべきかどうかのチェックも併せておこなう
	//(PreTranslateMessageではpMsgも指定する)
	//
	bool	TranslatePopupMessage(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam,MSG* pMsg=NULL)
	{
		if(_pIMenuBand == NULL)
			return	false;

		HRESULT	hr;
		LRESULT	ret = 0;
		MSG		msg;

		if(pMsg == NULL)
		{
			::ZeroMemory(&msg,sizeof(MSG));
			msg.hwnd = hWnd;
			msg.message = uMsg;
			msg.wParam = wParam;
			msg.lParam = lParam;
		}
		else
		{
			msg = *pMsg;
		}

		hr = _pIMenuBand->IsMenuMessage(&msg);
		if(hr == S_OK)
			hr = _pIMenuBand->TranslateMenuMessage(&msg,&ret);
		if(hr == S_OK)
			return	true;

		//メニューを閉じるべきかどうかのチェック
		//もう少しチェック項目を増やした方がいい。。。
		if(pMsg == NULL
			&& (hr == E_FAIL
			|| (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
			|| uMsg == WM_INITMENUPOPUP
			|| uMsg == WM_SIZE
			|| uMsg == WM_MOVE
			|| uMsg == WM_MOVING
			|| uMsg == WM_INITMENU
			|| 0))
		{
			//メニューを閉じる

			CComPtr<IOleCommandTarget>	pIOleCommandTarget;

			_pIMenuBand->QueryInterface(&pIOleCommandTarget);
			if(pIOleCommandTarget)
				pIOleCommandTarget->Exec(&CLSID_MenuBand,22,0,NULL,NULL);
		}

		return	false;
	}

tabbrowser261.gif ちょっと汚い実装方法だが、メッセージマップの中からもIMenuBandへメッセージが渡るようにする。
	BEGIN_MSG_MAP(CMainFrame)
		//■追加
		{
			//「お気に入り」メニュー用TranslateMessage
			if(TranslatePopupMessage(hWnd,uMsg,wParam,lParam))
				return	TRUE;
		}
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		NOTIFY_CODE_HANDLER(CBEN_ENDEDIT, OnAddressbarEnter)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)

tabbrowser263.gif
これでビルド/実行するとIShellMenuにより「お気に入り」が表示されるようになった。

まだ実装が不十分なため「お気に入り」メニューが開いているのにコマンドバーの「お気に入り」がハイライト状態になっていない、ドラッグアンドドロップによる「お気に入り」の移動ができない、メニュー表示がクラシックスタイルなどなど機能が足りない。

しかし「お気に入り」の並び順がInternet Explorerでの表示と同じだったり、アイコンの読み込みがバックグラウンドで行われる、TIPSが表示されるなど「お気に入り」メニューとしてそこそこ使える状態になっている。

次回は選択した「お気に入り」のURLをタブとして開くようにする。

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

2009年04月16日

第56回 右クリックメニューでIEツールバーを選択する (タブブラウザーを作る)

tabbrowser250.gif
前回はインストールされて利用可能なInternet Explorer用ツールバーがすべて表示されるようにした。
今回は右クリックメニューから選んで表示/非表示できるようにする。

ツールバーなどのエリアでの右クリック処理はWM_PARENTNOTIFYとして届く。これを受け取ったらRB_HITTESTを利用して右クリックされた場所がツールバーならIEツールバー選択用のメニューを表示する。 そして選択されたIEツールバーの表示/非表示を切り替える。
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
		MESSAGE_HANDLER(WM_PARENTNOTIFY, OnParentNotify)			//■追加
		COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)
		CHAIN_MSG_MAP_MEMBER(_cFovMenu)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()


	//■追加
	LRESULT OnParentNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		if(wParam != WM_RBUTTONDOWN)
			return	0;

		size_t	nIndex;
		POINT	pt;

		::GetCursorPos(&pt);

		{
			RBHITTESTINFO	info;

			::ZeroMemory(&info,sizeof(RBHITTESTINFO));
			info.pt = pt;
			ScreenToClient(&info.pt);
			if(::SendMessage(m_hWndToolBar,RB_HITTEST,NULL,(LPARAM)&info) < 0)
				return	0;
		}

		//メニュー表示
		{
			HMENU	hMenu;
			size_t	i;
			size_t	nSize;

			nSize = _acAvailableIEToolbar.GetCount();
			if(nSize == 0)			//利用可能なIEツールバーがない
				return	0;

			hMenu = ::CreatePopupMenu();

			for(i = 0; i < nSize; i++)
			{
				::InsertMenu(hMenu,i,MF_BYPOSITION | MF_STRING,i + 1,_acAvailableIEToolbar[i].strName);

				if(_acAvailableIEToolbar[i].IsVisible())
					::CheckMenuItem(hMenu,i + 1,MF_CHECKED | MF_BYCOMMAND);
			}

			nIndex = (size_t)::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RETURNCMD,pt.x,pt.y,NULL,m_hWnd,NULL);
			::DestroyMenu(hMenu);

			if(nIndex == 0)
				return	0;			//選択されなかった
			nIndex--;
		}


		if(_acAvailableIEToolbar[nIndex].IsVisible() == false)
		{
			//ツールバーの表示
			AddIEToolbar(nIndex);
		}
		else
		{
			//ツールバーの消去
			int		i;
			int		nSize;
			CTabBrowser100View*	pView;

			nSize = m_view.GetPageCount();
			for(i = 0; i < nSize; i++)
			{
				pView = GetPageView(i);
				if(pView == NULL)
					continue;

				pView->RemoveIEToolbar(_acAvailableIEToolbar[nIndex].pwndDummy->m_hWnd);
			}

			::SendMessage(m_hWndToolBar,RB_DELETEBAND,(WPARAM)_acAvailableIEToolbar[nIndex].pwndDummy->GetRebarIndex(),NULL);

			_acAvailableIEToolbar[nIndex].pwndDummy->DestroyWindow();
			delete	_acAvailableIEToolbar[nIndex].pwndDummy;
			_acAvailableIEToolbar[nIndex].pwndDummy = NULL;
		}


		bHandled = FALSE;
		return	0;
	}

tabbrowser253.gif
今までツールバーの管理をCAtlArray<CDummyWnd*>にしていたが、このままだと表示/非表示の切り替えがややこしいのでacAvailableIEToolbarに移動する。

また、AddIEToolbarの引数もクラスIDから_acAvailableIEToolbarへのインデックスに変更しておく。
//	CAtlArray<CDummyWnd*>	_apwndDummy;	//■削除_acAvailableIEToolbarの中へ

	//■変更。引数からCLSIDから_acAvailableIEToolbarへのインデックスに変更。
	bool	AddIEToolbar(size_t nIndex)
	{
		IID			clsidIDeskBand;
		CDummyWnd*	pwndDummy;

		if(nIndex >= _acAvailableIEToolbar.GetCount())
			return	false;			//不正なインデックス
		if(_acAvailableIEToolbar[nIndex].IsVisible())
			return	false;			//すでに表示されている

		clsidIDeskBand = _acAvailableIEToolbar[nIndex].clsidToolbar;

		pwndDummy = new CDummyWnd(clsidIDeskBand);

		//ダミーウインドウを生成し、リバーに割り当てる。これはIEツールバーに利用
		pwndDummy->Create(m_hWnd,CRect(0,0,500,24),0,WS_CHILD);
		AddSimpleReBarBand(*pwndDummy,0,TRUE);						//1行表示

		//IEツールバー用のrebarのインデックス取得。_wndDummyへ割り当てると同時にrebarを非表示に
		{
			UINT	nCount;

			nCount = (UINT)::SendMessage(m_hWndToolBar,RB_GETBANDCOUNT,NULL,NULL);
			pwndDummy->_nBandID = nCount + ATL_IDW_BAND_FIRST - 1;

			if(m_view.IsWindow() && m_view.GetPageCount())
			{
				//IEツールバーの生成
				//タブの数だけ一気に生成しているから効率が悪すぎる。
				//アクティブになったときに生成すべき
				{
					size_t	i;
					size_t	nSize;
					CTabBrowser100View*	pView;

					nSize = m_view.GetPageCount();
					for(i = 0; i < nSize; i++)
					{
						pView = GetPageView(i);
						if(pView == NULL)
							continue;

						if(i == m_view.GetActivePage())
							pView->AddIEToolbar(*pwndDummy,true);		// IEツールバー生成
						else
							pView->AddIEToolbar(*pwndDummy,false);		// IEツールバー生成
					}
				}
				pwndDummy->ShowRebar(true);			//表示する
			}
			else
				pwndDummy->ShowRebar(false);		//非表示にする
		}

		_acAvailableIEToolbar[nIndex].pwndDummy = pwndDummy;

		return	true;
	}

tabbrowser254.gif IDeskBandの親ウインドウとして利用するpwndDummyとIsVisibleを用意した。ただしIsVisibleは実際にIDeskBandが表示状態かどうかまではチェックしていない。
	//■変更 pwndDummyを追加
	class	CAvailableIEToolbar
	{
	public:
		CAtlString	strName;
		IID			clsidToolbar;
		CDummyWnd*	pwndDummy;

		CAvailableIEToolbar()
		{
			pwndDummy = NULL;
			clsidToolbar = GUID_NULL;
		}

		CAvailableIEToolbar(const IID& clsid,LPCTSTR pszName)
		{
			pwndDummy = NULL;
			clsidToolbar = clsid;
			strName = pszName;
		}

		bool	IsVisible(void)
		{
			if(pwndDummy == NULL || pwndDummy->IsWindow() == FALSE)
				return	false;
			return	true;
		}
	};

tabbrowser249.gif デストラクタ内のCDummyWndのdelete処理も_acAvailableIEToolbarに変更する。
	//■変更
	~CMainFrame()
	{
		size_t	i;
		size_t	nSize;

		nSize = _acAvailableIEToolbar.GetCount();
		for(i = 0; i < nSize; i++)
		{
			if(_acAvailableIEToolbar[i].pwndDummy == NULL)
				continue;
			delete	_acAvailableIEToolbar[i].pwndDummy;
			_acAvailableIEToolbar[i].pwndDummy = NULL;
		}
	}

tabbrowser251.gif _acAvailableIEToolbarのCDummyWndを取得するように変更する。
	//■変更
	//タブが選択されたときの処理
	LRESULT OnTabPageActivated(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		if(pnmh == NULL)
			return	0;

		//全てのビューに対してタブ変更があったことを通知
		{
			int		i;
			int		nCount;
			CTabBrowser100View*	pView;

			nCount = m_view.GetPageCount();
			for(i = 0; i < nCount; i++)
			{
				pView = GetPageView(i);
				if(pView == NULL)
					continue;

				if(pnmh->idFrom == i)
					pView->OnTabPageChange(true);
				else
				{
					pView->OnTabPageChange(false);
					pView->SetStatusBar(m_hWndStatusBar,false);	//ステータスバー解放
				}
			}

			pView = GetActivePageView();
			if(pView)
				pView->SetStatusBar(m_hWndStatusBar,true);		//ステータスバーセット
		}


		bool		ret;
		CTabBrowser100View*	pView;
		size_t		i;
		size_t		nSize;
		bool		bVisible;
		nSize = _acAvailableIEToolbar.GetCount();

		pView = GetActivePageView();		//アクティブビュー取得
		if(pView == NULL)
			bVisible = false;
		else
			bVisible = true;

		for(i = 0; i < nSize; i++)
		{
			if(_acAvailableIEToolbar[i].pwndDummy)
				_acAvailableIEToolbar[i].pwndDummy->ShowRebar(bVisible);				//リバー表示
		}

		if(pView == NULL)
			return	0;

		//アドレスバーのURL変更
		{
			CAtlString	strURL;

			ret = pView->GetURL(strURL);		//URL取得
			if(ret)
				_wndAddressBar.SetWindowText(strURL);
		}

		return	0;
	}

tabbrowser252.gif _acAvailableIEToolbarのCDummyWndを取得するように変更する。
	//■変更
	//タブの新規作成
	//nPosはタブを追加する場所。nPos<0(もしくはnPos=m_view.GetPageCount())で一番後ろ、
	//そのほかの数値はCTabView::InsertPageにそのまま渡す
	//
	//戻ったポインタは自動削除されるため、deleteの必要なし
	//
	CTabBrowser100View*	CreateNewTab(LPCTSTR pszURL,LPCTSTR pszTitle,int nPos=-1,int nImage=-1)
	{
		bool	ret;
		CTabBrowser100View*	pView;

		pView = new CTabBrowser100View(&m_view);	//タブビューのポインタを渡す
		if(pView == NULL)
			return	NULL;

		//ビューウインドウ生成
		if(pszURL == NULL || *pszURL == NULL)
			pView->Create(m_view, rcDefault, _T("about:blank"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
		else
			pView->Create(m_view, rcDefault, pszURL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
		if(pView->IsWindow() == FALSE)
		{
			delete	pView;
			return	NULL;		//ビューウインドウ生成失敗
		}

		//タブにビューを割り当てる
		if(nPos < 0)
			nPos = m_view.GetPageCount();
		if(pszTitle)
			ret = m_view.InsertPage(nPos,pView->m_hWnd,pszTitle,nImage,pView);
		else
			ret = m_view.InsertPage(nPos,pView->m_hWnd,_T(""),nImage,pView);

		if(ret == false)
		{
			delete	pView;
			return	NULL;		//タブへの追加失敗
		}

		//IEツールバーの生成
		{
			size_t	i;
			size_t	nSize;

			nSize = _acAvailableIEToolbar.GetCount();
			for(i = 0; i < nSize; i++)
			{
				if(_acAvailableIEToolbar[i].pwndDummy)
					pView->AddIEToolbar(*_acAvailableIEToolbar[i].pwndDummy,true);		//IEツールバー生成
			}
		}

		return	pView;
	}

tabbrowser255.gif これまでOnCreateでは見つかったすべてのIEツールバーを表示していた。これをIEツールバーを探すにとどめる。
	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		// コマンドバー ウィンドウの作成
		HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
		// メニューのアタッチ
		m_CmdBar.AttachMenu(GetMenu());
		// コマンドバー画像の読み込み
		m_CmdBar.LoadImages(IDR_MAINFRAME);
		// 以前のメニューの削除
		SetMenu(NULL);

		HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);

		CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
		AddSimpleReBarBand(hWndCmdBar);
		AddSimpleReBarBand(hWndToolBar, NULL, TRUE);

		_wndAddressBar.Create(m_hWnd,CRect(0,0,200,200),0,WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_CLIPCHILDREN,WS_EX_CLIENTEDGE);
		AddSimpleReBarBand(_wndAddressBar,0,TRUE);	//アドレスバーを1列で表示


		//■削除
		//{
		//	size_t	i;
		//	size_t	nSize;

		//	EnumAvailableIEToolbar();

		//	nSize = _acAvailableIEToolbar.GetCount();
		//	for(i = 0; i < nSize; i++)
		//	{
		//		AddIEToolbar(_acAvailableIEToolbar[i].clsidToolbar);
		//	}
		//}

		//■追加
		EnumAvailableIEToolbar();			//IEツールバーの列挙

		CreateSimpleStatusBar();

		m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);

		UIAddToolBar(hWndToolBar);
		UISetCheck(ID_VIEW_TOOLBAR, 1);
		UISetCheck(ID_VIEW_STATUS_BAR, 1);

		// メッセージ フィルターおよび画面更新用のオブジェクト登録
		CMessageLoop* pLoop = _Module.GetMessageLoop();
		ATLASSERT(pLoop != NULL);
		pLoop->AddMessageFilter(this);
		pLoop->AddIdleHandler(this);

		CMenuHandle menuMain = m_CmdBar.GetMenu();
		m_view.SetWindowMenu(menuMain.GetSubMenu(WINDOW_MENU_POSITION));

		_cTabImageList.Create(16,16,ILC_COLOR | ILC_MASK,1,10);
		m_view.SetImageList(_cTabImageList);

		return 0;
	}

tabbrowser256.gif 最後にビューウインドウ側でIEツールバーを非表示にする処理を追加する。 今回は表示/非表示は生成/破棄により実現している。そのためタブが多い場合はかなり負荷がかかってしまう。
	//■追加
	bool	RemoveIEToolbar(HWND hWndIERebar)
	{
		size_t	i;
		size_t	nSize;

		nSize = _aToolbarInfo.GetCount();
		for(i = 0; i < nSize; i++)
		{
			if(_aToolbarInfo[i]._hWndRebarIE != hWndIERebar)
				continue;

			_aToolbarInfo[i]._hWndRebarIE = NULL;

			if(_aToolbarInfo[i]._pIDeskBand == NULL)
				return	true;

			_aToolbarInfo[i]._pIDeskBand->ShowDW(FALSE);
			_aToolbarInfo[i]._pIDeskBand->CloseDW(0);
			_aToolbarInfo[i]._pIDeskBand = NULL;

			return	true;
		}

		return	false;
	}

tabbrowser257.gif
これでビルド/実行すると、ツールバー部分の右クリックメニューによりInternet Explorer用ツールバーを選んで表示/非表示できるようになった。

次回はお気に入りメニューをきちんとしたものに作り直す準備をする。

ファイルをダウンロード

2009年04月12日

第55回 インストールされているすべてのIEツールバーを表示する (タブブラウザーを作る)

tabbrowser245.gif
前回までの作業で「Googleツールバー」を複数表示できるようになった。今回は「Googleツールバー」だけでなく、ほかのInternet Explorer用ツールバーも表示できるようにする。

まず、現在Internet Explorer用として登録されている利用可能なツールバー情報を保存しておくためのクラスを用意する。
	//■追加
	class	CAvailableIEToolbar
	{
	public:
		CAtlString	strName;
		IID			clsidToolbar;

		CAvailableIEToolbar()
		{
			clsidToolbar = GUID_NULL;
		}

		CAvailableIEToolbar(const IID& clsid,LPCTSTR pszName)
		{
			clsidToolbar = clsid;
			strName = pszName;
		}
	};

	CAtlArray<CAvailableIEToolbar>	_acAvailableIEToolbar;	//■追加



	//■追加
	//指定したHKEY内のレジストリキー名を列挙
	bool	RegEnumKeyName(HKEY hKey,CAtlArray<CAtlString>& astrClsid)
	{
		int		i;
		LONG	nRet;

		i = 0;
		while(1)
		{
			TCHAR	pszName[2048];
			DWORD	dwLen;
			DWORD	dwType;

			//レジストリ内を列挙
			dwLen = 2048;
			*pszName = NULL;
			nRet = ::RegEnumValue(hKey,i++,pszName,&dwLen,NULL,&dwType,NULL,NULL);
			if(nRet != ERROR_SUCCESS)
				break;
			if(pszName == NULL || *pszName == NULL)
				continue;

			astrClsid.Add(pszName);
		}

		return	true;
	}

tabbrowser246.gif レジストリから利用可能なIE用ツールバーのCLSIDを取得する。HKEY_CURRENT_USERとHKEY_LOCAL_MACHINEの2箇所に保存されているため、それら両方を読み込み重複チェックをしてから保存しておく。
	//■追加
	//レジストリに登録されているIEツールバーを見つけ
	//_acAvailableIEToolbarに格納する
	bool	EnumAvailableIEToolbar(void)
	{
		LONG		nRet;
		CRegKey		cRegistry;
		CAtlArray<CAtlString>	astrClsid;

		_acAvailableIEToolbar.RemoveAll();

		//最初にレジストリからIEツールバーかもしれないレジストリキーをすべて列挙して
		//astrClsidへ格納

		//ユーザー専用のIEツールバーかもしれないレジストリキーを列挙
		nRet = cRegistry.Open(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Internet Explorer\\Toolbar\\WebBrowser"));
		if(nRet == ERROR_SUCCESS)
		{
			RegEnumKeyName(cRegistry.m_hKey,astrClsid);
			cRegistry.Close();
		}

		//ユーザー共通のIEツールバーかもしれないレジストリキーを列挙
		nRet = cRegistry.Open(HKEY_LOCAL_MACHINE,_T("Software\\Microsoft\\Internet Explorer\\Toolbar"));
		if(nRet == ERROR_SUCCESS)
		{
			bool	bFind;
			size_t	i;
			size_t	j;
			size_t	nSize;
			CAtlArray<CAtlString>	astrClsid2;

			RegEnumKeyName(cRegistry.m_hKey,astrClsid2);
			cRegistry.Close();

			//重複チェックして重複していないものだけastrClsid2からastrClsidへコピー
			nSize = astrClsid.GetCount();
			for(i = 0; i < astrClsid2.GetCount(); i++)
			{
				bFind = false;
				for(j = 0; j < nSize; j++)
				{
					if(astrClsid[j].CompareNoCase(astrClsid2[i]))
						continue;

					bFind = true;		//重複が見つかった
					break;
				}

				if(bFind)
					continue;

				//重複はなかった
				astrClsid.Add(astrClsid2[i]);
			}
		}


		//astrClsidに格納されているIEツールバーかもしれないレジストリキーが
		//IEツールバーかどうかをチェックして_acAvailableIEToolbarへ追加

		size_t	i;
		size_t	nSize;

		nSize = astrClsid.GetCount();
		for(i = 0; i < nSize; i++)
		{
			HRESULT	hr;
			GUID	rclsid;
			CAtlStringW	ustrName;
			CAtlString	strName;

			CComPtr<IDeskBand>	pIDeskBand;

			//文字列からIIDへ変換
			ustrName = astrClsid[i];
			hr = ::CLSIDFromString(ustrName.GetBuffer(0),&rclsid);
			if(FAILED(hr))
				continue;

			//ためしにCoCreateしてIDeskBandかをチェック
			hr = ::CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER,IID_IDeskBand,(void**)&pIDeskBand);
			if(pIDeskBand == NULL)
				continue;
			pIDeskBand = NULL;

			/////////////////////////
			//IEツールバーが見つかった

			CLSIDtoUIString(rclsid,strName);
			_acAvailableIEToolbar.Add(CAvailableIEToolbar(rclsid,strName));
		}

		return	true;
	}

tabbrowser244.gif GUIDなCLSIDだけでは不便なので、CLSIDから「Google Toolbar」や「MSN Toolbar」というような表示用文字列を取得する。今回はレジストリを参照し、CLSIDとPROGIDの2つのみをチェックするようにした。
	//■追加
	//CLSIDをUI用の文字列に変換する
	bool	CLSIDtoUIString(const IID& rclsid,CAtlString& strText)
	{
		HRESULT		hr;
		LONG		nRet;
		ULONG		nChars;
		TCHAR		pszName[2048];
		WCHAR*		pwszString;
		CRegKey		cReg2;
		CAtlString	strKey;

		strText = _T("");

		/////////////////////////////////////
		// HKEY_CLASSES_ROOT\\CLSID\\{xxxxxxxx}
		//からUI名の取得を試みる
		//

		pwszString = NULL;
		hr = ::StringFromCLSID(rclsid,&pwszString);
		if(FAILED(hr))
			return	false;

		strKey = _T("CLSID\\") + (CAtlString)pwszString;
		::CoTaskMemFree(pwszString);

		nRet = cReg2.Open(HKEY_CLASSES_ROOT,strKey);
		if(nRet == ERROR_SUCCESS)
		{
			nChars = 2048;
			nRet = cReg2.QueryStringValue(NULL,pszName,&nChars);
			cReg2.Close();
			if(nRet == ERROR_SUCCESS)
			{
				strText = pszName;
				if(strText != _T(""))
					return	true;
			}
		}


		/////////////////////////////////////
		// HKEY_CLASSES_ROOT\\xxxx.xxxx.xxxx
		//(プログラムID)からUI名の取得を試みる
		//

		pwszString = NULL;
		hr = ::ProgIDFromCLSID(rclsid,&pwszString);
		strKey = pwszString;
		::CoTaskMemFree(pwszString);

		nRet = cReg2.Open(HKEY_CLASSES_ROOT,strKey);
		if(nRet == ERROR_SUCCESS)
		{
			nChars = 2048;
			nRet = cReg2.QueryStringValue(NULL,pszName,&nChars);
			cReg2.Close();
			if(nRet == ERROR_SUCCESS)
			{
				strText = pszName;
				if(strText != _T(""))
					return	true;
			}
		}

		strText = strKey;

		return	false;
	}

tabbrowser247.gif 最後にOnCreate内で利用可能なIE用ツールバーを探しだして、そのすべてを表示するようにした。
	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		// コマンドバー ウィンドウの作成
		HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
		// メニューのアタッチ
		m_CmdBar.AttachMenu(GetMenu());
		// コマンドバー画像の読み込み
		m_CmdBar.LoadImages(IDR_MAINFRAME);
		// 以前のメニューの削除
		SetMenu(NULL);

		HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);

		CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
		AddSimpleReBarBand(hWndCmdBar);
		AddSimpleReBarBand(hWndToolBar, NULL, TRUE);

		_wndAddressBar.Create(m_hWnd,CRect(0,0,200,200),0,WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_CLIPCHILDREN,WS_EX_CLIENTEDGE);
		AddSimpleReBarBand(_wndAddressBar,0,TRUE);	//アドレスバーを1列で表示

		//■変更
		{
			size_t	i;
			size_t	nSize;

			EnumAvailableIEToolbar();

			nSize = _acAvailableIEToolbar.GetCount();
			for(i = 0; i < nSize; i++)
			{
				AddIEToolbar(_acAvailableIEToolbar[i].clsidToolbar);
			}
		}


		CreateSimpleStatusBar();

		m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);

		UIAddToolBar(hWndToolBar);
		UISetCheck(ID_VIEW_TOOLBAR, 1);
		UISetCheck(ID_VIEW_STATUS_BAR, 1);

		// メッセージ フィルターおよび画面更新用のオブジェクト登録
		CMessageLoop* pLoop = _Module.GetMessageLoop();
		ATLASSERT(pLoop != NULL);
		pLoop->AddMessageFilter(this);
		pLoop->AddIdleHandler(this);

		CMenuHandle menuMain = m_CmdBar.GetMenu();
		m_view.SetWindowMenu(menuMain.GetSubMenu(WINDOW_MENU_POSITION));

		_cTabImageList.Create(16,16,ILC_COLOR | ILC_MASK,1,10);
		m_view.SetImageList(_cTabImageList);

		return 0;
	}

tabbrowser248.gif
これで実行すると、インストールされているIE用ツールバーの分だけリバーが作られ、複数のIE用ツールバーが表示されるようになった。

一番上の「DinopSearchBar」は内部処理がIEに直接アクセスする形で実装されているためきちんと表示されず、2つ目の「Adobe PDF」はリバーサイズがきちんと設定されず、4番目の「Gooスティック」はウインドウサイズ変更時の再描画処理がきちんとされていないなど、まだきちんと動かない部分も多いが、とりあえずはいろいろなIE用のツールバーが利用可能になった。

次回はメニューからIE用ツールバーを選択して表示できるようにする。

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

第54回 「Googleツールバー」を2つ表示する (タブブラウザーを作る)

tabbrowser237.gif
前回はビューウインドウを中心にIEツールバーを複数利用する準備をした。今回はCMainFrame側を中心に修正を行う。

まずIEツールバーの親ウインドウとなるCDummyWndの中身を変更する。「Googleツールバー」以外のIEツールバーにも対応できるように_clsidIDeskBandを用意し、それを取得するための独自メッセージ処理を用意した。

またこれまでリバーのインデックス渡しにバグがあったので、きちんとIDからインデックスを取得して渡すように修正した。
	//■処理内容変更
	class	CDummyWnd	: public CWindowImpl<CDummyWnd>
	{
		IID		_clsidIDeskBand;	//IEツールバーのGUID
	public:
		UINT	_nBandID;			//リバーのID

		CDummyWnd(IID clsidIDeskBand)
		{
			_nBandID = -1;
			_clsidIDeskBand = clsidIDeskBand;
		}

		BEGIN_MSG_MAP(CDummyWnd)
			MESSAGE_HANDLER(WM_DNP_GETRBINDEX, OnDnpRbIndex)
			MESSAGE_HANDLER(WM_DNP_GETCLSID, OnDnpGetClsid)
			MESSAGE_HANDLER(WM_SIZE, OnSize)
		END_MSG_MAP()


		//リバー操作用のINDEX取得
		LRESULT OnDnpRbIndex(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			UINT*	pnIndex = (UINT*)wParam;

			if(pnIndex)
				*pnIndex = GetRebarIndex();

			return	0;
		}

		LRESULT OnDnpGetClsid(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			IID*	piid = (IID*)wParam;

			if(piid)
				*piid = _clsidIDeskBand;

			return	0;
		}


		UINT	GetRebarIndex(void)
		{
			UINT	nIndex = -1;

			if(_nBandID == -1 || IsWindow() == FALSE)
				return	nIndex;

			nIndex = ::SendMessage(::GetParent(m_hWnd),RB_IDTOINDEX,(WPARAM)_nBandID,NULL);

			return	nIndex;
		}


		LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			bHandled = FALSE;
			if(_nBandID == -1)
				return	0;

			//このウインドウの直下にあるchildウインドウ=IEツールバーをrebarに全体表示する

			CRect	rect;
			HWND	hWnd;

			//rebarサイズ取得
			rect.left	= 0;
			rect.top	= 0;
			rect.right	= LOWORD(lParam);
			rect.bottom	= HIWORD(lParam);

			//子ウインドウを検索してサイズ調節
			hWnd = NULL;
			while(1)
			{
				hWnd = ::FindWindowEx(m_hWnd,hWnd,NULL,NULL);
				if(hWnd == NULL)
					break;

				::MoveWindow(hWnd,rect.left,rect.top,rect.Width(),rect.Height(),FALSE);
			}

			return	0;
		}


		bool	ShowRebar(bool bVisible)
		{
			if(_nBandID == -1 || IsWindow() == FALSE)
				return	false;

			::SendMessage(::GetParent(m_hWnd),RB_SHOWBAND,(WPARAM)GetRebarIndex(),(LPARAM)(bVisible ? TRUE : FALSE));

			return	true;
		}
	};

tabbrowser238.gif そしてCDummyWndをCAtlArrayにより配列化、簡単にCDummyWndを作れるようにAddIEToolbar関数を用意した。
	CAtlArray<CDummyWnd*>	_apwndDummy;		//■変更


	//■追加 処理はOnCreateから移動&変更
	bool	AddIEToolbar(IID clsidIDeskBand)
	{
		CDummyWnd*	pwndDummy;

		pwndDummy = new CDummyWnd(clsidIDeskBand);

		//ダミーウインドウを生成し、リバーに割り当てる。これはIEツールバーに利用
		pwndDummy->Create(m_hWnd,CRect(0,0,500,24),0,WS_CHILD);
		AddSimpleReBarBand(*pwndDummy,0,TRUE);						//1行表示

		//IEツールバー用のrebarのインデックス取得。_wndDummyへ割り当てると同時にrebarを非表示に
		{
			UINT	nCount;

			nCount = (UINT)::SendMessage(m_hWndToolBar,RB_GETBANDCOUNT,NULL,NULL);
			pwndDummy->_nBandID = nCount + ATL_IDW_BAND_FIRST - 1;

			if(m_view.IsWindow() && m_view.GetPageCount())
			{
				//IEツールバーの生成
				//タブの数だけ一気に生成しているから効率が悪すぎる。
				//アクティブになったときに生成すべき
				{
					size_t	i;
					size_t	nSize;
					CTabBrowser100View*	pView;

					nSize = m_view.GetPageCount();
					for(i = 0; i < nSize; i++)
					{
						pView = GetPageView(i);
						if(pView == NULL)
							continue;

						if(i == m_view.GetActivePage())
							pView->AddIEToolbar(*pwndDummy,true);		// IEツールバー生成
						else
							pView->AddIEToolbar(*pwndDummy,false);		// IEツールバー生成
					}
				}
				pwndDummy->ShowRebar(true);			//表示する
			}
			else
				pwndDummy->ShowRebar(false);		//非表示にする
		}

		_apwndDummy.Add(pwndDummy);

		return	true;
	}

tabbrowser234.gif CDummyWndの配列はnewによる確保にしたので、デストラクタでdeleteしておく。
	//■追加
	~CMainFrame()
	{
		size_t	i;
		size_t	nSize;

		nSize = _apwndDummy.GetCount();
		for(i = 0; i < nSize; i++)
		{
			if(_apwndDummy[i] == NULL)
				continue;
			delete	_apwndDummy[i];
			_apwndDummy[i] = NULL;
		}
	}

tabbrowser235.gif 後はこれまで_wndDummyにアクセスしていた部分を配列に沿ったものに修正する。 まずはリバーの表示/非表示を切り替える処理だ。
	//タブが選択されたときの処理
	LRESULT OnTabPageActivated(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		if(pnmh == NULL)
			return	0;

		//全てのビューに対してタブ変更があったことを通知
		{
			int		i;
			int		nCount;
			CTabBrowser100View*	pView;

			nCount = m_view.GetPageCount();
			for(i = 0; i < nCount; i++)
			{
				pView = GetPageView(i);
				if(pView == NULL)
					continue;

				if(pnmh->idFrom == i)
					pView->OnTabPageChange(true);
				else
				{
					pView->OnTabPageChange(false);
					pView->SetStatusBar(m_hWndStatusBar,false);	//ステータスバー解放
				}
			}

			pView = GetActivePageView();
			if(pView)
				pView->SetStatusBar(m_hWndStatusBar,true);		//ステータスバーセット
		}


		bool		ret;
		CTabBrowser100View*	pView;
		size_t		i;						//■追加
		size_t		nSize;					//■追加
		bool		bVisible;				//■追加

		nSize = _apwndDummy.GetCount();		//■追加

		pView = GetActivePageView();		//アクティブビュー取得
		if(pView == NULL)
			bVisible = false;
		else
			bVisible = true;

		//■変更
		for(i = 0; i < nSize; i++)
		{
			if(_apwndDummy[i])
				_apwndDummy[i]->ShowRebar(bVisible);				//リバー表示
		}

		if(pView == NULL)
			return	0;

		//アドレスバーのURL変更
		{
			CAtlString	strURL;

			ret = pView->GetURL(strURL);		//URL取得
			if(ret)
				_wndAddressBar.SetWindowText(strURL);
		}

		return	0;
	}

tabbrowser236.gif タブの新規作成処理。今のところ新しいタブは必ずアクティブに開くので、生成時はAddIEToolbarにtrueを渡している。
	//タブの新規作成
	//nPosはタブを追加する場所。nPos<0(もしくはnPos=m_view.GetPageCount())で一番後ろ、
	//そのほかの数値はCTabView::InsertPageにそのまま渡す
	//
	//戻ったポインタは自動削除されるため、deleteの必要なし
	//
	CTabBrowser100View*	CreateNewTab(LPCTSTR pszURL,LPCTSTR pszTitle,int nPos=-1,int nImage=-1)
	{
		bool	ret;
		CTabBrowser100View*	pView;

		pView = new CTabBrowser100View(&m_view);	//タブビューのポインタを渡す
		if(pView == NULL)
			return	NULL;

		//ビューウインドウ生成
		if(pszURL == NULL || *pszURL == NULL)
			pView->Create(m_view, rcDefault, _T("about:blank"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
		else
			pView->Create(m_view, rcDefault, pszURL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
		if(pView->IsWindow() == FALSE)
		{
			delete	pView;
			return	NULL;		//ビューウインドウ生成失敗
		}

		//タブにビューを割り当てる
		if(nPos < 0)
			nPos = m_view.GetPageCount();
		if(pszTitle)
			ret = m_view.InsertPage(nPos,pView->m_hWnd,pszTitle,nImage,pView);
		else
			ret = m_view.InsertPage(nPos,pView->m_hWnd,_T(""),nImage,pView);

		if(ret == false)
		{
			delete	pView;
			return	NULL;		//タブへの追加失敗
		}

		//■処理変更
		//IEツールバーの生成
		{
			size_t	i;
			size_t	nSize;

			nSize = _apwndDummy.GetCount();
			for(i = 0; i < nSize; i++)
			{
				if(_apwndDummy[i])
					pView->AddIEToolbar(*_apwndDummy[i],true);		//IEツールバー生成
			}
		}

		return	pView;
	}

tabbrowser239.gif OnCreateで「Googleツールバー」のGUIDを利用してリバーを作る。今回はAddIEToolbarを2度呼ぶことで2つの「Googleツールバー」を表示することにした。
	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		// コマンドバー ウィンドウの作成
		HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
		// メニューのアタッチ
		m_CmdBar.AttachMenu(GetMenu());
		// コマンドバー画像の読み込み
		m_CmdBar.LoadImages(IDR_MAINFRAME);
		// 以前のメニューの削除
		SetMenu(NULL);

		HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);

		CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
		AddSimpleReBarBand(hWndCmdBar);
		AddSimpleReBarBand(hWndToolBar, NULL, TRUE);

		_wndAddressBar.Create(m_hWnd,CRect(0,0,200,200),0,WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_CLIPCHILDREN,WS_EX_CLIENTEDGE);
		AddSimpleReBarBand(_wndAddressBar,0,TRUE);	//アドレスバーを1列で表示

		//■変更
		//Googleツールバー生成
		{
			GUID	rclsid;
			WCHAR	pszGUID[] = {L"{2318C2B1-4965-11d4-9B18-009027A5CD4F}"};		//GoogleツールバーのGUID

			::CLSIDFromString(pszGUID,&rclsid);

			AddIEToolbar(rclsid);
			AddIEToolbar(rclsid);		//2つ生成
		}


		CreateSimpleStatusBar();

		m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);

		UIAddToolBar(hWndToolBar);
		UISetCheck(ID_VIEW_TOOLBAR, 1);
		UISetCheck(ID_VIEW_STATUS_BAR, 1);

		// メッセージ フィルターおよび画面更新用のオブジェクト登録
		CMessageLoop* pLoop = _Module.GetMessageLoop();
		ATLASSERT(pLoop != NULL);
		pLoop->AddMessageFilter(this);
		pLoop->AddIdleHandler(this);

		CMenuHandle menuMain = m_CmdBar.GetMenu();
		m_view.SetWindowMenu(menuMain.GetSubMenu(WINDOW_MENU_POSITION));

		_cTabImageList.Create(16,16,ILC_COLOR | ILC_MASK,1,10);
		m_view.SetImageList(_cTabImageList);

		return 0;
	}

tabbrowser240.gif 実際のIEツールバー生成処理を担っているビューウインドウ側のCIEToolbarInfoの中で、新しい独自メッセージによりGUIDを取得して利用するようにする。
		//■変更
		//IEツールバーを生成する
		bool	Create(IWebBrowser2* pIWebBrowser2,bool bVisible)
		{
			IID		rclsid = GUID_NULL;

			if(::IsWindow(_hWndRebarIE))
				::SendMessage(_hWndRebarIE,WM_DNP_GETCLSID,(WPARAM)&rclsid,NULL);

			return	CreateIEToolbar(pIWebBrowser2,_hWndRebarIE,&_pIDeskBand,rclsid,bVisible);
		}

tabbrowser241.gif ビューウインドウ側のAddIEToolbarで引数にbVisibleを用意した。
	//■変更
	bool	AddIEToolbar(HWND hWndIERebar,bool bVisible)
	{
		size_t	nIndex;

		nIndex = _aToolbarInfo.Add(CIEToolbarInfo(hWndIERebar));
		_aToolbarInfo[nIndex].Create(_pIWebBrowser2,bVisible);

		return	true;
	}

tabbrowser242.gif 最後に「stdafx.h」内に独自メッセージの定義を追加する。
#define	WM_DNP_CHANGEFOCUS		(WM_APP + 1)
#define	WM_DNP_CREATENEWTAB		(WM_APP + 2)
#define	WM_DNP_CHANGEADDRESS	(WM_APP + 3)
#define	WM_DNP_SCRIPTERROR		(WM_APP + 6)
#define	WM_DNP_SHOWSCRIPTERROR	(WM_APP + 7)
#define	WM_DNP_SHOWPRIVACYREPORT	(WM_APP + 8)
#define	WM_DNP_SHOWPSECURITYREPORT	(WM_APP + 9)
#define	WM_DNP_SHOWZONECONFIGURE	(WM_APP + 10)
#define	WM_DNP_GETRBINDEX		(WM_APP + 11)
#define	WM_DNP_GETCLSID			(WM_APP + 12)		//■追加

tabbrowser243.gif
これでビルド/実行すると「Googleツールバー」が2つ表示されるようになった。

次回はInternet Explorerで現在利用可能なツールバーを列挙して使えるようにする。

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

前の10件 8  9  10  11  12  13  14  15  16  17  18