前の10件 11  12  13  14  15  16  17  18  19  20  21

記事一覧

2009年04月06日

第34回 ステータスバーのダブルクリックでスクリプトエラーの詳細情報を表示する (タブブラウザーを作る)

tabbrowser137.gif
前回は開いているページでスクリプトエラーが発生したときにステータスバーにアイコンが表示されるようにした。今回はステータスバーをダブルクリックしたときにエラーに関する情報を表示する。

IEコントロールでは私の知る限りエラーメッセージを後から表示するための機能は備わっていない。Internet Explorer 8などではエラーメッセージを内部で保管しておき、必要に応じてエラー情報をダイアログで表示していると思われる。

そのため今回は内部でエラー情報を保持してステータスバーがダブルクリックされた時にそれを表示することにする。まずスクリプトエラー検出用のインターフェースにエラーメッセージを表示するためのメソッドを追加する。
//スクリプトエラーの通知先ウインドウなどを設定するためのインターフェース
MIDL_INTERFACE("2C66181A-27D5-486e-A84A-3B8EDAE1951F")
IScriptErrorSetting : public IUnknown
{
public:
	STDMETHOD (put_hWndNotify)(HWND hWnd) = 0;		//エラーの通知先HWND(ビューのHWND)を指定
	STDMETHOD (ShowLastScriptError)(HWND hWnd) = 0;		//■追加
};

	CAtlString		_strErrorMessage;		//■追加 表示用のエラー情報

	//■追加
	STDMETHOD (ShowLastScriptError)(HWND hWnd)
	{
		if(_strErrorMessage == _T(""))
			return	E_FAIL;

		::MessageBox(hWnd,_strErrorMessage,_T("エラー情報"),MB_OK | MB_ICONWARNING);

		return	S_OK;
	}

tabbrowser138.gif そしてExecでスクリプトエラーを検出したら、その詳細情報を保存しておく。
    STDMETHODIMP Exec( const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut)
    {
		//コマンドがスクリプトエラーを示すものなら、エラー表示を無効にする
		if(nCmdID == OLECMDID_SHOWSCRIPTERROR)
		{
			(*pvaOut).vt		= VT_BOOL;
			(*pvaOut).boolVal	= VARIANT_TRUE;		//エラーを無視してスクリプト続行

			if(_hWndNotify && ::IsWindow(_hWndNotify))
				::SendMessage(_hWndNotify,WM_DNP_SCRIPTERROR,(WPARAM)TRUE,NULL);


			//■追加
			//エラー情報を保存
			//面倒なので表示用に整形して_strErrorMessageに代入している
			//本当はURIをチェックして同じならどんどん貯めておいた方がいい
			{
				_strErrorMessage = _T("");

				CComPtr<IHTMLDocument2>	pIHTMLDocument2;
				CComPtr<IHTMLWindow2>	pIHTMLWindow2;
				CComPtr<IHTMLEventObj>	pIHTMLEventObj;

				if(pvaIn && pvaIn->punkVal)
					pvaIn->punkVal->QueryInterface(IID_IHTMLDocument2,(void**)&pIHTMLDocument2);
				if(pIHTMLDocument2)
					pIHTMLDocument2->get_parentWindow(&pIHTMLWindow2);
				if(pIHTMLWindow2)
					pIHTMLWindow2->get_event(&pIHTMLEventObj);
				if(pIHTMLEventObj)
				{
					int			i;
					OLECHAR*	pwszName[] = {L"errorLine",L"errorCharacter",L"errorCode",L"errorMessage",L"errorUrl"};
					TCHAR*		pszHeader[] = {_T("ライン:"),_T("文字:"),_T("エラーコード:"),_T(""),_T("URI:")};

					for(i = 0; i < 5; i++)
					{
						DISPID		nDispID;
						HRESULT		hr;
						DISPPARAMS	sDispParams;
						CComVariant	vResult;

						//エラー情報のDISPID取得
						hr = pIHTMLEventObj->GetIDsOfNames(IID_NULL,&pwszName[i],1,LOCALE_SYSTEM_DEFAULT,&nDispID);
						if(FAILED(hr))
							continue;

						//エラー情報取得
						::ZeroMemory(&sDispParams,sizeof(DISPPARAMS));
						hr = pIHTMLEventObj->Invoke(nDispID,IID_NULL,LOCALE_SYSTEM_DEFAULT,DISPATCH_PROPERTYGET,&sDispParams,&vResult,NULL,NULL);
						if(FAILED(hr))
							continue;

						CAtlString	strBuff(vResult);

						_strErrorMessage += pszHeader[i];
						_strErrorMessage += strBuff;
						_strErrorMessage += _T("\n");
					}
				}
			}

			return S_OK;
		}

		return E_NOTIMPL;
    }

tabbrowser139.gif ビュークラスの中に上で最初に実装したスクリプトエラーを表示するための関数を用意する。
	//■追加
	bool	ShowScriptError(void)
	{
		if(_bPageError == false)
			return	false;			//現在エラー中ではない

		CComPtr<IScriptErrorSetting>	pIScriptErrorSetting;	//独自に作ったインターフェース

		if(_pHideScriptError)
			_pHideScriptError->QueryInterface(&pIScriptErrorSetting);
		if(pIScriptErrorSetting)
			pIScriptErrorSetting->ShowLastScriptError(m_hWnd);		//エラーメッセージの表示

		return	true;
	}

tabbrowser140.gif
最後にメインウインドウにステータスバーがダブルクリックされたときの処理を追加する。

ステータスバーがダブルクリックされるとメインウインドウにWM_NOTIFYが届くので、今回はそれを処理することにした。
		COMMAND_ID_HANDLER(ID_IE_ORGANIZE_FAVORITE, OnIEOrganizeFavorite)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
		NOTIFY_HANDLER(ATL_IDW_STATUS_BAR, NM_DBLCLK, OnStatusBarDblClk)			//■追加
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		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 OnStatusBarDblClk(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		bHandled = FALSE;

		NMMOUSE*	pNmMouse = (NMMOUSE*)pnmh;
		CTabBrowser100View*	pView;

		if(pNmMouse == NULL)
			return	0;

		if(pNmMouse->dwItemSpec == 0)
		{
			//ゼロ番目のペインがダブルクリックされた
			pView = GetActivePageView();
			if(pView)
				pView->ShowScriptError();			//スクリプトエラー情報を表示する
		}

		return	0;
	}

tabbrowser141.gif
これでビルド/実行するとスクリプトエラー発生時にステータスバーをダブルクリックすることでエラーの詳細表示を見れるようになった。

次回はメニューバーにInternet Explorerにあるような項目を追加する。

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

第33回 ページエラー発生時にアイコンを表示する (タブブラウザーを作る)

tabbrowser132.gif
前回はステータスバーにアイコンを表示するテストを行った。今回はページにエラーが発生しているときのみアイコンが表示されるようにする。

まずビュークラスにエラーが発生したときにメインウインドウへそれを知らせる処理を追加する。 ここでは独自メッセージWM_DNP_SCRIPTERRORのWPARAMをエラー発生時TRUE、エラー解消時FALSEという意味付けを行い、メインウインドウへ送信するようにした。
	//■追加
	//エラー発生/解消時に呼ぶ
	//bError=trueならエラー発生
	void	SetErrorState(bool bError)
	{
		_bPageError = bError;			//エラーが発生したことを保持
		if(bError)
			ChangeStatusText(NULL);

		if(_pTabView == NULL || _pTabView->GetActivePage() != _pTabView->PageIndexFromHwnd(m_hWnd))
			return;			//このビューがアクティブでなければこれ以降処理しない

		//メインウインドウにエラー発生を通知
		GetTopLevelWindow().SendMessage(WM_DNP_SCRIPTERROR,(WPARAM)(bError ? TRUE : FALSE));
	}

	//■追加
	bool	IsPageError(void)
	{
		return	_bPageError;
	}



	LRESULT OnDnpScriptError(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		//■処理変更
		SetErrorState(true);
		return	0;
	}


	//最後にIEコントロールから受け取ったステータスバー用メッセージを返す
	void	GetLastStatusText(CAtlString& strText)
	{
		strText = _strLastStatusText;

		//表示する文字がないときで、現在ページにエラーがあればエラーメッセージを返す
		if(_bPageError && strText == _T(""))
			strText = _T("ページでエラーが発生しました。");
	}

	void __stdcall OnDownloadComplete()
	{
	}

	void __stdcall OnCommandStateChange(long lCommand,VARIANT_BOOL vbEnable)
	{
	}

	void __stdcall OnDownloadBegin(void)
	{
		SetErrorState(false);		//■変更 エラー情報をリセット
	}

tabbrowser131.gif WM_DNP_SCRIPTERRORのWPARAMに意味付けを追加してしまったので、直接は関係ないがスクリプト検出用のクラスの処理でもWPARAMがTRUEになるように変更しておく。
    STDMETHODIMP Exec( const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut)
    {
		//コマンドがスクリプトエラーを示すものなら、エラー表示を無効にする
		if(nCmdID == OLECMDID_SHOWSCRIPTERROR)
		{
			(*pvaOut).vt		= VT_BOOL;
			(*pvaOut).boolVal	= VARIANT_TRUE;

			if(_hWndNotify && ::IsWindow(_hWndNotify))
				::SendMessage(_hWndNotify,WM_DNP_SCRIPTERROR,(WPARAM)TRUE,NULL);		//■変更

			return S_OK;
		}

		return E_NOTIMPL;
    }

tabbrowser133.gif 忘れずにNavigateErrorイベント発生時の処理にもメインウインドウへ通知がいくように変更しておく。
	void __stdcall OnNavigateError(IDispatch *pDisp,VARIANT *vURL,VARIANT *vTargetFrameName,VARIANT *vStatusCode,VARIANT_BOOL *vbCancel)
	{
		SetErrorState(true);		//■変更 スクリプトエラー出なくてもエラーが発生したらtrueにする
	}

tabbrowser134.gif そしてメインウインドウ側でスクリプトエラーが処理されるようにメッセージハンドラを追加する。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		MESSAGE_HANDLER(WM_DNP_CHANGESTATUSTEXT, OnDnpChangeStatusText)
		MESSAGE_HANDLER(WM_DNP_CHANGEPROGRESS, OnDnpChangeProgress)
		MESSAGE_HANDLER(WM_DNP_SCRIPTERROR, OnDnpScriptError)		//■追加
		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_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)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		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()

tabbrowser135.gif 最後にタブが切り替わったときと、ビューウインドウからスクリプトエラーのメッセージが送られてきたときにステータスバーにアイコンが表示されるようにする。
	//タブが選択されたときの処理
	LRESULT OnTabPageActivated(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		if(pnmh == NULL)
			return	0;

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

			int kk=m_view.GetActivePage();

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

		bool		ret;
		CTabBrowser100View*	pView;

		pView = GetActivePageView();		//アクティブビュー取得
		if(pView == NULL)
			return	0;

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

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

		//ステータスバーのテキスト変更
		{
			CAtlString	strText;

			pView->GetLastStatusText(strText);
			if(::IsWindow(m_hWndStatusBar))
				::SetWindowText(m_hWndStatusBar,strText);
		}

		//■追加
		//ステータスバーのエラーアイコン変更
		{
			CStatusBarCtrl	wndStatusBar = m_hWndStatusBar;

			if(wndStatusBar.IsWindow())
			{
				if(pView->IsPageError())
					wndStatusBar.SetIcon(0,_cIconList.GetIcon(CIconManager::ICON_WARNING_SM));
				else
					wndStatusBar.SetIcon(0,NULL);
			}
		}

		//プログレス変更
		{
			long	nProgress;
			long	nProgressMax;

			pView->GetLastProgress(nProgress,nProgressMax);
			RefreshProgressBar(nProgress,nProgressMax);
		}

		return	0;
	}


	//■追加
	//ステータスバーのエラーアイコン変更
	LRESULT OnDnpScriptError(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		BOOL	bPageError = (BOOL)wParam;
		CStatusBarCtrl	wndStatusBar = m_hWndStatusBar;

		if(wndStatusBar.IsWindow())
		{
			if(bPageError)
				wndStatusBar.SetIcon(0,_cIconList.GetIcon(CIconManager::ICON_WARNING_SM));
			else
				wndStatusBar.SetIcon(0,NULL);
		}

		return	0;
	}

tabbrowser136.gif
これでビルド/実行するとページにエラーがあるときだけステータスバーにアイコンが表示されるようになった。

だんだんビューからメインウインドウへの通知が増えてきてそろそろソースコードが崩壊しそうな雰囲気があるため整理したいところだがまだしばらく誤魔化しながら実装し続けることにする。

次回はページにエラーがあるときにステータスバーをダブルクリックすることでエラーの詳細情報が表示されるようにする。

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

第32回 ステータスバーにアイコンを表示する (タブブラウザーを作る)

tabbrowser127.gif
開いているページでスクリプトエラーが発生したときにステータスバーにアイコンを表示する機能を実装する前段階として、今回はステータスバーにアイコンを表示するテストを行う。

Windowsにはエラー表示用のアイコンがOEMリソースとして用意されている。その利用を「stdafx.h」内の最初で定義する。
#define	OEMRESOURCE			//■追加 OEMアイコンを利用する

tabbrowser128.gif 次にOEMリソースからスモールアイコンを読み込み、管理するためのクラスCIconManagerを作る。今回は1つのアイコンのみを管理しているが、このクラスは今後順次機能を追加する予定だ。
//■追加
//アイコンを管理
//アイコンをAddする関数などは未実装。今後追加
class	CIconManager
{
protected:
	CAtlArray<HICON>	_ahIcon;		//アイコンリスト

public:
	CIconManager()
	{
		SetFixedIndexIcon();		//固定インデックスのアイコンを追加
	}

	~CIconManager()
	{
		Destroy();
	}

	enum	ICON_INDEX
	{
		ICON_WARNING_SM = 0,			//警告アイコン。スクリプトエラー時に利用
	};

	HICON	GetIcon(UINT nIconIndex)
	{
		if(nIconIndex >= _ahIcon.GetCount())
			return	NULL;

		return	_ahIcon[nIconIndex];
	}

protected:

	//固定インデックスのアイコンを追加
	//かならずICON_INDEXと同じ値になるようにする
	bool	SetFixedIndexIcon(void)
	{
		ATLASSERT(_ahIcon.GetCount() == 0);		//すでに設定されてる?
		if(_ahIcon.GetCount())
			return	false;

		size_t	nIndex;
		HICON	hIcon;

		//警告アイコン
		//LoadImageで32x32を読み込み、CopyImageで16x16に縮小。LoadImageのHICONはLR_SHAREDなのでDestroyIcon必要なし
		hIcon = (HICON)::LoadImage(NULL,MAKEINTRESOURCE(OIC_WARNING),IMAGE_ICON,0,0,LR_DEFAULTSIZE | LR_SHARED);
		hIcon = (HICON)::CopyImage(hIcon,IMAGE_ICON,::GetSystemMetrics(SM_CXSMICON),::GetSystemMetrics(SM_CYSMICON),LR_COPYFROMRESOURCE);
		nIndex = _ahIcon.Add(hIcon);
		ATLASSERT(nIndex == ICON_WARNING_SM);

		return	true;
	}

	//保持してるアイコンの破棄
	void	Destroy(void)
	{
		size_t	i;
		size_t	nSize;

		nSize = _ahIcon.GetCount();
		for(i = 0; i < nSize; i++)
		{
			if(_ahIcon[i] == NULL)
				continue;
			::DestroyIcon(_ahIcon[i]);
		}
		_ahIcon.RemoveAll();
	}
};


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

	CIconManager	_cIconList;		//■追加

tabbrowser129.gif そしてOnCreate内にアイコンをステータスバーに表示するためのテストコードを1行追加する。この1行のコードはアイコンが表示されることを確認したら削除する。
		//■追加アイコン表示。テスト用。確認したら削除すること
		::SendMessage(m_hWndStatusBar,SB_SETICON,0,(LPARAM)_cIconList.GetIcon(CIconManager::ICON_WARNING_SM));

tabbrowser130.gif
これでビルド/実行するとステータスバーにエラー表示用のアイコンが表示された。

次回はページにエラーが発生したときにこのアイコンが表示されるようにする。

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

第31回 アドレスバーでキーボードショートカットが使えるようにする (タブブラウザーを作る)

tabbrowser124.gif
今までアドレスバー内で、[Ctrl]+[V]などのキーボードによる操作ができなかったり、横幅以上のサイズのURLが入力できないなどの致命的な問題点があった。今回はそれらを修正する。

まずはアドレスバーでキーボードショートカットが利用できない点。これはアクセラレーターが設定されているためで、その処理はCFrameWindowImpl<CMainFrame>::PreTranslateMessageで行われている(むしろそこではその処理しかしていない)。そのためアドレスバー内にフォーカスがある場合はCFrameWindowImpl<CMainFrame>::PreTranslateMessageが実行されないようにする。
	virtual BOOL PreTranslateMessage(MSG* pMsg)
	{
		if(_wndAddressBar.IsChild(::GetFocus()) == FALSE)		//■追加 アドレスバーにフォーカスがあるときはPreTranslateMessageを回さない(アドレスバーでCtrl+Cなどを使えるようにするため)
		{
			if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
				return TRUE;
		}

		return m_view.PreTranslateMessage(pMsg);
	}

tabbrowser125.gif
次に長い文字列を入力できない点。これは使っているコンボボックスにCBS_AUTOHSCROLLスタイルを設定していなかったためだ。ちなみにエディットコントロールの場合はES_AUTOHSCROLLスタイルを指定する。

また今回は外見に3D境界線をつける拡張スタイルのWS_EX_CLIENTEDGEなども追加しておいた。
		//■スタイル変更
		_wndAddressBar.Create(m_hWnd,CRect(0,0,200,200),0,WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_CLIPCHILDREN,WS_EX_CLIENTEDGE);

tabbrowser126.gif
これでビルド/実行するとアドレスバーでの上記2つの問題を修正できた。

次回はステータスバーにアイコンを表示する。

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

第30回 ステータスバーにエラーメッセージが表示され続けるようにする (タブブラウザーを作る)

tabbrowser120.gif
前回はスクリプトエラーが発生したとき、ステータスバーにエラーメッセージが表示されるようにした。しかしエラーが起きたことを保存していなかったためすぐにエラーメッセージが消えてしまうという問題があった。

今回はエラーが起きたことを記憶して、常にエラーメッセージが表示されるようにする。

ビュークラスにエラーが起きたことを保存するための変数_bPageError を追加する。
class CTabBrowser100View : public CWindowImpl<CTabBrowser100View, CAxWindow>
	,public IDispEventImpl<SINKID_EVENTS, CTabBrowser100View, &DIID_DWebBrowserEvents2>
	,public CIEUtility
{
	CTabView*	_pTabView;		//タブビューを保持
	HWND		_hWndRebarIE;	//IEツールバー用のリバー

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

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


	CTabBrowser100View(CTabView* pTabView,HWND hWndRebarIE)
	{
		_pTabView = pTabView;
		_nLastProgress		= 0;
		_nLastProgressMax	= -1;
		_hWndRebarIE = hWndRebarIE;
		_bPageError = false;			//■追加
	}

tabbrowser121.gif
そしてエラーが起きたときに_bPageError をtrueにして、エラーが起きたことを保存し、エラー用のステータスバー用メッセージが表示されるようにする。

_bPageError がtrueのままだと、一度エラーがおきたらずっとエラーメッセージが表示されたままになってしまう。そのためDownloadBeginイベントを受け取ったらfalseを設定してエラー情報をクリアする。
	//ステータスバーのメッセージ変更をメインウインドウへ通知
	void	ChangeStatusText(LPCTSTR pszText)
	{
		_strLastStatusText = pszText;		//いつでも参照できるようにクラスのメンバーに保存しておく

		if(_pTabView == NULL || _pTabView->GetActivePage() != _pTabView->PageIndexFromHwnd(m_hWnd))
			return;			//このビューがアクティブでなければこれ以降処理しない

		GetLastStatusText(_strLastStatusText);		//■追加 この呼び出しによりエラー時にはエラー用メッセージに変わる

		//メインウインドウに変更を通知する
		GetTopLevelWindow().SendMessage(WM_DNP_CHANGESTATUSTEXT,(WPARAM)(LPCTSTR)_strLastStatusText);
	}

	void __stdcall OnStatusTextChange(BSTR bstrText)
	{
		ChangeStatusText((CAtlString)bstrText);
	}

	LRESULT OnDnpScriptError(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		_bPageError = true;			//■追加 エラーが発生したことを保持
		ChangeStatusText(NULL);		//■変更
		return	0;
	}

	//最後にIEコントロールから受け取ったステータスバー用メッセージを返す
	void	GetLastStatusText(CAtlString& strText)
	{
		strText = _strLastStatusText;

		//■追加 表示する文字がないときで、現在ページにエラーがあればエラーメッセージを返す
		if(_bPageError && strText == _T(""))
			strText = _T("ページでエラーが発生しました。");
	}

	void __stdcall OnDownloadComplete()
	{
	}

	void __stdcall OnCommandStateChange(long lCommand,VARIANT_BOOL vbEnable)
	{
	}

	void __stdcall OnDownloadBegin(void)
	{
		_bPageError = false;		//■追加 エラー情報をリセット
	}

tabbrowser122.gif せっかくなのでスクリプトエラーだけでなく、NavigateErrorを受信したときもエラーメッセージを表示するようにした。
	void __stdcall OnNavigateError(IDispatch *pDisp,VARIANT *vURL,VARIANT *vTargetFrameName,VARIANT *vStatusCode,VARIANT_BOOL *vbCancel)
	{
		//■2行追加
		_bPageError = true;			//スクリプトエラー出なくてもエラーが発生したらtrueにする
		ChangeStatusText(NULL);
	}

tabbrowser123.gif
これでビルド/実行するとステータスバーにエラーメッセージが表示され続けるようになった。

もう少しスクリプトエラー関連を修正しておきたい気もするが保留して、次回はアドレスバーにクリップボードから張り付けたURLが途切れる問題などを修正する。

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

2009年04月05日

第29回 スクリプトエラー発生時にステータスバーにエラーメッセージを表示する (タブブラウザーを作る)

tabbrowser115.gif
前回は表示しているページのjavascriptなどでエラーが発生してもエラーダイアログが表示されないようにした。今回はエラーが発生したときにステータスバーにエラーが出た旨のメッセージが表示されるようにする。

まず内部でエラー通知用に利用する独自メッセージを「stdafx.h」に追加する。
#define	WM_DNP_SCRIPTERROR		(WM_APP + 6)			//■追加

tabbrowser116.gif 次に、前回作成したスクリプトエラーを消すためのクラスに独自インターフェースを実装して、スクリプトエラーの発生がビューに通知されるようにしておく。
#include "DnpDocHostUIHandlerImpl.h"

//■追加
//スクリプトエラーの通知先ウインドウなどを設定するためのインターフェース
MIDL_INTERFACE("2C66181A-27D5-486e-A84A-3B8EDAE1951F")
IScriptErrorSetting : public IUnknown
{
public:
	STDMETHOD (put_hWndNotify)(HWND hWnd) = 0;		//エラーの通知先HWND(ビューのHWND)を指定
};


//スクリプトエラーを消すためのUIHandler
class CHideScriptError :
	public CComObjectRootEx<CComSingleThreadModel>
	,public IDnpDocHostUIHandlerImpl
	,public	IDispatchImpl<IDispatch>
	,public IOleCommandTarget
	,public IScriptErrorSetting			//■追加
{
protected:
	HWND	_hWndNotify;				//■追加
public:

	//■追加
	CHideScriptError()
	{
		_hWndNotify = NULL;
	}

	BEGIN_COM_MAP(CHideScriptError)
		COM_INTERFACE_ENTRY(IScriptErrorSetting)		//■追加
		COM_INTERFACE_ENTRY(IDispatch)
		COM_INTERFACE_ENTRY(IDocHostUIHandler)
		COM_INTERFACE_ENTRY(IOleCommandTarget)
	END_COM_MAP()

	//■追加
	//エラーの通知先HWND(ビューのHWND)を指定
	STDMETHOD (put_hWndNotify)(HWND hWnd)
	{
		_hWndNotify = hWnd;
		return	S_OK;
	}

    STDMETHODIMP QueryStatus( const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText ) 
	{
		return E_NOTIMPL; 
    }

    STDMETHODIMP Exec( const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut)
    {
		//コマンドがスクリプトエラーを示すものなら、エラー表示を無効にする
		if(nCmdID == OLECMDID_SHOWSCRIPTERROR)
		{
			(*pvaOut).vt		= VT_BOOL;
			(*pvaOut).boolVal	= VARIANT_TRUE;

			//■追加
			if(_hWndNotify && ::IsWindow(_hWndNotify))
				::SendMessage(_hWndNotify,WM_DNP_SCRIPTERROR,NULL,NULL);

			return S_OK;
		}

		return E_NOTIMPL;
    }
};

tabbrowser117.gif
そしてビュー側で送られてきたスクリプトエラーメッセージを受信したらステータスバーに表示するようにする。

ステータスバーへのメッセージ表示は以前に作成した処理を利用した。
	BEGIN_MSG_MAP(CTabBrowser100View)
		MESSAGE_HANDLER(WM_DNP_SCRIPTERROR, OnDnpScriptError)		//■追加
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
	END_MSG_MAP()
	//■追加
	//ステータスバーのメッセージ変更をメインウインドウへ通知
	void	ChangeStatusText(LPCTSTR pszText)
	{
		_strLastStatusText = pszText;		//いつでも参照できるようにクラスのメンバーに保存しておく

		if(_pTabView == NULL || _pTabView->GetActivePage() != _pTabView->PageIndexFromHwnd(m_hWnd))
			return;			//このビューがアクティブでなければこれ以降処理しない

		//メインウインドウに変更を通知する
		GetTopLevelWindow().SendMessage(WM_DNP_CHANGESTATUSTEXT,(WPARAM)(LPCTSTR)_strLastStatusText);
	}

	//■内部処理変更
	void __stdcall OnStatusTextChange(BSTR bstrText)
	{
		ChangeStatusText((CAtlString)bstrText);
	}

	//■追加
	LRESULT OnDnpScriptError(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		ChangeStatusText(_T("ページでエラーが発生しました。"));		//固定文字列を表示している!エラーとそうでないのと見分けがつかない
		return	0;
	}

tabbrowser118.gif 最後にスクリプトエラーを検出するクラスにビューウインドウのハンドルを渡しておく。
	//UIHandlerの変更。
	//■■本当はもともと入っていたUIhandlerを取得/保持してそれも併せて使うようにすべき
	bool	ChangeUIHandler(void)
	{
		HRESULT	hr = E_FAIL;

		CComPtr<IDispatch>	pIDispatch;
		CComPtr<ICustomDoc>	pICustomDoc;
		CComPtr<IScriptErrorSetting>	pIScriptErrorSetting;	//■追加 独自に作ったインターフェース

		if(_pHideScriptError == NULL)
			_pHideScriptError = new CComObject<CHideScriptError>;

		//■4行追加 スクリプトエラー発生時の通知先としてこのビューウインドウを登録する
		if(_pHideScriptError)
			_pHideScriptError->QueryInterface(&pIScriptErrorSetting);
		if(pIScriptErrorSetting)
			pIScriptErrorSetting->put_hWndNotify(m_hWnd);

		if(_pIWebBrowser2)
			hr = _pIWebBrowser2->get_Document(&pIDispatch);
		if(pIDispatch)
			hr = pIDispatch->QueryInterface(IID_ICustomDoc,(void **)&pICustomDoc);
		if(pICustomDoc)
			hr = pICustomDoc->SetUIHandler(_pHideScriptError);

		return	SUCCEEDED(hr) ? true : false;
	}

tabbrowser119.gif
これでビルド/実行するとスクリプトエラーが発生したときにステータスバーにメッセージがひょじされるようになった。

とは言うものの、マウスカーソルがリンクの上に乗るなどしてIEコントロールからステータスバーメッセージ更新のイベントが届くとエラーメッセージがすぐに消えてしまう。

次回はエラーメッセージがすぐに消えてしまわないように修正する。

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

第28回 スクリプトエラーダイアログが表示されないようにする (タブブラウザーを作る)

tabbrowser109.gif
今回は前に紹介した「IEコントロールのスクリプトエラー表示を消す」の方法を用いて、javascriptなどにエラーがあってもエラーダイアログが表示されないようにする。

まず、前に作成したクラスをタブブラウザーのプロジェクトに取り込む。「プロジェクト」メニューから「既存項目の追加」を選択する。

tabbrowser110.gif
そして「DnpDocHostUIHandlerImpl.h」をプロジェクトに追加する。

tabbrowser112.gif 前はWTLのアプリケーションウイザードが生成したビュークラスにIDocHostUIHandlerなどを追加することでスクリプトエラーを消した。しかし今回は将来的な拡張のことも考慮して、独立したクラスCHideScriptError を作りそこでエラーダイアログを非表示にすることにした。
#include "DnpDocHostUIHandlerImpl.h"		//■追加

//■追加
//スクリプトエラーを消すためのUIHandler
class CHideScriptError :
	public CComObjectRootEx<CComSingleThreadModel>
	,public IDnpDocHostUIHandlerImpl
	,public	IDispatchImpl<IDispatch>
	,public IOleCommandTarget
{
public:

	BEGIN_COM_MAP(CHideScriptError)
		COM_INTERFACE_ENTRY(IDispatch)
		COM_INTERFACE_ENTRY(IDocHostUIHandler)
		COM_INTERFACE_ENTRY(IOleCommandTarget)
	END_COM_MAP()


    STDMETHODIMP QueryStatus( const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText ) 
	{
		return E_NOTIMPL; 
    }

    STDMETHODIMP Exec( const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut)
    {
		//コマンドがスクリプトエラーを示すものなら、エラー表示を無効にする
		if(nCmdID == OLECMDID_SHOWSCRIPTERROR)
		{
			(*pvaOut).vt		= VT_BOOL;
			(*pvaOut).boolVal	= VARIANT_TRUE;
			return S_OK;
		}

		return E_NOTIMPL;
    }
};

tabbrowser113.gif そしてビュークラス内でIEコントロールのUIHandlerとしてCHideScriptError を渡す。
	CComPtr<IDocHostUIHandler>		_pHideScriptError;		//■追加


	//■追加
	//UIHandlerの変更。本当はもともと入っていたUIhandlerを取得/保持してそれも併せて使うようにすべき
	bool	ChangeUIHandler(void)
	{
		HRESULT	hr = E_FAIL;

		CComPtr<IDispatch>	pIDispatch;
		CComPtr<ICustomDoc>	pICustomDoc;

		if(_pHideScriptError == NULL)
			_pHideScriptError = new CComObject<CHideScriptError>;

		if(_pIWebBrowser2)
			hr = _pIWebBrowser2->get_Document(&pIDispatch);
		if(pIDispatch)
			hr = pIDispatch->QueryInterface(IID_ICustomDoc,(void **)&pICustomDoc);
		if(pICustomDoc)
			hr = pICustomDoc->SetUIHandler(_pHideScriptError);

		return	SUCCEEDED(hr) ? true : false;
	}

	//HTMLページが読み終わったときに呼ばれる処理
	void	_stdcall OnDocumentComplete(IDispatch* pDisp, VARIANT* vURL)
	{
		if(pDisp != _pIWebBrowser2)
			return;			//フレームなどの読み込み終了は無視

		ChangeUIHandler();			//■追加 UIHandlerを変更する

		HICON		hIcon;
		CAtlString	strFile;

		//テンポラリファイルパス取得
		{
			DWORD	dwRet;
			TCHAR	pszPath[MAX_PATH + 1024];
			TCHAR	pszFile[MAX_PATH + 1024];

			dwRet = ::GetTempPath(MAX_PATH + 1024,pszPath);				//テンポラリフォルダ取得
			if(dwRet)
				dwRet = ::GetTempFileName(pszPath,NULL,0,pszFile);		//テンポラリファイル名自動生成
			if(dwRet)
				strFile = pszFile;
			if(strFile == _T(""))
				return;
		}

		//インターネットショートカットファイル作成
		{
			bool	ret;

			strFile += _T(".url");
			ret = CreateInternetShortcut((CAtlString)vURL->bstrVal,strFile);
			if(ret == false)
				return;

			//インターネットショートカットにアイコンが適用されるのを待つ(//■■これだとダメ?要調査)
			::SHChangeNotify(SHCNE_CREATE,SHCNF_PATH | SHCNF_FLUSH,(LPCTSTR)strFile,NULL);
		}

		//ファイルからHICONを取得
		{
			SHFILEINFO	sShFileInfo;
			HIMAGELIST	hImageList;

			//パスからアイコン取得
			hImageList = (HIMAGELIST)::SHGetFileInfo(strFile,0,&sShFileInfo,sizeof(SHFILEINFO),SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
			hIcon = ::ImageList_GetIcon(hImageList,sShFileInfo.iIcon,ILD_NORMAL);
			::ImageList_Destroy(hImageList);
		}
		::remove((CAtlStringA)strFile);		//ファイル削除
		if(hIcon == NULL)
			return;

		//タブにHICONを割り当てる
		{
			int			nPage;
			int			nImage;
			CImageList	cImageList;

			nPage = _pTabView->PageIndexFromHwnd(m_hWnd);
			cImageList = _pTabView->GetImageList();			//タブのイメージリスト取得

			nImage = _pTabView->GetPageImage(nPage);		//現在のイメージ番号取得
			if(nImage < 0)
			{
				//タブに画像がなければ新しく追加して使う
				nImage = cImageList.AddIcon(hIcon);
			}
			else
			{
				//タブに画像があればそれと置き換える
				cImageList.ReplaceIcon(nImage,hIcon);
			}
			_pTabView->SetPageImage(nPage,nImage);		//nImageが変わらなくても再描画のためにセットしなおす
		}
		::DestroyIcon(hIcon);
	}

tabbrowser114.gif 今回作成しているタブブラウザーでは「Googleツールバー」の機能を実装している。このツールバーが独自にUIHandlerを横取りしているようなので、タブが切り替わったときにUIhandlerを上書きしておく。
	//タブの選択変更があったときメインウインドウから呼ばれる
	bool	OnTabPageChange(bool bActivated)
	{
		if(_pIDeskBand == NULL)
			return	false;

		if(bActivated)
			_pIDeskBand->ShowDW(TRUE);		//IEツールバーを表示
		else
			_pIDeskBand->ShowDW(FALSE);		//IEツールバーを消す

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

		return	true;
	}

tabbrowser111.gif
これでビルド/実行すると、そのページにエラーがあってもエラーダイアログがでなくなった。

しかし逆にエラーがあってもユーザーにはエラーがあったことが分からなくなってしまった。次回はユーザーが分かるようにエラーがあった場合には通知する処理を追加する。


※ちなみに「このリンク」をクリックするとスクリプトエラーが発生する。

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

2009年04月04日

第27回 「コピー」「切り取り」などをIEコントロールの状態に合わせて有効にする (タブブラウザーを作る)

tabbrowser106.gif
前回は「編集」メニューの「コピー」などを使えるようにした。しかしメニュー項目が常に有効なままだった。

今回はIEコントロール内の状況に合わせて、メニュー項目の有効と無効が切り替わるようにする。

IWebBrowser2からIHTMLDocumentを取り出し、選択されているHTMLエレメントがあるときは「コピー」メニューを有効に、、、という方法も考えられるが非常に煩雑だ。今回はIOleCommandtarget::QueryStatusを利用して状態を取得する。

状態を取得するための関数をCIEUtilityに追加する。
	//■追加
	//「コピー」「切り取り」「貼り付け」「全てを選択」メニューが使える状態かどうかの取得
	bool	IsEnable_EditCommand(bool& bCopy,bool& bCut,bool& bPaste,bool& bSelectAll)
	{
		HRESULT	hr;
		OLECMD	pOleCmd[] = 
			{
				{ OLECMDID_COPY, 0 },
				{ OLECMDID_CUT, 0 },
				{ OLECMDID_PASTE, 0 },
				{ OLECMDID_SELECTALL, 0 }
			};

		CComPtr<IOleCommandTarget>	pIOleCommandTarget;

		bCopy		= false;
		bCut		= false;
		bPaste		= false;
		bSelectAll	= false;
		if(_pIWebBrowser2)
			_pIWebBrowser2->QueryInterface(&pIOleCommandTarget);
		if(pIOleCommandTarget == NULL)
			return	true;				//trueで返す

		hr = pIOleCommandTarget->QueryStatus(NULL,sizeof(pOleCmd)/sizeof(OLECMD),pOleCmd,NULL);
		if(FAILED(hr))
			return	true;				//trueで返す

		bCopy		= (pOleCmd[0].cmdf & OLECMDF_ENABLED) ? true : false;
		bCut		= (pOleCmd[1].cmdf & OLECMDF_ENABLED) ? true : false;
		bPaste		= (pOleCmd[2].cmdf & OLECMDF_ENABLED) ? true : false;
		bSelectAll	= (pOleCmd[3].cmdf & OLECMDF_ENABLED) ? true : false;

		return	true;
	}

tabbrowser107.gif そして「MainFrm.h」のBEGIN_UPDATE_UI_MAPの中にメニュー項目用のIDを追加し、ちょっと行儀が悪いが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);
		}

		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)
		UPDATE_ELEMENT(ID_EDIT_COPY, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)			//■追加
		UPDATE_ELEMENT(ID_EDIT_CUT, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)			//■追加
		UPDATE_ELEMENT(ID_EDIT_PASTE, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)			//■追加
		UPDATE_ELEMENT(ID_EDIT_SELECT_ALL, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)		//■追加
	END_UPDATE_UI_MAP()

tabbrowser108.gif
これでビルド/実行すると「編集」メニューにある「コピー」「切り取り」「貼り付け」の各項目がIEコントロールの状態に合わせて有効になるようになった。

次回は開いているページに含まれるJavascriptなどにエラーがあってもエラーダイアログが開かないようにする。

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

第26回 「編集」メニューから「コピー」などできるようにする (タブブラウザーを作る)

tabbrowser103.gif
前回は「印刷」「印刷プレビュー」などの機能が使えるようにした。

今回は前回とまったく同じ方法で「コピー」や「切り取り」などのクリップボード関連操作をメニューからできるようにする。

CIEUtilityにCopy()などを実装する。今回は利用しないがSelectAll()も用意した。
	//■追加
	bool	Copy(void)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;
		return	SUCCEEDED(_pIWebBrowser2->ExecWB(OLECMDID_COPY,OLECMDEXECOPT_PROMPTUSER,NULL,NULL)) ? true : false;
	}

	//■追加
	bool	Cut(void)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;
		return	SUCCEEDED(_pIWebBrowser2->ExecWB(OLECMDID_CUT,OLECMDEXECOPT_PROMPTUSER,NULL,NULL)) ? true : false;
	}

	//■追加
	bool	Paste(void)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;
		return	SUCCEEDED(_pIWebBrowser2->ExecWB(OLECMDID_PASTE,OLECMDEXECOPT_PROMPTUSER,NULL,NULL)) ? true : false;
	}

	//■追加
	bool	SelectAll(void)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;
		return	SUCCEEDED(_pIWebBrowser2->ExecWB(OLECMDID_SELECTALL,OLECMDEXECOPT_PROMPTUSER,NULL,NULL)) ? true : false;
	}

tabbrowser104.gif そして「MainFrm.h」の中でメニューが選択されたときに上で作成した関数が呼ばれるようにする。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		MESSAGE_HANDLER(WM_DNP_CHANGESTATUSTEXT, OnDnpChangeStatusText)
		MESSAGE_HANDLER(WM_DNP_CHANGEPROGRESS, OnDnpChangeProgress)
		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_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)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		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_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;
	}

tabbrowser105.gif
これでビルド/実行すると「編集」メニューから「コピー」などの操作ができるようになった。

本来であればIEコントロール上で選択されている部分がないばあいは「コピー」メニューを無効にするべきだ。しかし今回の実装では常にメニューが有効になっている。

次回はIEコントロールの状態に合わせてメニューの有効/無効が自動で変わるようにする。

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

第25回 印刷関連機能が使えるようにする (タブブラウザーを作る)

tabbrowser100.gif
前回はツールバーとして「Googleツールバー」を利用するというかなりヘビーな実装をした。今回は軽く印刷関連機能の実装を行う。

IEコントロールはIWebBrowser2::ExecWB (IOleCommandTarget::Exec)により各種操作ができるように作られている。ここにOLECMDID_PRINTなどを与えることで印刷機能を利用する。

実際にIWebBrowser2を操作する処理をCIEUtilityに追加する。

ちなみに私はIOleCommandTarget関連は後から機能を拡張できる利点がある反面、外側から何が実装されているのかが分かりにくくCOMインターフェースの嫌な部分だと思う。なんとかならないのかな。。。
	//■追加
	//bPrintOrPreview=trueなら印刷、falseなら印刷プレビュー
	bool	PrintOrPreview(bool bPrintOrPreview)
	{
		HRESULT	hr;

		if(_pIWebBrowser2 == NULL)
			return	false;

		if(bPrintOrPreview)
			hr = _pIWebBrowser2->ExecWB(OLECMDID_PRINT,OLECMDEXECOPT_PROMPTUSER,NULL,NULL);
		else
			hr = _pIWebBrowser2->ExecWB(OLECMDID_PRINTPREVIEW,OLECMDEXECOPT_PROMPTUSER,NULL,NULL);

		return	SUCCEEDED(hr) ? true : false;
	}

	//■追加
	bool	PrintSetup(void)
	{
		HRESULT	hr = E_FAIL;

		if(_pIWebBrowser2)
			hr = _pIWebBrowser2->ExecWB(OLECMDID_PAGESETUP,OLECMDEXECOPT_PROMPTUSER,NULL,NULL);

		return	SUCCEEDED(hr) ? true : false;
	}

tabbrowser101.gif そして「MainFrm.h」側でメニュー操作により飛んで来た印刷関連メッセージを処理するメッセージハンドラを追加する。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		MESSAGE_HANDLER(WM_DNP_CHANGESTATUSTEXT, OnDnpChangeStatusText)
		MESSAGE_HANDLER(WM_DNP_CHANGEPROGRESS, OnDnpChangeProgress)
		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_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)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		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_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;
	}

tabbrowser102.gif
これで「印刷」「印刷プレビュー」「プリンタの設定」の各項目が利用できるようになった。

次回は「切り取り」「コピー」「貼り付け」というクリップボード関連機能を実装する。

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

前の10件 11  12  13  14  15  16  17  18  19  20  21