萬盛學電腦網

 萬盛學電腦網 >> 網頁制作 >> 腳本Html教程 >> 如何禁用HTML頁面的上下文菜單

如何禁用HTML頁面的上下文菜單

 提出問題: 

  VC知識庫《在線雜志》第六期有一篇文章“VC6中使用CHtmlView在對話框控制中顯示HTML文件”,很多讀者來信說很喜歡這種功能。但是美中不足的是在對話框的HTML頁面上單擊鼠標右鍵會彈出上下文菜單。從而可以象在IE中那樣看到頁面的源代碼。為了防止用戶查看HTML代碼,有人嘗試過在CHtmlCtrl派生的窗口中重載WM_CONTEXTMENU,或者在CHtmlView以及CHtmlCtrl類中禁用右鍵的上下文菜單和彈出式菜單,這兩個方法都沒有成功。那麼如何禁用HTML的這個上下文菜單呢? 本文就針對這個問題用不同的方法來完善上次的程序。

  解答:

  CHtmlCtrl類可以將CHtmlView轉換成在任何窗口中使用的控制。我用它寫了一個程序叫AboutHtml,此程序實現了一個HTML對話框。但疏忽了鼠標右鍵的上下文菜單,所以在HTML對話框中單擊鼠標右鍵,會彈出標准的浏覽器上下文菜單(如圖一),而這個菜單對於某些人來說可能是多余的。


圖一 不想要的上下文菜單

  其實,要解決這個問題有一個非常簡單的辦法,真是易如反掌,甚至不用寫任何C++代碼!只要在HTML頁面中加一行指令即可:

  //

  //

  這條指令告訴浏覽器不要顯示上下文菜單。也可以象下面這樣寫: 

  //

  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)定制它!否則後果不堪設想。

copyright © 萬盛學電腦網 all rights reserved