如何禁用HTML页面的上下文菜单

本文介绍如何在HTML对话框中禁用右键上下文菜单,提供两种方法:一种是简单地在HTML页面中添加一行JavaScript代码阻止菜单显示;另一种是通过C++实现IDocHostUIHandler接口,子类化InternetExplorer_Server窗口。
提出问题:
VC知识库《在线杂志》第六期有一篇文章“VC6中使用CHtmlView在对话框控制中显示HTML文件”,很多读者来信说很喜欢这种功能。但是美中不足的是在对话框的HTML页面上单击鼠标右键会弹出上下文菜单。从而可以象在IE中那样看到页面的源代码。为了防止用户查看HTML代码,有人尝试过在CHtmlCtrl派生的窗口中重载WM_CONTEXTMENU,或者在CHtmlView以及CHtmlCtrl类中禁用右键的上下文菜单和弹出式菜单,这两个方法都没有成功。那么如何禁用HTML的这个上下文菜单呢? 本文就针对这个问题用不同的方法来完善上次的程序。
解答:
CHtmlCtrl类可以将CHtmlView转换成在任何窗口中使用的控制。我用它写了一个程序叫AboutHtml,此程序实现了一个HTML对话框。但疏忽了鼠标右键的上下文菜单,所以在HTML对话框中单击鼠标右键,会弹出标准的浏览器上下文菜单(如图一),而这个菜单对于某些人来说可能是多余的。

图一 不想要的上下文菜单
其实,要解决这个问题有一个非常简单的办法,真是易如反掌,甚至不用写任何C++代码!只要在HTML页面中加一行指令即可:
//<BODY oncontextmenu="return false">//

这条指令告诉浏览器不要显示上下文菜单。也可以象下面这样写:
//oncontextmenu="ShowMyMenu(); return false"//
ShowMyMenu是一个显示定制菜单的JavaScript过程。本文例子代码之一AboutHtml1使用的就是oncontextmenu。源代码可以从本文的开始处下载。
由于VC知识库是一个关于C++以及Visual C++的网站,与JavaScript之类的脚本语言没什么关系。所以我们要用另一种稍微复杂一点的方法来实现相同的事情,那就是用C++来做。为此,正规的C++方法是实现IDocHostUIHandler接口,而且要做的事情很多。至于为什么要实现它,请参见有关文档。用WM_CONTEXTMENU 或者 WM_RBUTTONDOWN来处理这个问题的思路的确是通常Windows做事情的方式。但是问题是CHtmlCtrl窗口不是真正的输入窗口。窗口有很多种,只要用Spy++工具看一下我们的例子程序就知道在你眼前会出现多少种窗口。如图二所示,在实际的输入窗口上,浏览器窗口有三级父/子窗口。
Dialog AfxFrameOrView42d // CHtmlCtrl Shell Embedding Shell DocObject View Internet Explorer_Server
它是个接收输入的Internet Explorer_Server服务器窗口,并且如果你想要截获WM_CONTEXTMENU消息,必须子类化这个窗口。在MFC中,这意味着你必须获取HWND并调用SubclassWindow。记住了,这是一种非常规方式,而且微软的那帮家伙也明确禁止这样做,不过我还是根据原来的程序写了另一个版本AboutHtml2,我这么做了。
图二在Spy++中的父/子关系
获得这个神秘的Internet Explorer_Server HWND的方法有很多种。但FindWindow不行,因为它只能得到顶层窗口。由于此服务器窗口是浏览器的曾孙(great-grandchild),在所有层次上都没有同胞兄弟,所以下列算法成立:
static HWND GetLastChild(HWND hwndParent) { HWND hwnd = hwndParent; while (TRUE) { HWND hwndChild = ::GetWindow(hwnd, GW_CHILD); if (hwndChild==NULL) return hwnd; hwnd = hwndChild; } return NULL; }
这个函数假设只有单子继承链,如同浏览器中的一个窗口——即每个父窗口肯定有一个子窗口——并且获取最末尾(或最小)的子窗口就是Internet Explorer_Server窗口。一旦取得HWND,剩下的事情便是写一个新的MFC类对它进行子类化。
class CMyIEWnd : public CWnd {
public:
   afx_msg void OnContextMenu(CWnd* pWnd, CPoint pos) { }
   DECLARE_MESSAGE_MAP();
};

这个类重载WM_CONTEXTMENU,其它什么事情也不做:OnContextMenu是个空函数,返回的东西不显示菜单,也不调用基类(CWnd)的方法。使用CMyIEWnd时,在CMyHtmlCtrl中添加一个实例:
//class CMyHtmlCtrl : public CHtmlCtrl {protected:   CMyIEWnd m_myIEWnd;};//

把这一切联系在一起的最关键的一步是调用SubclassWindow。但在哪里调用以及什么时候调用呢?最好时机是在浏览器加载页面之后。
void CMyHtmlCtrl::OnNavigateComplete2(LPCTSTR strURL)
{
   if (!m_myIEWnd.m_hWnd) {
      HWND hwnd = GetLastChild(m_hWnd);
      m_myIEWnd.SubclassWindow(hwnd);
   }
}

具体处理过程是这样的:当用户打开“关于”对话框,对话框创建CHtmlCtrl窗口来打开文档,当浏览器将文档打开以后,它发送一个通知,MFC将这个通知定向到OnNavigateComplete2。CMyHtmlCtrl::OnNavigateComplete2调用GetLastChild来获得“真正的”输入窗口并将它子类化。这时所有的消息将通过CMyIEWnd类去往Internet Explorer_Server,包括WM_CONTEXTMENU。这里要注意,IE的HWND是可以修改的,所以如果除了“关于”对话框外,你还想做一些其它的事情的话,必须要对HWND进行反子类化(unsubclass)和重子类化(resubclass)处理。
使用这个技术有两个重要事情需要注意。第一,它功能很强,因为你子类化了“真正的”IE窗口,你可以做几乎任何事情。第二,如果你不小心而使用不当,那将会发生最糟糕最糟糕的事情。一旦你用这种方法控制了资源管理器窗口,等于是把所有赌注放进去了。记住不要用不正当的方式去玩弄浏览器,而是要通过正式接口(IDocHostUIHandler)定制它!否则后果不堪设想。
屏幕取词程序VC源码 nhw32.dll 主要引出两个函数: 1. DWORD WINAPI BL_SetFlag32(UINTnFlag, HWND hNotifyWnd, int MouseX, int MouseY) 功能: 启动或停止取词。 参数: nFlag [输入] 指定下列值之一: GETWORD_ENABLE: 开始取词。在重画被取单词区域前设置此标志。 nhw32.dll是通过 重画单词区域,截取TextOutA, TextOutW,ExtTextOutA, ExtTextOutW等Windows API函数的参数来取词的。 GETWORD_DISABLE: 停止取词。 hNotifyWnd [输入] 通知窗口句柄。当取到此时,向该通知窗口发送一登记消息: GWMSG_GETWORDOK。 MouseX [输入] 指定取词点的X坐标。 MouseY [输入] 指定取词点的Y坐标。 返回值: 可忽略。 2. DWORD WINAPI BL_GetText32(LPSTRlpszCurWord, int nBufferSize, LPRECT lpWordRect) 功能: 从内部缓冲区取出单词文本串。对英语文本,该函数最长取出一行内以 空格为界的三个英文单词串,遇空格,非英文字母及除‘-’外的标点符 号,则终止取词。对汉字文本,该函数最长取出一行汉字串,遇英语字 母,标点符号等非汉语字符,则终止取词。该函数能同时取出英语和 汉语字符。 参数: lpszCurWord [输入] 目的缓冲区指针。 nBufferSize [输入] 目的缓冲区大小。 lpWordRect [输出] 指向 RECT 结构的指针。该结构定义了被取单词所在矩形区域。 返回值: 当前光标在全部词中的位置。 此外,WinNT/2000版nhw32.dll 还引出另两个函数: 1. BOOL WINAPI SetNHW32() 功能: Win NT/2000 环境下的初始化函数。一般在程序开始时,调用一次。 参数: 无。 返回值: 如果成功 TRUE ,失败 FALSE 。 2. BOOL WINAPI ResetNHW32() 功能: Win NT/2000 环境下的去初始化函数。一般在程序结束时调用。 参数: 无。 返回值: 如果成功 TRUE ,失败 FALSE
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值