« 2009年03月 | メイン | 2010年05月 »

前の10件 1  2  3  4  5  6  7

2009年04月 記事一覧

第49回 Navigate時のクリック音をON/OFFする (タブブラウザーを作る)

tabbrowser207.gif
Internet ExplorerやIEコントロールでは、リンクをクリックするときなどNavigateが開始されるときにプチッというクリック音が鳴る。今回はそのクリック音を切る機能を追加する。

設定方法はいくつか考えられるが、今回はツールバーのボタンとして実装する。そのためのボタンをリソースウインドウで追加する。IDは「ID_CLICK_SOUND」とした。

tabbrowser208.gif
作成したボタンをUI_MAPに追加する。そしてOnIdleで現在音が鳴る状態なら押下状態、鳴らないならノーマルなボタン状態に設定する。

現在、クリック音が鳴るかどうかはCoInternetIsFeatureEnabledで取得できる。ただしこの関数はIE6&XPSP2以降でのみ使える関数だ。
	virtual BOOL OnIdle()
	{
		CTabBrowser100View*	pView;

		pView = GetActivePageView();

		//「戻る」「進む」ボタンの状態設定
		{
			bool	bBack = false;
			bool	bNext = false;

			if(pView)
				pView->IsButtonEnable(bBack,bNext);

			UIEnable(ID_IE_BACK,bBack ? TRUE : FALSE);
			UIEnable(ID_IE_NEXT,bNext ? TRUE : FALSE);
		}

		//「編集」メニュー項目更新
		{
			bool	bCopy		= false;
			bool	bCut		= false;
			bool	bPaste		= false;
			bool	bSelectAll	= false;

			if(pView)
				pView->IsEnable_EditCommand(bCopy,bCut,bPaste,bSelectAll);

			UIEnable(ID_EDIT_COPY		,bCopy ? TRUE : FALSE);
			UIEnable(ID_EDIT_CUT		,bCut ? TRUE : FALSE);
			UIEnable(ID_EDIT_PASTE		,bPaste ? TRUE : FALSE);
			UIEnable(ID_EDIT_SELECT_ALL	,bSelectAll ? TRUE : FALSE);
		}


		//未実装メニュー項目を全部無効に設定
		{
			UIEnable(ID_FILE_COPY_TAB, FALSE);
			UIEnable(ID_FILE_NEW_WINDOW, FALSE);
			UIEnable(ID_FILE_NEW_SESSION, FALSE);
			UIEnable(ID_FILE_OPEN, FALSE);
			UIEnable(ID_FILE_EDIT, FALSE);
//			UIEnable(ID_FILE_SAVE, FALSE);				//有効/無効設定必要
//			UIEnable(ID_FILE_SAVE_AS, FALSE);			//有効/無効設定必要
			UIEnable(ID_FILE_SEND_PAGE, FALSE);
			UIEnable(ID_FILE_SEND_LINK, FALSE);
//			UIEnable(ID_FILE_CREATE_SHORTCUT, FALSE);	//有効/無効設定必要
			UIEnable(ID_FILE_INPORT_EXPORT, FALSE);
//			UIEnable(ID_FILE_PROPERTY, FALSE);			//有効/無効設定必要
			UIEnable(ID_FILE_OFFLINE, FALSE);
//			UIEnable(ID_EDIT_FIND, FALSE);				//有効/無効設定必要
			UIEnable(ID_VIEW_FIXED, FALSE);
			UIEnable(ID_VIEW_QUICKTAB, FALSE);
			UIEnable(ID_VIEW_FAVORITE, FALSE);
			UIEnable(ID_VIEW_HISTORY, FALSE);
			UIEnable(ID_VIEW_FEED, FALSE);
			UIEnable(ID_VIEW_GO, FALSE);
(...省略...)
			UIEnable(ID_TOOL_WINDOWS_UPDATE, FALSE);
			UIEnable(ID_TOOL_DEVELOP, FALSE);
			UIEnable(ID_TOOL_INTERNET_OPTION, FALSE);
		}

		//■追加
		//ツールバーボタン。クリック音を鳴らす
		{
			bool	bDisable;

			bDisable = IsFeatureEnabled(FEATURE_DISABLE_NAVIGATION_SOUNDS);
			UISetCheck(ID_CLICK_SOUND,! bDisable);
		}

		UIUpdateToolBar();
		return FALSE;
	}


	//■追加
	bool	IsFeatureEnabled(INTERNETFEATURELIST FeatureEntry)
	{
		HRESULT	hr;

		hr = ::CoInternetIsFeatureEnabled(FeatureEntry,SET_FEATURE_ON_PROCESS);
		if(hr == S_OK)
			return	true;
		return	false;
	}

	BEGIN_UPDATE_UI_MAP(CMainFrame)
		UPDATE_ELEMENT(ID_CLICK_SOUND, UPDUI_TOOLBAR)		//■追加
		UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
		UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
		UPDATE_ELEMENT(ID_IE_BACK, UPDUI_TOOLBAR)
		UPDATE_ELEMENT(ID_IE_NEXT, UPDUI_TOOLBAR)
		UPDATE_ELEMENT(ID_EDIT_COPY, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)
		UPDATE_ELEMENT(ID_EDIT_CUT, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)

tabbrowser209.gif
さらにツールバーのボタンがクリックされたときの処理を追加する。

クリック音が鳴らないようにする関数はCoInternetSetFeatureEnabledで、これもXP SP2&IE6以降でのみ使えるAPIとなる。
	BEGIN_MSG_MAP(CMainFrame)
		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)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
		COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
		COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
		COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll)
		COMMAND_ID_HANDLER(ID_FILE_PRINT, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_PRINT_PREVIEW, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_PRINT_SETUP, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_COPY, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_CUT, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_PASTE, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_SELECT_ALL, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_SAVE, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_SAVE_AS, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_CREATE_SHORTCUT, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_PROPERTY, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_FIND, OnFileCommand)
		COMMAND_ID_HANDLER(ID_CLICK_SOUND, OnFileCommand)		//■追加
		COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate)
		COMMAND_ID_HANDLER(ID_IE_BACK, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_NEXT, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_STOP, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_REFRESH, OnIECommand)
		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, OnMenuSelect)
		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 OnFileCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled)
	{
		CTabBrowser100View*	pView;

		pView = GetActivePageView();
		if(pView == NULL)
		{
			bHandled = FALSE;
			return	0;
		}

		bHandled = TRUE;
		switch(wID)
		{
			//■追加
		case	ID_CLICK_SOUND:				//クリック音を再生するかどうかの設定
			{
				bool	bDisable;

				bDisable = IsFeatureEnabled(FEATURE_DISABLE_NAVIGATION_SOUNDS);
				::CoInternetSetFeatureEnabled(FEATURE_DISABLE_NAVIGATION_SOUNDS,SET_FEATURE_ON_PROCESS,bDisable ? FALSE : TRUE);
			}
			break;

		case	ID_FILE_SAVE:				//上書き保存
			pView->Save();
			break;

		case	ID_FILE_SAVE_AS:			//名前を付けて保存
			pView->SaveAs();
			break;

		case	ID_FILE_CREATE_SHORTCUT:	//デスクトップにリンク作成
			pView->CreateShortcut();
			break;

		case	ID_FILE_PROPERTY:			//プロパティ
			pView->ShowProperty();
			break;

		case	ID_EDIT_FIND:				//検索
			pView->ShowFind();
			break;

		case	ID_EDIT_COPY:				//コピー
			pView->Copy();
			break;

		case	ID_EDIT_CUT:				//切り取り
			pView->Cut();
			break;

		case	ID_EDIT_PASTE:				//貼り付け
			pView->Paste();
			break;

		case	ID_EDIT_SELECT_ALL:			//全てを選択
			pView->SelectAll();
			break;

		case	ID_FILE_PRINT:				//印刷
			pView->PrintOrPreview(true);
			break;

		case	ID_FILE_PRINT_PREVIEW:		//印刷プレビュー
			pView->PrintOrPreview(false);
			break;

		case	ID_FILE_PRINT_SETUP:		//印刷セットアップ
			pView->PrintSetup();
			break;

		default:
			bHandled = FALSE;
			break;
		}

		return	0;
	}

tabbrowser210.gif ついにで今までアドレスバーが一列で表示されず、ツールバーの一部が隠れてしまっていたので、アドレスバーが一列で表示されるように修正する。
	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列で表示

		//ダミーウインドウを生成し、リバーに割り当てる。これはIEツールバーに利用
		//ここで生成してしまうと何もないリバーができてしまうが気にしない
		_wndDummy.Create(m_hWnd,CRect(0,0,500,24),0,WS_CHILD);
		AddSimpleReBarBand(_wndDummy,0,TRUE);						//1行表示

		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;
	}

tabbrowser211.gif
これでツールバーにプッシュ式のボタンが追加され、押し込んだ状態のときはクリック音が鳴り、そうでないときはクリック音が鳴らなくなった。

次回からはスクリプトや画像表示のON/OFF用のボタンを用意し、今回のクリック音のように自由に設定できるようにする。

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

2009年04月10日

第50回 「スクリプト実行」「ActiveX実行」「画像表示」などをON/OFFする (タブブラウザーを作る)

tabbrowser212.gif
前回はツールバーにリンククリック時に鳴る音をON/OFFするためのボタンを追加した。今回は同じように「スクリプトの実行」「画像の表示」「AvtiveXコントロールの実行」「BGMの再生」の各項目をON/OFFするため機能を実装する。

「スクリプトの実行」や「ActiveXコントロールの実行」を今回のような手法で制御することはセキュリティ上の弱点となる。セキュリティ的に少しは弱みとならないような切り替え方法を少し考えてみたのだがどうも複雑になりそう。もともとステータスバーやアドレスバーの実装方法などセキュリティ的に最悪な部分がたくさんあるので細かいことは気にせずに単純にON/OFFを切り替えるに留める。

まずツールバーに4つのボタンを追加する。スクリプト実行のON/OFF用の「ID_SCRIPT」、画像表示用の「ID_IMAGE」、ActiveXコントロール実行用の「ID_ACTIVEX」、BGM再生用の「ID_BGM」を用意した。

tabbrowser213.gif 「MainFrm.h」で用意したボタンをツールバーのUI_ELEMENTとして登録すると同時にOnIdle内でボタン状態が設定されるようにする。
	virtual BOOL OnIdle()
	{
		CTabBrowser100View*	pView;

		pView = GetActivePageView();

		//「戻る」「進む」ボタンの状態設定
		{
			bool	bBack = false;
			bool	bNext = false;

			if(pView)
				pView->IsButtonEnable(bBack,bNext);

			UIEnable(ID_IE_BACK,bBack ? TRUE : FALSE);
			UIEnable(ID_IE_NEXT,bNext ? TRUE : FALSE);
		}

		//「編集」メニュー項目更新
		{
			bool	bCopy		= false;
			bool	bCut		= false;
			bool	bPaste		= false;
			bool	bSelectAll	= false;

			if(pView)
				pView->IsEnable_EditCommand(bCopy,bCut,bPaste,bSelectAll);

			UIEnable(ID_EDIT_COPY		,bCopy ? TRUE : FALSE);
			UIEnable(ID_EDIT_CUT		,bCut ? TRUE : FALSE);
			UIEnable(ID_EDIT_PASTE		,bPaste ? TRUE : FALSE);
			UIEnable(ID_EDIT_SELECT_ALL	,bSelectAll ? TRUE : FALSE);
		}


		//未実装メニュー項目を全部無効に設定
		{
			UIEnable(ID_FILE_COPY_TAB, FALSE);
			UIEnable(ID_FILE_NEW_WINDOW, FALSE);
			UIEnable(ID_FILE_NEW_SESSION, FALSE);
			UIEnable(ID_FILE_OPEN, FALSE);
			UIEnable(ID_FILE_EDIT, FALSE);
//			UIEnable(ID_FILE_SAVE, FALSE);				//有効/無効設定必要
//			UIEnable(ID_FILE_SAVE_AS, FALSE);			//有効/無効設定必要
			UIEnable(ID_FILE_SEND_PAGE, FALSE);
			UIEnable(ID_FILE_SEND_LINK, FALSE);
//			UIEnable(ID_FILE_CREATE_SHORTCUT, FALSE);	//有効/無効設定必要
			UIEnable(ID_FILE_INPORT_EXPORT, FALSE);
//			UIEnable(ID_FILE_PROPERTY, FALSE);			//有効/無効設定必要
			UIEnable(ID_FILE_OFFLINE, FALSE);
//			UIEnable(ID_EDIT_FIND, FALSE);				//有効/無効設定必要
			UIEnable(ID_VIEW_FIXED, FALSE);
			UIEnable(ID_VIEW_QUICKTAB, FALSE);
			UIEnable(ID_VIEW_FAVORITE, FALSE);
			UIEnable(ID_VIEW_HISTORY, FALSE);
			UIEnable(ID_VIEW_FEED, FALSE);
(...省略...)
			UIEnable(ID_TOOL_DEVELOP, FALSE);
			UIEnable(ID_TOOL_INTERNET_OPTION, FALSE);
		}

		//ツールバーボタン。クリック音を鳴らす
		{
			bool	bDisable;

			bDisable = IsFeatureEnabled(FEATURE_DISABLE_NAVIGATION_SOUNDS);
			UISetCheck(ID_CLICK_SOUND,! bDisable);
		}

		//■追加
		//ダウンロードコントロール
		if(pView)
		{
			bool	bEnable;

			//何度もフラグを読み出してるので効率が悪いが気にしない
			bEnable = pView->IsEnableAmbientDownloadControl(DLCTL_DLIMAGES);
			UISetCheck(ID_IMAGE,bEnable);

			bEnable = pView->IsEnableAmbientDownloadControl(DLCTL_BGSOUNDS);
			UISetCheck(ID_BGM,bEnable);

			bEnable = pView->IsEnableAmbientDownloadControl(DLCTL_NO_SCRIPTS);
			UISetCheck(ID_SCRIPT,! bEnable);		//反転

			bEnable = pView->IsEnableAmbientDownloadControl(DLCTL_NO_RUNACTIVEXCTLS);
			UISetCheck(ID_ACTIVEX,! bEnable);		//反転
		}

		UIUpdateToolBar();
		return FALSE;
	}


	bool	IsFeatureEnabled(INTERNETFEATURELIST FeatureEntry)
	{
		HRESULT	hr;

		hr = ::CoInternetIsFeatureEnabled(FeatureEntry,SET_FEATURE_ON_PROCESS);
		if(hr == S_OK)
			return	true;
		return	false;
	}

	BEGIN_UPDATE_UI_MAP(CMainFrame)
		UPDATE_ELEMENT(ID_SCRIPT, UPDUI_TOOLBAR)		//■追加
		UPDATE_ELEMENT(ID_IMAGE, UPDUI_TOOLBAR)			//■追加
		UPDATE_ELEMENT(ID_ACTIVEX, UPDUI_TOOLBAR)		//■追加
		UPDATE_ELEMENT(ID_BGM, UPDUI_TOOLBAR)			//■追加
		UPDATE_ELEMENT(ID_CLICK_SOUND, UPDUI_TOOLBAR)
		UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
		UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
		UPDATE_ELEMENT(ID_IE_BACK, UPDUI_TOOLBAR)
		UPDATE_ELEMENT(ID_IE_NEXT, UPDUI_TOOLBAR)
		UPDATE_ELEMENT(ID_EDIT_COPY, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)

tabbrowser214.gif そしてツールバーが押されたときの動作を実装する。
	BEGIN_MSG_MAP(CMainFrame)
		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)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
		COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
		COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
		COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll)
		COMMAND_ID_HANDLER(ID_FILE_PRINT, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_PRINT_PREVIEW, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_PRINT_SETUP, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_COPY, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_CUT, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_PASTE, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_SELECT_ALL, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_SAVE, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_SAVE_AS, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_CREATE_SHORTCUT, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_PROPERTY, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_FIND, OnFileCommand)
		COMMAND_ID_HANDLER(ID_CLICK_SOUND, OnFileCommand)
		COMMAND_ID_HANDLER(ID_IMAGE, OnFileCommand)			//■追加
		COMMAND_ID_HANDLER(ID_BGM, OnFileCommand)			//■追加
		COMMAND_ID_HANDLER(ID_SCRIPT, OnFileCommand)		//■追加
		COMMAND_ID_HANDLER(ID_ACTIVEX, OnFileCommand)		//■追加
		COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate)
		COMMAND_ID_HANDLER(ID_IE_BACK, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_NEXT, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_STOP, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_REFRESH, OnIECommand)
		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, OnMenuSelect)
		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 OnFileCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled)
	{
		CTabBrowser100View*	pView;

		pView = GetActivePageView();
		if(pView == NULL)
		{
			bHandled = FALSE;
			return	0;
		}

		bHandled = TRUE;
		switch(wID)
		{
			//■追加
		case	ID_IMAGE:			//画像表示
		case	ID_BGM:				//BGM再生
		case	ID_SCRIPT:			//スクリプト実行
		case	ID_ACTIVEX:			//ActiveXコントロール実行
			{
				bool	bEnable;
				long	nFlag;

				nFlag = 0;
				switch(wID)
				{
				case	ID_IMAGE:		nFlag = DLCTL_DLIMAGES;					break;
				case	ID_BGM:			nFlag = DLCTL_BGSOUNDS;					break;
				case	ID_SCRIPT:		nFlag = DLCTL_NO_SCRIPTS;				break;
				case	ID_ACTIVEX:		nFlag = DLCTL_NO_RUNACTIVEXCTLS;		break;
				}

				bEnable = pView->IsEnableAmbientDownloadControl(nFlag);
				pView->SetAmbientDownloadControl(nFlag,! bEnable);
				break;
			}

		case	ID_CLICK_SOUND:				//クリック音を再生するかどうかの設定
			{
				bool	bDisable;

				bDisable = IsFeatureEnabled(FEATURE_DISABLE_NAVIGATION_SOUNDS);
				::CoInternetSetFeatureEnabled(FEATURE_DISABLE_NAVIGATION_SOUNDS,SET_FEATURE_ON_PROCESS,bDisable ? FALSE : TRUE);
			}
			break;

		case	ID_FILE_SAVE:				//上書き保存
			pView->Save();
			break;

		case	ID_FILE_SAVE_AS:			//名前を付けて保存
			pView->SaveAs();
			break;

		case	ID_FILE_CREATE_SHORTCUT:	//デスクトップにリンク作成
			pView->CreateShortcut();
			break;

		case	ID_FILE_PROPERTY:			//プロパティ
			pView->ShowProperty();
			break;

		case	ID_EDIT_FIND:				//検索
			pView->ShowFind();
			break;

		case	ID_EDIT_COPY:				//コピー
			pView->Copy();
			break;

		case	ID_EDIT_CUT:				//切り取り
			pView->Cut();
			break;

		case	ID_EDIT_PASTE:				//貼り付け
			pView->Paste();
			break;

		case	ID_EDIT_SELECT_ALL:			//全てを選択
			pView->SelectAll();
			break;

		case	ID_FILE_PRINT:				//印刷
			pView->PrintOrPreview(true);
			break;

		case	ID_FILE_PRINT_PREVIEW:		//印刷プレビュー
			pView->PrintOrPreview(false);
			break;

		case	ID_FILE_PRINT_SETUP:		//印刷セットアップ
			pView->PrintSetup();
			break;

		default:
			bHandled = FALSE;
			break;
		}

		return	0;
	}

tabbrowser217.gif スクリプト実行のON/OFFはDISPID_AMBIENT_DLCONTROLで行われる。ここでDLCTL_DLIMAGES(画像の表示)などのフラグを設定することでON/OFFできる。
#include "mshtmdid.h"

//■追加
MIDL_INTERFACE("A419B9D4-2940-44c9-B0E0-A0B0FC61046F")
IAmbientControl : public IUnknown
{
public:
	STDMETHOD (put_nAmbient)(long newVal) = 0;
	STDMETHOD (get_nAmbient)(long* pnVal) = 0;
};

//■追加
class CAmbientControl :
	public CComObjectRootEx<CComSingleThreadModel>
	,public	IDispatchImpl<IDispatch>
	,public IOleClientSite
	,public IAmbientControl
{
	long	_nAmbient;

public:

	CAmbientControl()
	{
		_nAmbient = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_BGSOUNDS;
	}

    BEGIN_COM_MAP(CAmbientControl)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(IAmbientControl)
        COM_INTERFACE_ENTRY(IOleClientSite)
    END_COM_MAP()


	STDMETHOD(Invoke)(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr)
	{
		if(dispIdMember == DISPID_AMBIENT_DLCONTROL)
		{
			//ダウンロードコントロール設定
			V_VT(pVarResult) = VT_I4;
			V_I4(pVarResult) = _nAmbient;
			return	S_OK;
		}

		//if(dispIdMember == DISPID_AMBIENT_USERAGENT)
		//{
		//	//pvar->vt = VT_BSTR;
		//	//pvar->bstrVal = ::SysAllocString(T2OLE(strUserAgent));
		//}

		return	E_NOTIMPL;
	}

	STDMETHOD(put_nAmbient)(long newVal)
	{
		_nAmbient = newVal;
		return	S_OK;
	}

	STDMETHOD(get_nAmbient)(long* pnVal)
	{
		if(pnVal == NULL)
			return	E_POINTER;
		*pnVal = _nAmbient;
		return	S_OK;
	}


	STDMETHOD(SaveObject)(void)
	{
		return	E_NOTIMPL;
	}

	STDMETHOD(GetMoniker)(DWORD dwAssign,DWORD dwWhichMoniker,IMoniker **ppmk)
	{
		return	E_NOTIMPL;
	}

	STDMETHOD(GetContainer)(IOleContainer **ppContainer)
	{
		return	E_NOTIMPL;
	}

	STDMETHOD(ShowObject)(void)
	{
		return	E_NOTIMPL;
	}

	STDMETHOD(OnShowWindow)(BOOL fShow)
	{
		return	E_NOTIMPL;
	}

	STDMETHOD(RequestNewObjectLayout)(void)
	{
		return	E_NOTIMPL;
	}
};

tabbrowser215.gif
ビューウインドウ内にAmbientDownloadのフラグを取得/設定するための関数を追加する。

本来であればIWebBrowser2から直接現在のダウンロードフラグを取得するべきだが、今回は独自インターフェースIAmbientControlを実装することで、前回設定したフラグ値を取得して利用するようにした。
	CComPtr<IAmbientControl>	_pIAmbientControl;		//■追加


	//■追加
	//ダウンロードコントロール設定
	//nFlagsは DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_BGSOUNDS などを渡す
	bool	SetAmbientDownloadControl(long nFlags)
	{
		if(_pIAmbientControl == NULL)
			return	false;

		//設定
		_pIAmbientControl->put_nAmbient(nFlags);

		//変更の適用
		HRESULT		hr;
		CComPtr<IOleControl>	pIOleControl;

		hr = _pIWebBrowser2->QueryInterface(&pIOleControl);
		if(pIOleControl)
			hr = pIOleControl->OnAmbientPropertyChange(DISPID_AMBIENT_DLCONTROL);

		return	SUCCEEDED(hr) ? true : false;
	}


	//■追加
	//ダウンロードコントロール取得
	//nFlagsは DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_BGSOUNDS などが返る
	bool	GetAmbientDownloadControl(long& nFlags)
	{
		nFlags = 0;
		if(_pIAmbientControl == NULL)
			return	false;

		return	SUCCEEDED(_pIAmbientControl->get_nAmbient(&nFlags)) ? true : false;
	}

	//■追加
	//指定したダウンロードコントロールフラグが現在有効化どうか
	//nFlagはDLCTL_DLIMAGESやDLCTL_NO_SCRIPTSなどを1つだけ渡す
	bool	IsEnableAmbientDownloadControl(long nFlag)
	{
		long	nAmbient;

		GetAmbientDownloadControl(nAmbient);

		return	(nAmbient & nFlag) ? true : false;
	}

	//■追加
	//指定したダウンロードコントロールフラグの設定/解除
	//nFlagはDLCTL_DLIMAGESやDLCTL_NO_SCRIPTSなどを1つだけ渡す
	//bEnable=trueならそのフラグを設定、falseなら解除する
	bool	SetAmbientDownloadControl(long nFlag,bool bEnable)
	{
		bool	ret;
		long	nAmbient;

		ret = GetAmbientDownloadControl(nAmbient);
		if(ret == false)
			return	false;

		if(bEnable)
			nAmbient |= nFlag;
		else
			nAmbient &= ~nFlag;

		return	SetAmbientDownloadControl(nAmbient);
	}

tabbrowser216.gif 最後にビューウインドウが生成されたときに、クライアントサイトとして登録されるようにする。
	//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;

		hWnd = __super::Create(hWndParent,rect,szWindowName,dwStyle,dwExStyle,MenuOrID,lpCreateParam);
		if(hWnd == NULL)
			return	NULL;			//Create失敗

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

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


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

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

			//IEコントロールへのセット
			_pIWebBrowser2->QueryInterface(&pIOleObject);
			if(pIOleObject && pCAmbientControl)
				pIOleObject->SetClientSite(pCAmbientControl);

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

		//Googleツールバーを生成する
		{
			HRESULT	hr;
			GUID	rclsid;
			WCHAR	pszGUID[] = {L"{2318C2B1-4965-11d4-9B18-009027A5CD4F}"};		//GoogleツールバーのGUID

			::CLSIDFromString(pszGUID,&rclsid);

			_pIDeskBand = NULL;
			hr = ::CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER,IID_IDeskBand,(void**)&_pIDeskBand);
			if(_pIDeskBand)
			{
				CComPtr<IObjectWithSite>	pIObjectWithSite;

				_pIDeskBand->QueryInterface(IID_IObjectWithSite,(void**)&pIObjectWithSite);

				DESKBANDINFO	sDeskBandInfo;

				//別に必要ないが情報をたくさん取得
				::ZeroMemory(&sDeskBandInfo,sizeof(DESKBANDINFO));
				sDeskBandInfo.dwMask = DBIM_MINSIZE | DBIM_MAXSIZE | DBIM_INTEGRAL | DBIM_ACTUAL | DBIM_TITLE | DBIM_MODEFLAGS | DBIM_BKCOLOR;
				hr = _pIDeskBand->GetBandInfo(0,DBIF_VIEWMODE_NORMAL,&sDeskBandInfo);

				//■■本当はきちんと調整しないとだめ!!
				CRect	rect(0,0,2000,sDeskBandInfo.ptMinSize.y);		//横サイズは十分大きいサイズを確保(//■■2000px固定)

				//デスクバンドをホスト用クラスに割り当てる
				{
					CComPtr<IIEToolbar>	pIIEToolbar;

					pIIEToolbar = new CComObject<CIEToolbar>;

					pIIEToolbar->put_hwnd(_hWndRebarIE);
					if(_pIWebBrowser2)
						pIIEToolbar->put_IWebBrowser2(_pIWebBrowser2);
					if(pIObjectWithSite)
						hr = pIObjectWithSite->SetSite(pIIEToolbar);

					if(SUCCEEDED(hr))
						hr = _pIDeskBand->ShowDW(TRUE);
				}

				//位置調整
				{
					HWND	hWnd;

					_pIDeskBand->GetWindow(&hWnd);
					if(::IsWindow(hWnd))
						::MoveWindow(hWnd,0,0,rect.Width(),rect.Height(),TRUE);
				}
			}
		}

		return	hWnd;
	}

tabbrowser218.gif
これでビルド/実行し「画像表示」をOFFにして「更新」したところ、画像が表示されなくなった。


Googleツールバー6が公開されていたのでインストールしたところ、このタブブラウザ上でGoogleツールバーの下部が少し欠けて表示されるようになってしまった。次回はこれを機会にGoogleツールバー表示部分を少し見直す。

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

2009年04月11日

第51回 タブがないときにGoogleツールバーが表示されないようにする (タブブラウザーを作る)

tabbrowser219.gif
これまで起動直後などタブが1つもないときには「Googleツールバー」を表示するための領域(リバー)が空白のまま表示されていた。今回はタブがない場合にはリバーが表示されないようにする。

まずGoogleツールバーの親ウインドウとして利用しているCDummyWndの処理内容を追加する。リバーの表示/非表示を切り替える関数やリバーサイズが変わったときにGoogleツールバーのウインドウサイズを変更する処理を追加した。
	//■処理内容実装
	class	CDummyWnd	: public CWindowImpl<CDummyWnd>
	{
	public:
		UINT	_nBandIndex;		//rebar上のインデックス

		CDummyWnd()
		{
			_nBandIndex = -1;
		}

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

		LRESULT OnDnpRbIndex(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			UINT*	pnIndex = (UINT*)wParam;

			if(pnIndex)
				*pnIndex = _nBandIndex;

			return	0;
		}


		LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			bHandled = FALSE;
			if(_nBandIndex == -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(_nBandIndex == -1 || IsWindow() == FALSE)
				return	false;

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

			return	true;
		}
	};

tabbrowser220.gif そしてOnCreate内でGoogleツールバー用のリバー生成後、そのIDをCDummyWndに渡しておく。
	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列で表示

		//ダミーウインドウを生成し、リバーに割り当てる。これはIEツールバーに利用
		//ここで生成してしまうと何もないリバーができてしまうが気にしない
		_wndDummy.Create(m_hWnd,CRect(0,0,500,24),0,WS_CHILD);
		AddSimpleReBarBand(_wndDummy,0,TRUE);						//1行表示

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

			nCount = (UINT)::SendMessage(m_hWndToolBar,RB_GETBANDCOUNT,NULL,NULL);
			_wndDummy._nBandIndex = nCount - 1;
			_wndDummy.ShowRebar(false);			//非表示にする
		}


		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;
	}

tabbrowser221.gif OnTabPageActivatedの部分でツールバーの表示/非表示が切り替わるようにする。
	//タブが選択されたときの処理
	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;

		pView = GetActivePageView();		//アクティブビュー取得
		if(pView == NULL)
		{
			_wndDummy.ShowRebar(false);			//■追加 リバー消去
			return	0;
		}

		_wndDummy.ShowRebar(true);			//■追加 リバー表示

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

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

		return	0;
	}

tabbrowser222.gif 今回はCDummyWndに独自メッセージを送ることでリバーのIDが取得できるようにした。そのためのメッセージを「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)		//■追加

tabbrowser223.gif
これでタブがないときには「Googleツールバー」用のリバーが表示されなくなった。

次回はGoogleツールバーの下端が切れる問題とページ上でキー入力ができなくなるバグを修正する。

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

第52回 Googleツールバーの表示サイズを自動調整する (タブブラウザーを作る)

tabbrowser224.gif
前回はタブがあるときだけ「Googleツールバー」用のリバーが表示されるようにした。今回はリバーの大きさが「Googleツールバー」に合わせて変わるようにする。

「Googleツールバー」の生成はCMainFrame::OnCreate内で行っていた。それを関数に切り出して呼び出すようにした。生成処理内では前回実装したリバーのIDを取得する処理を使ってDESKBANDINFOの情報をリバーにセットする。
	//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;

		hWnd = __super::Create(hWndParent,rect,szWindowName,dwStyle,dwExStyle,MenuOrID,lpCreateParam);
		if(hWnd == NULL)
			return	NULL;			//Create失敗

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

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


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

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

			//IEコントロールへのセット
			_pIWebBrowser2->QueryInterface(&pIOleObject);
			if(pIOleObject && pCAmbientControl)
				pIOleObject->SetClientSite(pCAmbientControl);

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

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

			::CLSIDFromString(pszGUID,&rclsid);

			CreateIEToolbar(_hWndRebarIE,&_pIDeskBand,rclsid);
		}

		return	hWnd;
	}




	//■追加
	//hWndParentはCDummyWnd(Googleツールバーの親となるウインドウハンドル)
	//clsidIDeskBandはGoogleツールバーのCLSIDを指定する
	bool	CreateIEToolbar(HWND hWndParent,IDeskBand** ppIDeskBand,const IID& clsidIDeskBand)
	{
		HRESULT	hr;

		if(ppIDeskBand == NULL)
			return	false;
		if(*ppIDeskBand)
		{
			ATLASSERT(0);
			(*ppIDeskBand)->Release();
			*ppIDeskBand = NULL;
		}

		hr = ::CoCreateInstance(clsidIDeskBand,NULL,CLSCTX_INPROC_SERVER,IID_IDeskBand,(void**)ppIDeskBand);
		if(FAILED(hr) || *ppIDeskBand == NULL)
			return	false;

		CComPtr<IObjectWithSite>	pIObjectWithSite;

		(*ppIDeskBand)->QueryInterface(IID_IObjectWithSite,(void**)&pIObjectWithSite);

		//デスクバンドをホスト用クラスに割り当てる
		{
			CComPtr<IIEToolbar>	pIIEToolbar;

			pIIEToolbar = new CComObject<CIEToolbar>;

			pIIEToolbar->put_hwnd(hWndParent);
			if(_pIWebBrowser2)
				pIIEToolbar->put_IWebBrowser2(_pIWebBrowser2);
			if(pIObjectWithSite)
				hr = pIObjectWithSite->SetSite(pIIEToolbar);

			if(SUCCEEDED(hr))
				hr = (*ppIDeskBand)->ShowDW(TRUE);		//表示
		}

		DESKBANDINFO	sDeskBandInfo;

		//別に必要ないが情報をたくさん取得
		::ZeroMemory(&sDeskBandInfo,sizeof(DESKBANDINFO));
		sDeskBandInfo.dwMask = DBIM_MINSIZE | DBIM_MAXSIZE | DBIM_INTEGRAL | DBIM_ACTUAL | DBIM_TITLE | DBIM_MODEFLAGS | DBIM_BKCOLOR;
		hr = (*ppIDeskBand)->GetBandInfo(0,DBIF_VIEWMODE_NORMAL,&sDeskBandInfo);

		CRect	rect(0,0,1500,sDeskBandInfo.ptMinSize.y);		//サイズの一時設定(後で自動調整)

		//rebar情報の設定
		{
			HWND	hWndRebar;
			UINT	nIndex;

			hWndRebar = ::GetParent(hWndParent);
			nIndex = -1;
			::SendMessage(hWndParent,WM_DNP_GETRBINDEX,(WPARAM)&nIndex,NULL);
			if(nIndex != -1 && hWndRebar)
			{
				REBARBANDINFO	info;

				//MinSizeのみをリバーへ設定。本来ならもっと情報を設定すべき
				::ZeroMemory(&info,sizeof(REBARBANDINFO));
				info.cbSize	= sizeof(REBARBANDINFO);
				info.fMask	= RBBIM_CHILDSIZE;
				::SendMessage(hWndRebar,RB_GETBANDINFO,nIndex,(LPARAM)&info);
				info.cyMinChild	= sDeskBandInfo.ptMinSize.y;
				info.cyChild	= sDeskBandInfo.ptMinSize.y;
				info.cxMinChild	= sDeskBandInfo.ptMinSize.x;
				::SendMessage(hWndRebar,RB_SETBANDINFO,nIndex,(LPARAM)&info);

				//位置調整
				::SendMessage(hWndRebar,RB_GETRECT,nIndex,(LPARAM)&rect);
				::SendMessage(hWndParent,WM_SIZE,SIZE_RESTORED,MAKELPARAM(rect.Width(),rect.Height()));
			}
		}

		return	true;
	}

tabbrowser225.gif 次に「Googleツールバー」で(トップレベルHTMLとして作られている?)ドロップダウンメニューを開くとビューウインドウにきちんとフォーカスが当たらない問題を修正する。CIEUtilityのSetFocusChange内を変更する。今まではUIDeactivateを呼ぶのみだったが、InPlaceDeactivateも呼ぶようにした。
	//■処理内容追加
	//IEコントロールがフォーカスを受け取ったときbGetFocus=true、
	//IEコントロールがフォーカスを失ったときbGetFocus=falseとして呼び出す
	bool	SetFocusChange(bool bGetFocus)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;

		HRESULT	hr = E_FAIL;
		CComPtr<IOleInPlaceObject> pIOleInPlaceObject;

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

		if(bGetFocus)
		{
			hr = pIOleInPlaceObject->UIDeactivate();
			pIOleInPlaceObject->InPlaceDeactivate();
		}
		else
		{
			HWND hWnd;

			hr = pIOleInPlaceObject->GetWindow(&hWnd);
			if(SUCCEEDED(hr) && ::IsWindow(hWnd))
				::ShowWindow(hWnd,SW_NORMAL);
		}

		return	true;
	}

tabbrowser226.gif 同様にCIEToolbar内も変更しておく。
	//■処理内容追加
	//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;
	}

tabbrowser227.gif
これでビルド/実行すると、これまで下端が切れて表示されていた「Googleツールバー」がきちんと表示されるようになった。

次回複数のInternet Explorer用ツールバーが表示できるようにするための準備をする。

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

2009年04月12日

第53回 IE用ツールバーを複数利用するための準備をする (タブブラウザーを作る)

tabbrowser228.gif
これまでInternet Explorer用のツールバーとして「Googleツールバー」のみを固定表示していた。これをこれから数回のステップでInternet Explorerのようにインストールされているツールバーを自由に選択/利用できるできるようにする。

まずはビューウインドウ側を少し修正する。単純にメンバー変数として定義していたHWNDとIDeskBand*をCIEToolbarInfoの中に追い出す。
class CTabBrowser100View : public CWindowImpl<CTabBrowser100View, CAxWindow>
	,public IDispEventImpl<SINKID_EVENTS, CTabBrowser100View, &DIID_DWebBrowserEvents2>
	,public CIEUtility
{
	CTabView*	_pTabView;		//タブビューを保持
//	HWND		_hWndRebarIE;	//■削除 IEツールバー用のリバー

	bool		_bPageError;	//現在のページでスクリプトエラーなどがあればtrue、通常はfalse


	CAtlString	_strLastStatusText;					//最後に送られてきたステータステキストを保管
	CComPtr<IDocHostUIHandler>	_pHideScriptError;
	CIELikeStatusbar			_wndStatusbar;		//ステータスバー
//	CComPtr<IDeskBand>			_pIDeskBand;		//■削除 Googleツールバー

	//■追加
	class	CIEToolbarInfo
	{
	public:
		IDeskBand*	_pIDeskBand;
		HWND		_hWndRebarIE;	//IEツールバー用のリバー


		CIEToolbarInfo()
		{
			_hWndRebarIE	= NULL;
			_pIDeskBand		= NULL;
		}

		CIEToolbarInfo(HWND hWndRebar)
		{
			_hWndRebarIE = hWndRebar;
			_pIDeskBand		= NULL;
		}

		//■CMainFrame内から移動&変更
		//Googleツールバーを生成する
		bool	Create(IWebBrowser2* pIWebBrowser2,bool bVisible)
		{
			GUID	rclsid;
			WCHAR	pszGUID[] = {L"{2318C2B1-4965-11d4-9B18-009027A5CD4F}"};		//GoogleツールバーのGUID

			::CLSIDFromString(pszGUID,&rclsid);

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

	protected:

		//■CMainFrame内から移動&変更
		//hWndParentはCDummyWnd(Googleツールバーの親となるウインドウハンドル)
		//clsidIDeskBandはGoogleツールバーのCLSIDを指定する
		bool	CreateIEToolbar(IWebBrowser2* pIWebBrowser2,HWND hWndParent,IDeskBand** ppIDeskBand,const IID& clsidIDeskBand,bool bVisible)
		{
			HRESULT	hr;

			if(ppIDeskBand == NULL || pIWebBrowser2 == NULL)
				return	false;
			if(*ppIDeskBand)
			{
				ATLASSERT(0);
				(*ppIDeskBand)->Release();
				*ppIDeskBand = NULL;
			}

			hr = ::CoCreateInstance(clsidIDeskBand,NULL,CLSCTX_INPROC_SERVER,IID_IDeskBand,(void**)ppIDeskBand);
			if(FAILED(hr) || *ppIDeskBand == NULL)
				return	false;

			CComPtr<IObjectWithSite>	pIObjectWithSite;

			(*ppIDeskBand)->QueryInterface(IID_IObjectWithSite,(void**)&pIObjectWithSite);

			//デスクバンドをホスト用クラスに割り当てる
			{
				CComPtr<IIEToolbar>	pIIEToolbar;

				pIIEToolbar = new CComObject<CIEToolbar>;

				pIIEToolbar->put_hwnd(hWndParent);
				if(pIWebBrowser2)
					pIIEToolbar->put_IWebBrowser2(pIWebBrowser2);
				if(pIObjectWithSite)
					hr = pIObjectWithSite->SetSite(pIIEToolbar);

				if(SUCCEEDED(hr) && bVisible)
					hr = (*ppIDeskBand)->ShowDW(TRUE);		//表示
			}

			DESKBANDINFO	sDeskBandInfo;

			//別に必要ないが情報をたくさん取得
			::ZeroMemory(&sDeskBandInfo,sizeof(DESKBANDINFO));
			sDeskBandInfo.dwMask = DBIM_MINSIZE | DBIM_MAXSIZE | DBIM_INTEGRAL | DBIM_ACTUAL | DBIM_TITLE | DBIM_MODEFLAGS | DBIM_BKCOLOR;
			hr = (*ppIDeskBand)->GetBandInfo(0,DBIF_VIEWMODE_NORMAL,&sDeskBandInfo);

			CRect	rect(0,0,1500,sDeskBandInfo.ptMinSize.y);		//サイズの一時設定(後で自動調整)

			//rebar情報の設定
			{
				HWND	hWndRebar;
				UINT	nIndex;

				hWndRebar = ::GetParent(hWndParent);
				nIndex = -1;
				::SendMessage(hWndParent,WM_DNP_GETRBINDEX,(WPARAM)&nIndex,NULL);
				if(nIndex != -1 && hWndRebar)
				{
					REBARBANDINFO	info;

					//MinSizeのみをリバーへ設定。本来ならもっと情報を設定すべき
					::ZeroMemory(&info,sizeof(REBARBANDINFO));
					info.cbSize	= sizeof(REBARBANDINFO);
					info.fMask	= RBBIM_CHILDSIZE;
					::SendMessage(hWndRebar,RB_GETBANDINFO,nIndex,(LPARAM)&info);
					info.cyMinChild	= sDeskBandInfo.ptMinSize.y;
					info.cyChild	= sDeskBandInfo.ptMinSize.y;
					info.cxMinChild	= sDeskBandInfo.ptMinSize.x;
					::SendMessage(hWndRebar,RB_SETBANDINFO,nIndex,(LPARAM)&info);

					//位置調整
					::SendMessage(hWndRebar,RB_GETRECT,nIndex,(LPARAM)&rect);
					::SendMessage(hWndParent,WM_SIZE,SIZE_RESTORED,MAKELPARAM(rect.Width(),rect.Height()));
				}
			}

			return	true;
		}
	};

tabbrowser229.gif 今回はツールバー情報をCAtlArrayとして実装することにした。
	CAtlArray<CIEToolbarInfo>	_aToolbarInfo;		//■追加


public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())


	CTabBrowser100View(CTabView* pTabView)		//■変更
	{
		_pTabView = pTabView;
//		_hWndRebarIE = hWndRebarIE;				//■削除
		_bPageError = false;
	}

tabbrowser230.gif
CAtlArrayにより単純にCIEToolbarInfoを配列化しているのでCIEToolbarInfoではCComPtrを使わずにIDeskBand*を利用した。そのためビューウインドウが破棄されるときにReleaseしておく。

さらに配列化したのでOnTabPageChangeでもそれらが呼び出されるように変更する。
	//ビューを閉じるときの処理
	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;

		if(_pIWebBrowser2)
			Unadvise(_pIWebBrowser2);	//IEからの切断
		_pIWebBrowser2 = NULL;

		//■追加 IEツールバーの削除
		{
			size_t	i;
			size_t	nSize;

			nSize = _aToolbarInfo.GetCount();
			for(i = 0; i < nSize; i++)
			{
				if(_aToolbarInfo[i]._pIDeskBand == NULL)
					continue;

				_aToolbarInfo[i]._pIDeskBand->ShowDW(FALSE);	//非表示
				_aToolbarInfo[i]._pIDeskBand->CloseDW(0);		//終了
				_aToolbarInfo[i]._pIDeskBand->Release();		//リリース
				_aToolbarInfo[i]._pIDeskBand = NULL;
			}
		}

		return	0;
	}



public:



	//■変更
	//メインウインドウから呼ばれる
	//タブの選択変更があったときメインウインドウから呼ばれる
	bool	OnTabPageChange(bool bActivated)
	{
		size_t	i;
		size_t	nSize;

		nSize = _aToolbarInfo.GetCount();
		for(i = 0; i < nSize; i++)
		{
			if(_aToolbarInfo[i]._pIDeskBand == NULL)
				continue;

			if(bActivated)
				_aToolbarInfo[i]._pIDeskBand->ShowDW(TRUE);		//IEツールバーを表示
			else
				_aToolbarInfo[i]._pIDeskBand->ShowDW(FALSE);	//IEツールバーを消す
		}

		//GoogleツールバーはShowDWで?UIハンドラーを変更するのでここで上書き。
		//ShowWindowで表示/非表示を切り替えれば解決するのかな?要調査
		if(bActivated)
			ChangeUIHandler();			//UIHandlerを変更する

		return	true;
	}

tabbrowser231.gif さらにビューウインドウのCreate内でのGoogleツールバー生成処理を削除し、外部からIE用ツールバーを作成するための関数を追加する。
	//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;

		hWnd = __super::Create(hWndParent,rect,szWindowName,dwStyle,dwExStyle,MenuOrID,lpCreateParam);
		if(hWnd == NULL)
			return	NULL;			//Create失敗

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

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


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

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

			//IEコントロールへのセット
			_pIWebBrowser2->QueryInterface(&pIOleObject);
			if(pIOleObject && pCAmbientControl)
				pIOleObject->SetClientSite(pCAmbientControl);

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


		//■以下削除

		return	hWnd;
	}


	//■追加
	bool	AddIEToolbar(HWND hWndIERebar)
	{
		size_t	nIndex;

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

		return	true;
	}

tabbrowser232.gif 最後にCMainFrame側でビューを生成するときの処理を変えれば終わりだ。
	//タブの新規作成
	//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;		//タブへの追加失敗
		}

		pView->AddIEToolbar(_wndDummy);		//■追加 Googleツールバー生成

		return	pView;
	}

tabbrowser233.gif
これで実行したときの見た目は変わらないが、少しずつ対応できてきた。次回も引き続き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で現在利用可能なツールバーを列挙して使えるようにする。

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

第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用ツールバーを選択して表示できるようにする。

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

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月17日

第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をタブとして開くようにする。

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

第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がビューで開くようになった。また「お気に入り」の表示順序をドラッグアンドドロップにより変更できるようになった。

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

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

前の10件 1  2  3  4  5  6  7





usefullcode@gmail.com

About 2009年04月

2009年04月にブログ「UsefullCode.net」に投稿されたすべてのエントリーです。

前の記事は2009年03月です。

次の記事は2010年05月です。

他にも多くのエントリーがあります。メインページ記事一覧も見てください。