IEコントロールの右クリックメニューを実装する

IEコントロールの右クリックメニューを自分で制御したいときにはIDocHostUIHandlerを実装して、SetExternalUIHandlerで設定すればいい。
ここでの処理はIE8のアクセラレーター項目には対応しない。

1.Visual Studio 2008の「ファイル」メニューにある「新規」メニューから「プロジェクト」を選択する。

2.「新しいプロジェクト」の画面で「ATL/WTLアプリケーションウイザード」を選択する。ここではプロジェクト名を「RightClickTest」とした。

3.アプリケーションウイザードが開いたら、「ユーザーインターフェース機能」のタブにある「ビューウインドウの形式」で「HTML」ページを選択して「完了」ボタンを押す。

4.(以上の操作でアプリケーションウイザードにより自動的にひな形となるソースコードが自動生成される)

5.生成したソースコードのViewクラスを開き、以下のように6箇所ソースコードを追加する。

#pragma once

#include "DnpDocHostUIHandlerDispatchImpl.h"	//■追加

class CRightClickTestView : public CWindowImpl<CRightClickTestView, CAxWindow>
	,public CComObjectRootEx<CComSingleThreadModel>		//■追加
	,public IDnpDocHostUIHandlerDispatchImpl			//■追加
{
public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())

	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(CRightClickTestView)
	END_MSG_MAP()


	//■以下4行を追加
	BEGIN_COM_MAP(CRightClickTestView)
		COM_INTERFACE_ENTRY(IDispatch)
		COM_INTERFACE_ENTRY(IDocHostUIHandlerDispatch)
	END_COM_MAP()


	//■右クリック表示用の仮想関数を追加
	STDMETHOD(ShowContextMenu)(DWORD dwID,DWORD x,DWORD y,IUnknown *pcmdTarget,IDispatch *pdispReserved,HRESULT *dwRetVal)
	{
		bool	ret;

		HMENU	hMenu;
		HWND	hWnd;
		bool	bUseShellExtention;
		bool	bUseLanguage;
		bool	bMenuUserCtrl;

		hMenu = NULL;
		hWnd = NULL;
		bUseShellExtention	= true;		//シェルエクステンションメニューを追加する
		bUseLanguage		= true;		//エンコードメニューを追加する
		bMenuUserCtrl		= true;		//メニュー項目の有効/無効を自分で決定する

		ret = GetDefaultContextMenu(hMenu,hWnd,dwID,pcmdTarget,bUseShellExtention,bUseLanguage);
		if(ret == false)
			return	E_NOTIMPL;		//メニュー取得失敗。IEデフォルト処理に任せる

		//メニューの表示とその処理
		{
			int		nSelection;

			if(bMenuUserCtrl)
			{
				//自分でメニューの有効/無効を決定する(WM_INITMENUPOPUPもしくはこの関数内で設定可能になる)
				nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,m_hWnd,(RECT*)NULL);
			}
			else
			{
				//IEにメニューの有効/無効を決定させる
				nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,hWnd,(RECT*)NULL);
			}
			::DestroyMenu(hMenu);

			//メニュー選択結果の処理
			if(nSelection)
				::SendMessage(hWnd,WM_COMMAND,nSelection,NULL);
		}

		*dwRetVal = S_OK;
		return S_OK;
	}


	//■生成時処理用に関数を追加
	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 = CWindowImpl<CRightClickTestView, CAxWindow>::Create(hWndParent,rect,szWindowName,dwStyle,dwExStyle,MenuOrID,lpCreateParam);
		if(hWnd == NULL)
			return	NULL;

		//右クリックメニュー処理をこのクラスで行う
		SetExternalUIHandler(static_cast<IDocHostUIHandlerDispatch*>(this));

		return	hWnd;
	}
};
6.「MainFrm.h」を開き、
	CRightClickTestView		m_view;
という部分を
	CComObject<CRightClickTestView> m_view;
に変更する。

7.最後に「DnpDocHostUIHandlerDispatchImpl.h」というファイルを作成し、その内容を以下のようにする。
#pragma	once

#include "Mshtmcid.h"

///
///\brief
///	IDocHostUIHandlerDispatch実装補助用クラス
///
///仮想関数はすべてE_NOTIMPLを返す
///
class	IDnpDocHostUIHandlerDispatchImpl :
	public IDispatchImpl<IDocHostUIHandlerDispatch, &IID_IDocHostUIHandlerDispatch, &LIBID_ATLLib>
{
public:
	virtual HRESULT STDMETHODCALLTYPE ShowContextMenu( 
		/* [in] */ DWORD dwID,
		/* [in] */ DWORD x,
		/* [in] */ DWORD y,
		/* [in] */ IUnknown *pcmdtReserved,
		/* [in] */ IDispatch *pdispReserved,
		/* [retval][out] */ HRESULT *pdwRetVal)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetHostInfo( 
		/* [out][in] */ DWORD *pdwFlags,
		/* [out][in] */ DWORD *pdwDoubleClick)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE ShowUI( 
		/* [in] */ DWORD dwID,
		/* [in] */ IUnknown *pActiveObject,
		/* [in] */ IUnknown *pCommandTarget,
		/* [in] */ IUnknown *pFrame,
		/* [in] */ IUnknown *pDoc,
		/* [retval][out] */ HRESULT *pdwRetVal)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE HideUI( void)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE UpdateUI( void)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE EnableModeless( 
		/* [in] */ VARIANT_BOOL fEnable)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE OnDocWindowActivate( 
		/* [in] */ VARIANT_BOOL fActivate)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE OnFrameWindowActivate( 
		/* [in] */ VARIANT_BOOL fActivate)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE ResizeBorder( 
		/* [in] */ long left,
		/* [in] */ long top,
		/* [in] */ long right,
		/* [in] */ long bottom,
		/* [in] */ IUnknown *pUIWindow,
		/* [in] */ VARIANT_BOOL fFrameWindow)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( 
		/* [in] */ DWORD_PTR hWnd,
		/* [in] */ DWORD nMessage,
		/* [in] */ DWORD_PTR wParam,
		/* [in] */ DWORD_PTR lParam,
		/* [in] */ BSTR bstrGuidCmdGroup,
		/* [in] */ DWORD nCmdID,
		/* [retval][out] */ HRESULT *pdwRetVal)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetOptionKeyPath( 
		/* [out] */ BSTR *pbstrKey,
		/* [in] */ DWORD dw)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDropTarget( 
		/* [in] */ IUnknown *pDropTarget,
		/* [out] */ IUnknown **ppDropTarget)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetExternal( 
		/* [out] */ IDispatch **ppDispatch)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE TranslateUrl( 
		/* [in] */ DWORD dwTranslate,
		/* [in] */ BSTR bstrURLIn,
		/* [out] */ BSTR *pbstrURLOut)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE FilterDataObject( 
		/* [in] */ IUnknown *pDO,
		/* [out] */ IUnknown **ppDORet)
	{
		return	E_NOTIMPL;
	}





	///
	///\brief
	///	IEの右クリックメニューの取得
	///
	///\param[in,out]	hMenu
	///取得/生成した右クリックメニュー
	///必ずNULLを渡すこと!
	///
	///\param	hWnd
	///メニュー選択結果の送信先HWND。\n
	///必ずNULLを渡すこと!
	///
	///\param[in]	dwID
	///IDocHostUIHandler::ShowContextMenu() の第1引数
	///
	///\param	*pcmdTarget
	///IDocHostUIHandler::ShowContextMenu() の第3引数IOleCommandTargetのIUnknown
	///
	///\param[in]	bUseShellExtention
	///シェルエクステンションメニューを利用するかどうか
	///たとえばフリーソフトなどで追加される「○○でダウンロード」などのメニュー。trueなら追加する。
	///
	///\param[in]	bUseLanguage
	///エンコードメニューを利用するかどうか
	///ページの言語エンコード指定用のメニュー。trueなら追加する。
	///
	///\retval	true	取得成功
	///\retval	false	失敗
	///
	///取得方法は以下のHPを参照
	///「WebBrowser Customization (Part 2)」
	///http://msdn.microsoft.com/en-us/library/aa770042(VS.85).aspx
	///
	///- 利用例
	/** \code
		STDMETHOD(ShowContextMenu)(DWORD dwID,DWORD x,DWORD y,IUnknown *pcmdTarget,IDispatch *pdispReserved,HRESULT *dwRetVal)
		{
			bool	ret;

			HMENU	hMenu;
			HWND	hWnd;
			bool	bUseShellExtention;
			bool	bUseLanguage;
			bool	bMenuUserCtrl;

			hMenu = NULL;
			hWnd = NULL;
			bUseShellExtention	= true;		//シェルエクステンションメニューを追加する
			bUseLanguage		= true;		//エンコードメニューを追加する
			bMenuUserCtrl		= true;		//メニュー項目の有効/無効を自分で決定する

			ret = GetDefaultContextMenu(hMenu,hWnd,dwID,pcmdTarget,bUseShellExtention,bUseLanguage);
			if(ret == false)
				return	E_NOTIMPL;		//メニュー取得失敗。IEデフォルト処理に任せる

			//メニューの表示とその処理
			{
				int		nSelection;

				if(bMenuUserCtrl)
				{
					//自分でメニューの有効/無効を決定する(WM_INITMENUPOPUPもしくはこの関数内で設定可能になる)
					nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,m_hWnd,(RECT*)NULL);
				}
				else
				{
					//IEにメニューの有効/無効を決定させる
					nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,hWnd,(RECT*)NULL);
				}
				::DestroyMenu(hMenu);

				//メニュー選択結果の処理
				if(nSelection)
					::SendMessage(hWnd,WM_COMMAND,nSelection,NULL);
			}

			*dwRetVal = S_OK;
			return S_OK;
		}
	\endcode
	*/
	///
	///\todo
	///IE8では未来クリックメニューにアクセラレーターがあるが、SHDVIDが不明なため追加できない
	///
	bool	GetDefaultContextMenu(HMENU& hMenu,HWND& hWnd,DWORD dwID,IUnknown *pcmdTarget,bool bUseShellExtention,bool bUseLanguage)
	{
		#define IDR_BROWSE_CONTEXT_MENU  24641
		#define SHDVID_GETMIMECSETMENU   27
		#define SHDVID_ADDMENUEXTENSIONS 53

		HRESULT		hr;
		CComPtr<IOleCommandTarget>	pIOleCommandTarget;

		if(pcmdTarget == NULL)
			return	false;

		ATLASSERT(hMenu == NULL);
		ATLASSERT(hWnd == NULL);

		pcmdTarget->QueryInterface(IID_IOleCommandTarget,(void**)&pIOleCommandTarget);

		//IEのhWndの取得
		{
			CComPtr<IOleWindow>	pIOleWindow;

			hr = pcmdTarget->QueryInterface(IID_IOleWindow,(void**)&pIOleWindow);
			hr = pIOleWindow->GetWindow(&hWnd);
		}

		//DLLからのメニューリソース読み込み
		{
			HINSTANCE	hDll;

			hDll = ::LoadLibrary(TEXT("SHDOCLC.DLL"));			//XPまではこのDLL
			if(hDll == NULL)
				hDll = ::LoadLibrary(_T("ieframe.dll"));		//VistaではこのDLL

			if(hDll == NULL)
				return	false;

			hMenu = ::LoadMenu(hDll,MAKEINTRESOURCE(IDR_BROWSE_CONTEXT_MENU));
			hMenu = ::GetSubMenu(hMenu,dwID);

			::FreeLibrary(hDll);
		}

		//言語サブメニューの追加
		if(bUseLanguage)
		{
			CComVariant		var;
			MENUITEMINFO	mii = {0};

			hr = pIOleCommandTarget->Exec(&CGID_ShellDocView, SHDVID_GETMIMECSETMENU, 0, NULL, &var);

			mii.cbSize = sizeof(mii);
			mii.fMask  = MIIM_SUBMENU;
			mii.hSubMenu = (HMENU) var.byref;

			::SetMenuItemInfo(hMenu, IDM_LANGUAGE, FALSE, &mii);
		}


		//IEのExtentionのメニューを追加
		//たとえばフリーソフトなどで追加される「○○でダウンロード」などのメニュー
		if(bUseShellExtention)
		{
			CComVariant	var1;
			CComVariant	var2;

			V_VT(&var1) = VT_INT_PTR;
			V_BYREF(&var1) = hMenu;

			V_VT(&var2) = VT_I4;
			V_I4(&var2) = dwID;
			hr = pIOleCommandTarget->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);
		}



		//IEコントロールデフォルトの設定でメニュー項目の有効/無効/チェック設定をする
		{
			int	i;

			for (i = 0; i < GetMenuItemCount(hMenu); i++)
			{
				OLECMD olecmd;
				olecmd.cmdID = GetMenuItemID(hMenu, i);

				//■■通常のOLECMDFとは値が異なる。要調査
				if (olecmd.cmdID > 0)
				{
					UINT mf;
					pIOleCommandTarget->QueryStatus(&CGID_MSHTML,1,&olecmd,NULL);
					switch (olecmd.cmdf)
					{
					case OLECMDF_ENABLED:
					case 3:
						mf = MF_BYCOMMAND | MF_ENABLED | MF_UNCHECKED;
						break;

					case OLECMDF_LATCHED:
						mf = MF_BYCOMMAND | MF_ENABLED | MF_CHECKED;
						break;

					case 1:
					default:
						mf = MF_BYCOMMAND | MF_DISABLED | MF_GRAYED;
						break;
					}
					::CheckMenuItem(hMenu, olecmd.cmdID, mf);
					::EnableMenuItem(hMenu, olecmd.cmdID, mf);
				}
			}
		}

		return	true;
	}
};

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


カテゴリー「Internet Explorer」 のエントリー