1.常被人鄙视的方法(PreTranslateMessage)
经常见到有人问怎么屏蔽html的右键菜单,有人答用PreTranslateMessage函数拦截wm_rbuttondown消息,于是总会有人说这种方法怎么烂,@_@,我真不知道为什么,不过我想这也是一种方法呀,而且非常简单,所以还是列出来吧:)
BOOL CPreTranslateMsgView::PreTranslateMessage(MSG* pMsg) { if ((pMsg->message == WM_RBUTTONDOWN)||(pMsg->message == WM_RBUTTONDBLCLK)){ CPoint point(pMsg->pt); ScreenToClient(&point);
IHTMLDocument2* pdoc2=NULL; IHTMLElement* pElement=NULL; IDispatch* pDisp=NULL; pDisp=GetHtmlDocument(); pDisp->QueryInterface(IID_IHTMLDocument2,(void**)&pdoc2); pDisp->Release();
pdoc2->elementFromPoint(point.x,point.y,&pElement); pdoc2->Release(); if(pElement){ BSTR ID; pElement->get_id(&ID); pElement->Release(); CString str=(LPCTSTR)(_bstr_t)ID; if(str=="Layer1"){ CMenu menu; menu.LoadMenu(IDR_MENU1 ); CMenu* pmenu=menu.GetSubMenu(0); pmenu->TrackPopupMenu(0,pMsg->pt.x,pMsg->pt.y,this); } } return TRUE;//如果想完全屏蔽掉,不显示任何菜单,直接返回TRUE就行,上面这些代码演示了怎么对html中特定ID的元素弹出自己想要显示的菜单 }else return CHtmlView::PreTranslateMessage(pMsg); }
2.通过子类化IE控件的窗口并处理WM_CONTEXTMENU消息
这是MSDN上介绍的方法,111222翻译了那文章,大家自己去看: 屏蔽CHtmlViewCWebBrower2右键菜单的非官方方法 111222(翻译)。 http://www.csdn.net/develop/Read_Article.asp?Id=10427
3.利用IDocHostUIHandler接口(官方提供的方法) VC.net中的CDHtmlDialog就是这种方法了,想自己实现的可以参考这篇文章: 在对话框中显示网页,并屏蔽掉IE的弹出式菜单 wuya(原作) http://www.csdn.net/develop/Read_Article.asp?Id=8813
4.利用COM的连接点机制,直接处理html元素的事件
我们先看看在VB中想屏蔽掉右键菜单是怎么做的:
Dim WithEvents m_doc As HTMLDocument
Private Function m_doc_oncontextmenu() As Boolean m_doc_oncontextmenu = False End Function
Private Sub WebBrowser1_DownloadComplete() Set m_doc = WebBrowser1.Document End Sub 呵呵,非常简单吧?处理HTMLDocument的oncontextmenu事件,视具体需要弹出自己的菜单,然后返回true或false就可以了。 同理,我们在VC中如果能响应html页面元素对象的事件并做出处理的话也可以达到屏蔽右键菜单的目的了,在VB中响应COM的事件被封装起来,用一个WithEvents关键字就轻松搞定,在VC中怎么实现?? 我们知道COM是通过连接点机制来实现事件的,VC中你得在程序中实现一个特定的接口,并把这个接口的指针通过一定的途径传送给COM对象并通知它需要订阅该对象的消息。
下面通过处理HTMLDocument2的oncontextmenu事件来演示具体怎么在MFC程序中实现屏蔽掉右键菜单,同理你可以处理其它的像DIV,Button等元素的事件来制定相应的右键菜单。 通过查阅MSDN,可以知道要响应HTMLDocument2的事件必须在程序中实现HTMLDocumentEvents2接口,这个接口是Dispinterface类型的,而在MFC想实现一个IDispatch接口就简单的就是从CCmdTarget类派生一个新类并用DECLARE_DISPATCH_MAP,BEGIN_DISPATCH_MAP,DISP_FUNCTION_ID,END_DISPATCH_MAP等宏来添加接口函数,由于CHtmlView或是CDialog都是间接派生自CCmdTarget的,所以直接在这些类上实现这个接口就可以了,以对话框加IE控件为例(稍微改动可用于CHtmlView),实现如下:
1.新建基于对话框工程命名为HtmlDemoDialog,在主对话框中加入Microsoft Web Browser控件并生成包装类等。 2.在主对话框类头文件CHtmlDemoDialogDlg.h中加入 #include <MsHTML.h>//定义了IHTMLDocument2等接口 #include <mshtmdid.h>//定义了HTMLDocumentEvents2接口的方法DISPID class CCHtmlDemoDialogDlg : public CDialog { ... DECLARE_DISPATCH_MAP()//声明dispatch map表 public: BOOL onHtmlContextMenu(IHTMLEventObj *pEvtObj); //事件处理函数,原型可以参考MSDN中关于HTMLDocumentEvents2的说明 DWORD m_dwCookie; //用于标记一个连接点 IHTMLDocument2* pDoc2; //想要处理事件的COM对象的指针 ... } 2.在对话框类实现文件CHtmlDemoDialogDlg.cpp中加入 #include <afxctl.h>//定义了AfxConnectionAdvise、AfxConnectionUnadvise等函数,等会连接到事件源时要用到 ... //填充dispatch map表,以供Invoke()调用 BEGIN_DISPATCH_MAP(CCHtmlDemoDialogDlg, CDialog) DISP_FUNCTION_ID(CCHtmlDemoDialogDlg, "oncontextmenu", DISPID_HTMLDOCUMENTEVENTS2_ONCONTEXTMENU, onHtmlContextMenu, VT_BOOL, VTS_DISPATCH) END_DISPATCH_MAP() ...
CWebbrowserDlg::CWebbrowserDlg(CWnd* pParent /*=NULL*/) : CDialog(CWebbrowserDlg::IDD, pParent) { EnableAutomation();//必须有,否则等会用GetIDispatch()时会失败. ... }
BOOL CCHtmlDemoDialogDlg::onHtmlContextMenu(IHTMLEventObj *pEvtObj) { //在成功连接上事件源后,每次用户右击都会调用这个函数,你可以根据pEvtObj来判断当前光标位置等,然后决定是自己弹出菜单,让IE弹出菜单,还是什么都不做... return FALSE; }
void CCHtmlDemoDialogDlg::OnDocumentCompleteExplorer1(LPDISPATCH pDisp, VARIANT FAR* URL) { //处理WebBrowser控件的DocumentComplete事件,并初始化pDoc2指针和连接到事件源 HRESULT hr=m_wb.GetDocument()->QueryInterface(IID_IHTMLDocument2,(void**)&pDoc2); BOOL Ret = AfxConnectionAdvise( pDoc2, //可连接对象的接口指针 DIID_HTMLDocumentEvents2, //连接接口ID GetIDispatch(FALSE), //把内嵌的IDispatch实现类的一个对象实例m_xDispatch传了出去 FALSE, //donod addref &m_dwCookie ); //cookie to break connection later... if(Ret){ AfxMessageBox("成功挂接上"); } } 3.到这里,基本步骤都以完成,运行后如果没有什么灾难发生的话可以看到"成功挂接上"的消息框,并且在IE控件中点击右键不会弹出菜单,断开事件连接的代码如下: AfxConnectionUnadvise(pDoc2, DIID_HTMLDocumentEvents2 , GetIDispatch(FALSE), FALSE, m_dwCookie ); 其余的善后工作交给你去处理了.
(转载)

|