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

1  2  3  4  5  6  7

2009年04月 記事一覧

2009年04月01日

IE8環境でVisual Studioがきちんと動くようにする

Internet Explorer 8をインストールすると、Visual Studio 2008が「Internet Explorer 8をインストールするとVisual Studioが正常に動かない!」にあるようにウイザードが動かなくなったりとかなり不便です。

その後、調べてみたら「Visual C++ Team Blog」の「Some VS2005 and VS2008 Wizards Pop Up Script Error.」にそのものずばりの解決方法がありました。

解決方法を参考に以下のようなregファイルを作成して、レジストリに書き込めばエラーなくウイザードが動作するようになりました(レジストリ書き込み後にVisual Studioのの再起動が必要)。
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1000]
@=""
"1207"=dword:00000000
regファイルをダウンロード

これでIE8環境でもVisual Studio 2008が完全に動作する。。。と言いたいところですが、私の環境ではHTMLファイルを開いたとき「デザイン」画面への切り替えができません。Visual StudioでHTMLファイルを編集することはないからいいのですが。。。

第1回 VC++でタブブラウザー用プロジェクトを作る (タブブラウザーを作る)

tabbrowser01.gif
最新のWTL 8.0では標準でタブ切り替えのMDIがサポートされている。これを使うとこれまでは考えられなかったぐらい簡単にタブ切り替え型のアプリケーションを作れる。

今回から何回かにわたって例としてシンプルなタブブラウザーを作ってみる。

まずはVisual Studioで「ファイル」メニューにある「新規作成」から「プロジェクト」を選択して新しいプロジェクトを作成する。

tabbrowser02.gif
そしてプロジェクトの種類「WTL」から「ATL/WTLアプリケーションウイザード」を選択する。ここではプロジェクト名は「TabBrowser100」とした。

tabbrowser03.gif
ATL/WTLアプリケーションウイザードが開いたら左側の「アプリケーションの種類」タブで「タブ型ビューアプリケーション」を選択する。
これによりウイザードにより自動生成されるひな形のプロジェクトがタブ切り替え型のMDIとなる。

tabbrowser04.gif
さらに「ユーザーインターフェース機能」タブではビューウインドウの形式から「HTMLページ」を選択する。

tabbrowser05.gif
以上によりアプリケーションウイザードにより自動生成されたプロジェクトをビルド/実行すると画像のようになる。

すでにタブで切り替えできるブラウザーとなっているため、このままで普通にインターネット上のホームページを見ることができる。

とは言うもののアドレスバーがないのでURLを入力してアクセスすることはできないし、タブには「Document」と表示されてホームページ名も表示されない、リンクを新しいタブで開くこともできない。次回はこのプロジェクトに手動でURLを指定するためのアドレスバーを追加する。


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

第2回 アドレスバーを作る(その1) (タブブラウザーを作る)

tabbrowser06.gif
前回はタブブラウザーのひな形となるプロジェクトをATL/WTLプロジェクトウイザードにより自動生成した。今回はURLを手入力するための「アドレスバー」をウインドウに追加する。

前回のプロジェクトを開き、ソリューションウインドウにある「MainFrm.h」をダブルクリックして編集画面を開く。
そして、「MainFrm.h」にincludeファイル用の定義とアドレスバーとなるコンボボックスのメンバー変数を追加する。

#define WINDOW_MENU_POSITION	3

#include "atlmisc.h"					//■追加
#include "atlstr.h"						//■追加

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

public:
	DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)

tabbrowser07.gif 次に同じ「MainFrm.h」内にアドレスバー内で[Enter]キーが押されたことを検出して処理するためにメッセージマップにハンドラを追加し、対応した処理用の関数を追加する。 ここではコンボボックス編集終了の通知を示すWM_NOTIFYのCBEN_ENDEDITをとらえ、コンボボックスに入力されている文字をメッセージボックスに出力する処理を実装した(入力されたURLをブラウザで実際に開く処理は次回実装する)。
	BEGIN_MSG_MAP(CMainFrame)
		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_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()


	//■関数追加
	//アドレスバーで[ENTER]キーが押されたときなどに呼ばれる
	LRESULT OnAddressbarEnter(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		bHandled = FALSE;

		NMCBEENDEDIT*	pNmCbEndEdit = (NMCBEENDEDIT*)pnmh;
		CAtlString		strURL;

		if(pNmCbEndEdit == NULL || pNmCbEndEdit->iWhy != CBENF_RETURN)
			return	0;			//[ENTER]キーが押されたとき以外はreturn

		_wndAddressBar.GetWindowText(strURL);
		if(strURL == _T(""))
			return	0;		//URLが入力されてなかった

		MessageBox(strURL);	//とりあえず入力されたURLをメッセージボックスで表示する

		return	0;
	}

tabbrowser08.gif 最後に同じく「MainFrm.h」のOnCreate()内にアドレスバーとなるコンボボッククスを生成してツールバーに配置するコードを追加する。ここではコンボボックスのスタイルをドロップダウン式のCBS_DROPDOWNを指定した。
		_wndAddressBar.Create(m_hWnd,CRect(0,0,200,200),0,WS_CHILD | WS_VISIBLE | CBS_DROPDOWN);	//■追加
		AddSimpleReBarBand(_wndAddressBar);											//■追加

tabbrowser09.gif
以上の追加をしてビルド/実行するとツールバーにアドレスバーとなるコンボボックスが配置された。そして文字を入力して[Enter]キーを押すとメッセージボックスとして出力できた。

次回はこのアドレスバーに入力された文字列(URL)をビューで開く実装をする。

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

第3回 アドレスバーを作る(その2) (タブブラウザーを作る)

tabbrowser10.gif
前回はウインドウのツールバー領域にアドレスバー用のコンボボックスを配置した。今回はそのアドレスバーに入力されたURLを実際にビュー内で開く。

まずはビュー内でURLを簡単に開けるようにユーティリティ的な関数を実装する。前回までに作成したプロジェクトで、ソリューションウインドウにある「TabBrowser100View.h」をダブルクリックしてビュークラスの編集画面を開く。そしてここにURLを開くための関数などを追加する。

ここではQueryControlによりCAxWindowとして生成されたIEコントロールのIWebBrowser2を取得して_pIWebBrowser2に保持しておき、Navigate()ではIWebBrowser2::NavigateによりURLを開く。 これによりNavigate()を呼ぶことで指定されたURLをビュー内で開けるようになる。
	CComPtr<IWebBrowser2>		_pIWebBrowser2;		//■追加 このビューのブラウザーコントロールを保持 GetBrowserCtrl() によりこの変数に代入される

	//■関数追加
	//URLを開く
	bool	Navigate(LPCTSTR pszURL)
	{
		bool	ret;
		HRESULT	hr;

		ret = GetBrowserCtrl();
		if(ret == false)
			return	false;

		hr = _pIWebBrowser2->Navigate(CComBSTR(pszURL),&CComVariant(),&CComVariant(),&CComVariant(),&CComVariant());

		return	SUCCEEDED(hr) ? true : false;
	}

	//■関数追加
	//_pIWebBrowser2にこのビューに関連づいているIEをセットする
	bool	GetBrowserCtrl(void)
	{
		if(_pIWebBrowser2)
			return	true;

		QueryControl(&_pIWebBrowser2);

		return	_pIWebBrowser2 ? true : false;
	}

tabbrowser11.gif 次にソリューションウインドウの「MainFrm.h」をダブルクリックしてメインウインドウの編集画面を開く。 これは今回の作業で一番の肝となる項目だ。WTLのタブウインドウの実装方法はCTabViewImpl::AddPageにHWNDを渡す形となるため、一旦タブを作ってからビュークラスにアクセスするのが非常に煩雑になる。そのためここではタブの独自データ領域にビューへのポインタを格納しておくことで、簡単にビュークラスにアクセスできるようにした。
	LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		CTabBrowser100View* pView = new CTabBrowser100View;
		//TODO: 必要に応じてURLを書き換えてください。
		pView->Create(m_view, rcDefault, _T("http://www.usefullcode.net/"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
		m_view.AddPage(pView->m_hWnd, _T("Document"),-1,pView);		//■「,-1,pView」を追加

		// TODO: ドキュメント初期化コードの追加

		return 0;
	}

tabbrowser12.gif 最後にアドレスバー内で[Enter]キーを押されたときに入力されたURLを開くように実装する。前回は入力されたURLをメッセージボックスに表示した。その部分を先ほど作成したビューのNavigate()関数を呼ぶように変更する。
	//アドレスバーで[ENTER]キーが押されたときなどに呼ばれる
	LRESULT OnAddressbarEnter(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		bHandled = FALSE;

		NMCBEENDEDIT*	pNmCbEndEdit = (NMCBEENDEDIT*)pnmh;
		CAtlString		strURL;

		if(pNmCbEndEdit == NULL || pNmCbEndEdit->iWhy != CBENF_RETURN)
			return	0;			//[ENTER]キーが押されたとき以外はreturn

		_wndAddressBar.GetWindowText(strURL);
		if(strURL == _T(""))
			return	0;		//URLが入力されてなかった

		//■以下の処理を編集/追加
		int		nPage;
		CTabBrowser100View*	pView;

		nPage = m_view.GetActivePage();	//アクティブなタブのインデックス取得
		if(nPage < 0)
			return	0;		//アクティブなタブがない(=タブが1つもない->本来は自動で新しいタブを作るべき)

		pView = (CTabBrowser100View*)m_view.GetPageData(nPage);	//アクティブなタブの独自データを取得
		if(pView == NULL)
			return	0;		//予測外のエラー(ここに入ることはない)

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

		return	0;
	}

tabbrowser13.gif
以上によりビルド/実行してアドレスバーにGoogleのURLを入れて[Enter]キーを押すと、Googleのホームページが開いた。
このままでは入力されたアドレスの履歴機能、現在開いているホームページのURL表示機能、「移動」ボタンなどなど実際のブラウザーのアドレスバーとしては機能が不十分だ。しかも致命的なことに[BackSpace]キーなどが利用できない。しかし今のところはこのままにしておく。

次回は「Document」となっているタブ表示を、開いているホームページ名が表示されるようにする。

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

第4回 「タブ」にホームページの名前を表示する (タブブラウザーを作る)

tabbrowser14.gif
前回までにタブブラウザー用のプロジェクトを作成し、手動でURLを入力できるようにアドレスバーを実装した。今回はアクセスしたURLのホームページ名が「タブ」に表示されるようにする。

前回までに作成したプロジェクトを開く。そしてソリューションウインドウにある「TabBrowser100View.h」をダブルクリックしてビュークラスの編集画面を開く。そしてまず4箇所ソースコードを追加する。
#include "atlstr.h"				//■追加
#include "Exdispid.h"			//■追加

#define SINKID_EVENTS 0			//■追加

class CTabBrowser100View : public CWindowImpl<CTabBrowser100View, CAxWindow>
	,public IDispEventImpl<SINKID_EVENTS, CTabBrowser100View, &DIID_DWebBrowserEvents2>		//■追加
{
	CTabView*	_pTabView;		//■追加 タブビューを保持
public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())

	//■追加
	CTabBrowser100View(CTabView* pTabView)
	{
		_pTabView = pTabView;
	}


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

		// このメッセージを翻訳する機会をHTML ページに与える
		return (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);
	}

	BEGIN_MSG_MAP(CTabBrowser100View)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)		//■追加
	END_MSG_MAP()

tabbrowser15.gif さらに開いたURLが読み込まれたときに呼ばれる処理を追加する。 ここではDISPID_DOCUMENTCOMPLETEを受信したときにタブにホームページの名前を表示するようにした。通常のタブブラウザーの場合はほかのイベントが届いたときにも書き換える処理をしている。しかし処理が煩雑になるため今回は省略した。
	//■以下3行追加
	BEGIN_SINK_MAP(CTabBrowser100View)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
	END_SINK_MAP()

	//■関数追加
	//HTMLページが読み終わったときに呼ばれる処理
	void	_stdcall OnDocumentComplete(IDispatch* pDisp, VARIANT* URL)
	{
		bool	ret;

		ret = GetBrowserCtrl();
		if(ret == false)
			return;

		//タブに表示されている名前を変更
		if(_pTabView)
		{
			int			nPage;
			CComBSTR	bstr;
			CAtlString	strName;

			_pIWebBrowser2->get_LocationName(&bstr);		//現在のホームページ名を取得
			strName = bstr;

			nPage = _pTabView->PageIndexFromHwnd(m_hWnd);	//このウインドウに関連づいているタブのインデックス取得
			_pTabView->SetPageTitle(nPage,strName);			//タブの名前を変更
		}
	}

tabbrowser16.gif さらにIEコントロールからのイベントを受信できるように接続処理と、終了時のためにその切断処理を実装する。 本来であればIWebBrowser2を取得したとき、、、この場合はGetBrowserCtrl()内でIEへの接続を行った方がいいが、今回はCreateの中のままにしておく。
	//■追加
	//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)
	{
		bool	ret;
		HWND	hWnd;

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

		ret = GetBrowserCtrl();
		if(ret == false)
			return	hWnd;			//WebBrowser取得失敗

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

		return	hWnd;
	}


	//■追加
	//ビューを閉じるときの処理
	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;

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

		return	0;
	}

tabbrowser17.gif 次に「MainFrm.h」を開き、OnFileNew()の中にあるビューの生成時にCTabViewへのポインタを渡すようにする。
	LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		CTabBrowser100View* pView = new CTabBrowser100View(&m_view);	//■変更 ビューのポインタを渡しておく
		//TODO: 必要に応じてURLを書き換えてください。
		pView->Create(m_view, rcDefault, _T("http://www.usefullcode.net/"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
		m_view.AddPage(pView->m_hWnd, _T("Document"),-1,pView);

		// TODO: ドキュメント初期化コードの追加

		return 0;
	}

tabbrowser18.gif
以上によりビルド/実行すると、開いたホームページの名前がタブに表示されるようになった。

次回は右クリックして現われるメニューから「新しいウインドウで開く」を選択した場合に、Internet Explorerが起動するのではなく、新しいタブでURLが開くようにする。

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

第5回 新しいタブでリンクを開く (タブブラウザーを作る)

tabbrowser19.gif
前回までにタブブラウザー用のプロジェクトを作成し、アドレスバーの作成やタブにホームページ名が表示されるようにした。

今回はリンクを右クリックして現われるメニューから「新しいウインドウで開く」を選択したときに、そのリンクを新しいタブとして開くようにする。

前回までに作成したプロジェクトを開く。そしてソリューションウインドウにある「TabBrowser100View.h」をダブルクリックしてビュークラスの編集画面を開く。そして2箇所追加する。

IEコントロールでは新しいウインドウで開く直前にDISPID_NEWWINDOW2というイベントが発生する。それを検出したら、新しいタブを作りそこにあるIEコントロールのIDispatchを返せばInternet Explorerではなく「タブ」でリンクが開くようになる。
	BEGIN_SINK_MAP(CTabBrowser100View)
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW2, OnNewWindow2)		//■追加
		SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
	END_SINK_MAP()


	//■追加
	//新しいウインドウで開く前に呼ばれる処理
	//新しいタブで開く
	void	__stdcall OnNewWindow2(IDispatch **ppDisp,VARIANT_BOOL *Cancel)
	{
		//新しいタブを作る
		CTabBrowser100View* pView = new CTabBrowser100View(_pTabView);	//タブビューのポインタを渡しておく
		pView->Create(*_pTabView, rcDefault, _T("about:blank"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
		_pTabView->AddPage(pView->m_hWnd, _T("Document"),-1,pView);
		if(pView->_pIWebBrowser2 == NULL)
			return;

		//新しいタブで開くようにIEコントロールにポインタを渡す
		pView->_pIWebBrowser2->put_RegisterAsBrowser(VARIANT_TRUE);	//これしなくても開くようになっているのはなぜ?IE8から?一応実行しておく
		pView->_pIWebBrowser2->get_Application(ppDisp);
	}

tabbrowser20.gif
これでビルド/実行し、リンクを右クリックして「新しいウインドウで開く」を選択すると新しいタブで開いた。

次回は「戻る」と「進む」ボタンを実装する。

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

2009年04月02日

第6回 タブブラウザーに「戻る」や「進む」ボタンを配置する (タブブラウザーを作る)

tabbrowser21.gif
前回までにアドレスバーの実装やリンクを新しいタブで開く機能の実装などにより、タブブラウザーとしてそれらしく動作するようになってきた。今回は「戻る」「進む」「更新」「中止」ボタンをツールバーに作る。

前回までに作成したプロジェクトを開く。まずはツールバーにボタンを追加するため、リソースビューの「Toolbar」にある「IDR_MAINFRAME」をダブルクリックしてツールバーリソースを開いて、ボタンを4つ追加する。

tabbrowser22.gif
さらに「プロパティ」ウインドウを開き、新しく作成したボタンのIDを設定する。ここでは
「戻る」ボタンは「ID_IE_BACK」
「進む」ボタンは「ID_IE_NEXT」
「中止」ボタンは「ID_IE_STOP」
「更新」ボタンは「ID_IE_REFRESH」
とした。これでツールバーリソースに関する設定は終わりだ。

tabbrowser23.gif 次にソースコードを編集する。ソリューションウインドウで「TabBrowser100View.h」をダブルクリックしてビュークラスの編集画面を開く。そしてここにIWebBrowser2::GoBack()を呼びだして「戻る」ための関数などを追加する。
	//■追加
	void	Back(void)
	{
		if(_pIWebBrowser2)
			_pIWebBrowser2->GoBack();
	}

	//■追加
	void	Next(void)
	{
		if(_pIWebBrowser2)
			_pIWebBrowser2->GoForward();
	}

	//■追加
	void	Stop(void)
	{
		if(_pIWebBrowser2)
			_pIWebBrowser2->Stop();
	}

	//■追加
	void	Refresh(void)
	{
		if(_pIWebBrowser2)
			_pIWebBrowser2->Refresh2(&CComVariant(REFRESH_COMPLETELY));
	}

tabbrowser24.gif 次に「MainFrm.h」を開き、ツールバーのボタンが押されたときに実行されるメッセージハンドラを追加する。通常のMDIであればFrame -> Viewの順にWM_COMMANDが届くためメッセージハンドらを直接ビューに書くが、CTabViewになっているため直接ビューには届かないため今回はMainFrm.h内に実装した。
	BEGIN_MSG_MAP(CMainFrame)
		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_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)		//■追加
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()


	//■追加
	//「戻る」「進む」などのIEコマンド処理
	LRESULT OnIECommand(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
	{
		int		nPage;
		CTabBrowser100View*	pView;

		nPage = m_view.GetActivePage();
		if(nPage < 0)
			return	0;		//アクティブなタブがない

		pView = (CTabBrowser100View*)m_view.GetPageData(nPage);
		if(pView == NULL)
			return	0;		//ビューが取得できなかった

		switch(wID)
		{
		case	ID_IE_BACK:	//戻る
			pView->Back();
			break;

		case	ID_IE_NEXT:	//進む
			pView->Next();
			break;

		case	ID_IE_STOP:	//ストップ
			pView->Stop();
			break;

		case	ID_IE_REFRESH:	//更新
			pView->Refresh();
			break;
		}

		return	0;
	}

tabbrowser25.gif
これでビルド/実行すると、ツールバーに「戻る」や「進む」などのボタンが現れ、実際に「戻る」などの操作ができた。

今回作成した「戻る」「進む」ボタンは常に押せる状態にある。次回はIEコントロールから履歴を取り出して、「戻る」ことができるときだけ「戻る」ボタンを有効になるようにする。

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

第7回 IEコントロールの履歴を調べて「戻る」の状態を切り替える (タブブラウザーを作る)

tabbrowser26.gif
前回までにWTLのタブビューをひな形にアドレスバーや「戻る」「進む」ボタンなどを実装することでだんだんタブブラウザが形になってきた。

今回はIEコントロールから履歴情報を取り出して、「戻る」「進む」ボタンの有効/無効を自動的に切り換える処理を追加する。

前回までに作成したプロジェクトのソリューションウインドウにある「TabBrowser100View.h」をダブルクリックしてビュークラスの編集画面を開く。

IEコントロールからの履歴取得はITravelLogStgを利用する。そのためのinclude定義を追加する。
#include "tlogstg.h"		//■追加 IE5.5以上

tabbrowser27.gif 次に同じビュークラスの中にIEコントロールの履歴があるかどうかを返す関数を追加する。 ここれでは履歴が1つ以上あればbBackやbNextをtrueにすることで履歴情報を返している。
	//■追加
	//「戻る」や「進む」ための履歴がIEにあるかどうかのチェック
	bool	IsButtonEnable(bool& bBack,bool& bNext)
	{
		HRESULT	hr;
		DWORD	dwCount;

		CComPtr<ITravelLogStg>			pITravelLogStg;
		CComPtr<IServiceProvider>		pIServiceProvider;

		bBack = false;
		bNext = false;

		if(_pIWebBrowser2)
			_pIWebBrowser2->QueryInterface(&pIServiceProvider);
		if(pIServiceProvider)
			pIServiceProvider->QueryService(SID_STravelLogCursor,IID_ITravelLogStg,(void**)&pITravelLogStg);
		if(pITravelLogStg == NULL)
			return	false;

		hr = pITravelLogStg->GetCount(TLEF_RELATIVE_BACK,&dwCount);
		if(dwCount)
			bBack = true;
		if(FAILED(hr))
			return	false;

		hr = pITravelLogStg->GetCount(TLEF_RELATIVE_FORE,&dwCount);
		if(FAILED(hr))
			return	false;
		if(dwCount)
			bNext = true;

		return	true;
	}

tabbrowser28.gif
次に「MainFrm.h」を開き、ツールバーのボタン状態の有効/無効を、ビュークラスに実装したIsButtonEnableの結果をみて切りかえる処理を実装する。

本来であればIEコントロールがURLを読み込んだことを検出してボタン情報を切り替えた方が効率がいい。しかしここでは実装を簡単にするため、アイドル処理のためのOnIdle()でボタンの有効/無効を切り替えることにした。
	virtual BOOL OnIdle()
	{
		//■追加
		//「戻る」「進む」ボタンの状態設定
		{
			bool	ret;
			int		nPage;
			CTabBrowser100View*	pView = NULL;

			ret = false;
			nPage = m_view.GetActivePage();
			if(nPage >= 0)
				pView = (CTabBrowser100View*)m_view.GetPageData(nPage);
			if(pView)
			{
				bool	bBack;
				bool	bNext;

				ret = pView->IsButtonEnable(bBack,bNext);
				if(ret)
				{
					UIEnable(ID_IE_BACK,bBack ? TRUE : FALSE);
					UIEnable(ID_IE_NEXT,bNext ? TRUE : FALSE);
				}
			}
			if(ret == false)
			{
				UIEnable(ID_IE_BACK,FALSE);
				UIEnable(ID_IE_NEXT,FALSE);
			}
		}

		UIUpdateToolBar();
		return FALSE;
	}

	BEGIN_UPDATE_UI_MAP(CMainFrame)
		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)			//■追加
	END_UPDATE_UI_MAP()

tabbrowser29.gif
これでビルド/実行すると、「戻る」と「進む」ボタンの有効/無効が自動的に切り替わるようになった。

次回は今まで保留していたアドレスバーで[BackSpace]キーなどが使えない点を修正する。

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

第8回 アドレスバーで[BackSpace]キーが使えない問題を修正する (タブブラウザーを作る)

tabbrowser30.gif
前回までにWTLのタブビューをひな形にアドレスバーや「戻る」「進む」ボタンなどを実装してきた。タブブラウザーとしてそれなりに動くようになってきたが、アドレスバーに致命的な不具合があった。[BackSpace]や[←]キーなどが使えない点だ。今回はその点を修正する。


前回までに作成したプロジェクトのソリューションウインドウにある「MainFrm.h」をダブルクリックして編集画面を開く。

今まではアドレスバーはCComboBoxExをそのまま利用していたが、新しくアドレスバー用のクラスを追加する。このクラスでは中にあるコントロールをサブクラス化することにより、マウスによるフォーカス取得(WM_MOUSEACTIVATE)を受け取って、メインフレームへWM_DNP_CHANGEFOCUSへ送るだけの処理をしている。

アドレスバーへのフォーカス取得はWM_COMMANDのEN_SETFOCUSやEN_KILLFOCUSでも行える。しかし今回はEN_SETFOCUSによるタイミングで処理するとキーカーソル(キャレット)が消えてしまうためWM_MOUSEACTIVATEとした。 本当はEN_SETFOCUSLタイミングで処理した方がいいのだが、、、何時間かテストをしたが結局解決方法が見つからず今回は諦めた。
#define	WM_DNP_CHANGEFOCUS	(WM_APP + 1)		//■追加

//■追加
//アドレスバー用クラス
class	CAddressBarCtrl	: public CWindowImpl<CAddressBarCtrl,CComboBoxEx>
{
	class	CInnerComboBox	: public CWindowImpl<CInnerComboBox>
	{
	public:
		BEGIN_MSG_MAP(CInnerComboBox)
			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;
		}
	};

	CInnerComboBox	_wndInnerComboBox;

public:

	BEGIN_MSG_MAP(CAddressBarCtrl)
	END_MSG_MAP()


	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;

		_wndInnerComboBox.SubclassWindow(GetComboCtrl());	//サブクラス化

		return	hWnd;
	}
};

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

tabbrowser31.gif 次にアドレスバーから送られてきたWM_DNP_CHANGEFOCUSメッセージを処理して、IEコントロールからフォーカスを外す処理を実装する。ここではIOleInPlaceObject::UIDeactivate()を利用した。 ちなみにIDeskBand(IEツールバー)で同様の[BackSpace]キーが使えない問題を解消したいときは、IInputObjectSite::OnFocusChangeISを呼んでIInputObject::TranslateAcceleratorIOで処理する流れが一般的だ。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)		//■追加
		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_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)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()


	//■追加
	//フォーカス変更時処理
	LRESULT OnDnpChangeFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		BOOL	bHasFocus = (BOOL)wParam;		//アドレスバーなどがフォーカスを受け取っていたらTRUE

		int		nPage;
		CTabBrowser100View*	pView;

		nPage = m_view.GetActivePage();
		if(nPage < 0)
			return	0;		//アクティブなタブがない
		pView = (CTabBrowser100View*)m_view.GetPageData(nPage);
		if(pView == NULL)
			return	0;		//ビューが取得できなかった
		if(pView->_pIWebBrowser2 == NULL)
			return	0;

		if(bHasFocus)
		{
			//アドレスバーなどIE以外がフォーカスを受け取った

			CComPtr<IOleInPlaceObject> pIOleInPlaceObject;

			pView->_pIWebBrowser2->QueryInterface(&pIOleInPlaceObject);
			if(pIOleInPlaceObject)
				pIOleInPlaceObject->UIDeactivate();		//IEのUIを無効化
		}

		return	0;
	}

tabbrowser32.gif
これでビルド/実行するとアドレスバー内で[BackSpace]キーが普通に使えるようになった。

計画性なくタブブラウザーを作っているのと、画像をキャプチャーするためにソースコードを撮りやすく配置しているためなどなどの要因でだんだんソースコードがごちゃごちゃしてきた。ちょっと整理したい気もするのだがとりあえず保留して、次回はタブの右クリックメニューを実装する。

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

第9回 「タブ」の右クリックメニューを実装する (タブブラウザーを作る)

tabbrowser33.gif
今回はタブブラウザーの「タブ」部分で右クリックをしたときのメニュー表示機能を実装する。

まずはメニューリソースを作る。前回までに作成した作成したプロジェクトで「リソース」ウインドウを開き、Visual Studioの「プロジェクト」メニューから「リソースの追加」を選択する。

tabbrowser34.gif
「リソースの追加」ダイアログが開いたら「Menu」を選択して「新規作成」ボタンを押す。

tabbrowser35.gif
そして右クリック用のメニューリソースを作る。「タブの右クリック」というダミー項目を作り、その下に実際に右クリックメニューとして利用する「タブを閉じる」と「最新の状態に更新」の2つのメニューを作る。

tabbrowser36.gif
デフォルトのままだとメニューリソースのIDが「IDC_MENU1」のようになっているので、これを「プロパティ」ウインドウで「IDM_TAB」に変更する。

tabbrowser37.gif
さらに「タブを閉じる」は「ID_WINDOW_CLOSE」に、「最新の状態に更新」は「ID_IE_REFRESH」にそれぞれ変更する。

これでメニューリソースの準備が整った。

tabbrowser38.gif 次にソリューションウインドウで「MainFrm.h」をダブルクリックして開き、タブで右クリックされたときに届く「TBVN_CONTEXTMENU」用のメッセージハンドラと処理を追加する。 右クリックメニューを実際に表示するTrackPopupMenuを呼ぶときは、フラグでTPM_RETURNCMDを指定して関数内で選択されたメニューの処理している。これを指定せずにこのウインドウのメッセージハンドラに処理させると、アクティブでないタブの上の右クリックで「タブを閉じる」を選択したときであっても、アクティブなタブが閉じてしまうなどの不具合が生じるためだ。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		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_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)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)		//■追加
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()


	//■追加
	//タブ右クリックメニューの表示
	LRESULT OnTabContextMenu(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		if(pnmh == NULL)
			return	0;

		int		nPage = pnmh->idFrom;		//メニューが表示されたタブ
		UINT	nCmd;
		BOOL	ret;
		CMenu	cMenu;
		CMenu	cSubMenu;
		POINT	pt;
		CTabBrowser100View*	pView;

		pView = (CTabBrowser100View*)m_view.GetPageData(nPage);
		if(pView == NULL)
			return	0;		//ビューが取得できなかった

		ret = cMenu.LoadMenu(IDM_TAB);		//メニューリソースからメニューを読み込む
		if(ret == FALSE)
			return	0;						//メニュー取得失敗

		::GetCursorPos(&pt);				//マウスカーソル位置取得

		nCmd = 0;
		cSubMenu = cMenu.GetSubMenu(0);
		if(cSubMenu.m_hMenu)
			nCmd = (UINT)cSubMenu.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN,pt.x,pt.y,m_hWnd);	//メニュー表示
		if(nCmd == 0)
			return	0;		//メニューが選択されなかった

		//選択されたメニューコマンドの処理
		switch(nCmd)
		{
		case	ID_WINDOW_CLOSE:	//タブを閉じる
			m_view.RemovePage(nPage);
			break;

		case	ID_IE_REFRESH:		//更新
			pView->Refresh();
			break;

		default:
			ATLASSERT(0);		//サポートされていないコマンド
			break;
		}

		return	0;
	}

tabbrowser39.gif
これでタブ上で右クリックするとメニューが表示され、「タブを閉じる」などの処理ができるようになった。

次回はInternet Exploreのお気に入りをこのタブブラウザから使えるようにする。

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

1  2  3  4  5  6  7





usefullcode@gmail.com

About 2009年04月

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

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

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

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