使用 MFC `CHtmlView` 或 `CWebBrowser2` 開發內嵌瀏覽器時,點擊頁面內的超鏈接(``)或提交表單,新頁面卻在系統默認瀏覽器(IE/Edge/Chrome)中打開,而非在當前 MFC 窗口中導航。
`CHtmlView` 封裝了 WebBrowser ActiveX 控件。默認情況下,WebBrowser 控件**不會**自動處理導航請求,而是觸發 `DWebBrowserEvents2::NewWindow2` / `NewWindow3` 事件,期望宿主應用程序決定在哪裡打開新窗口。若宿主未處理此事件並取消默認行為,控件會將導航委托給系統默認瀏覽器。
void CMyHtmlView::OnNewWindow2(LPDISPATCH* ppDisp, BOOL* Cancel)
{
// 1. 取消默認的新窗口創建行為
*Cancel = TRUE;
// 2. 獲取即將導航的目標 URL
// 注意:此事件觸發時,URL 尚未直接傳遞,需通過其他方式獲取
// 常用技巧:在 BeforeNavigate2 中記錄最後一次導航 URL
CString strUrl = m_strLastNavigatedUrl; // 需在 BeforeNavigate2 中更新
// 3. 讓當前 WebBrowser 控件導航到該 URL
if (!strUrl.IsEmpty()) {
// 使用當前控件的 IWebBrowser2 導航
Navigate2(strUrl, NULL, NULL, NULL, NULL);
}
// 4. 重要:將 ppDisp 設置為 NULL,表示不創建新窗口
*ppDisp = NULL;
}
void CMyHtmlView::OnBeforeNavigate2(LPCTSTR lpszURL, DWORD nFlags,
LPCTSTR lpszTargetFrameName, CByteArray& baPostedData,
LPCTSTR lpszHeaders, BOOL* Cancel)
{
// 記錄即將導航的 URL,供 NewWindow2 使用
m_strLastNavigatedUrl = lpszURL;
// 若目標框架為 _blank 或目標為新窗口,可選擇在此直接取消並自行導航
if (lpszTargetFrameName && (_tcscmp(lpszTargetFrameName, _T("_blank")) == 0 ||
_tcscmp(lpszTargetFrameName, _T("_new")) == 0)) {
*Cancel = TRUE;
Navigate2(lpszURL, NULL, NULL, NULL, NULL);
}
}
class CMyHtmlView : public CHtmlView
{
DECLARE_DYNCREATE(CMyHtmlView)
protected:
CString m_strLastNavigatedUrl; // 記錄最後導航 URL
afx_msg void OnBeforeNavigate2(LPCTSTR lpszURL, DWORD nFlags, LPCTSTR lpszTargetFrameName,
CByteArray& baPostedData, LPCTSTR lpszHeaders, BOOL* Cancel);
afx_msg void OnNewWindow2(LPDISPATCH* ppDisp, BOOL* Cancel);
// 若支援 NewWindow3(IE6+),建議同時處理
afx_msg void OnNewWindow3(LPDISPATCH* ppDisp, BOOL* Cancel, DWORD dwFlags,
BSTR bstrUrlContext, BSTR bstrUrl);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyHtmlView, CHtmlView)
ON_NOTIFY_EX_RANGE(WebBrowser_EVENT_BEFORENAVIGATE2, 0, 0, OnBeforeNavigate2)
ON_NOTIFY_EX_RANGE(WebBrowser_EVENT_NEWWINDOW2, 0, 0, OnNewWindow2)
ON_NOTIFY_EX_RANGE(WebBrowser_EVENT_NEWWINDOW3, 0, 0, OnNewWindow3)
END_MESSAGE_MAP()
| 事件 | 用途 | 關鍵操作 |
|---|---|---|
| BeforeNavigate2 | 攔截所有導航,獲取目標 URL、目標框架名 | 記錄 URL;若 TargetFrame 為 _blank,直接 Cancel 並自行 Navigate2 |
| NewWindow2 | 處理 window.open、target=_blank 等新窗口請求 | Cancel=TRUE;用當前控件 Navigate2 到記錄的 URL;ppDisp=NULL |
| NewWindow3 | 增強版,提供更多上下文(Flags, URL Context) | 同 NewWindow2,優先處理 NewWindow3 |
MFC WebBrowser 基於 **IE Trident 內核**,已停止更新,兼容性差、安全風險高。微軟推薦遷移至 **WebView2**(基於 Edge Chromium):
MFC `CHtmlView` 點擊鏈接跳轉外部瀏覽器,本質是**未處理 `NewWindow2/3` 事件導致默認行為生效**。核心修復:在 `BeforeNavigate2` 緩存目標 URL,在 `NewWindow2/3` 中設置 `Cancel=TRUE`、用當前控件 `Navigate2` 導航、置 `ppDisp=NULL`。長遠看,強烈建議遷移至 **WebView2** 獲得現代渲染引擎與安全支援。