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;に変更する。
#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;
}
};