BHO (Browser Helper Object) 是實作特定 COM 介面的元件,隨 IE / 檔案總管啟動載入,能攔截瀏覽事件、修改 DOM、自訂工具列/選單、監控使用者行為。相較重寫瀏覽器,BHO 開發成本低、相容性好。本文示範使用 ATL 建立 BHO 實作網址過濾功能。
DllRegisterServer)。HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects 建立以 CLSID 為名的子機碼。CoCreateInstance 建立實例。IObjectWithSite::SetSite(IUnknown* pUnkSite),傳遞瀏覽器 IWebBrowser2 指標。IConnectionPointContainer 註冊事件接收 (DWebBrowserEvents2)。IDispatch::Invoke 處理。| Shell 版本 | OS | IE 版本 | 支援 |
|---|---|---|---|
| 4.00 | Win95/NT4 | 4.0 | 僅 IE |
| 4.71 | Win95/NT4 | 4.0 | IE + 檔案總管 |
| 4.72 | Win98 | 4.0 | IE + 檔案總管 |
| 5.00 | Win2000 | 5.0 | IE + 檔案總管 |
注意:BHO 僅支援 IE (Trident 引擎),不支援 Edge (Chromium)、Chrome、Firefox。
BhoPlugin → 預設設定 (Dynamic Link to ATL)。EyeOnIE → 類別 CEyeOnIE,基類含 IObjectWithSiteImpl。IObjectWithSite::SetSiteEyeOnIE.h 新增成員:
#include // IWebBrowser2 定義
#include // 事件 DISPID 定義
class ATL_NO_VTABLE CEyeOnIE :
public CComObjectRootEx,
public CComCoClass,
public IObjectWithSiteImpl,
public IDispatchImpl
{
CComQIPtr m_spWebBrowser2;
DWORD m_dwCookie; // 事件連線 Cookie
public:
STDMETHOD(SetSite)(IUnknown* pUnkSite);
// ...
};
EyeOnIE.cpp 實作:
STDMETHODIMP CEyeOnIE::SetSite(IUnknown* pUnkSite)
{
// 1. 呼叫基類實作 (處理站點設定/清除)
HRESULT hr = IObjectWithSiteImpl::SetSite(pUnkSite);
if (FAILED(hr)) return hr;
if (pUnkSite) // 啟動階段
{
// 取得 IWebBrowser2
m_spWebBrowser2 = pUnkSite;
if (!m_spWebBrowser2) return E_NOINTERFACE;
// 註冊事件接收
CComQIPtr spCPC = m_spWebBrowser2;
if (spCPC)
{
CComPtr spCP;
spCPC->FindConnectionPoint(DIID_DWebBrowserEvents2, &spCP);
if (spCP)
spCP->Advise(GetUnknown(), &m_dwCookie);
}
}
else // 關閉階段
{
// 取消事件註冊
if (m_spWebBrowser2 && m_dwCookie)
{
CComQIPtr spCPC = m_spWebBrowser2;
if (spCPC)
{
CComPtr spCP;
spCPC->FindConnectionPoint(DIID_DWebBrowserEvents2, &spCP);
if (spCP) spCP->Unadvise(m_dwCookie);
}
m_dwCookie = 0;
}
m_spWebBrowser2.Release();
}
return S_OK;
}
IDispatch::Invoke 處理事件僅處理感興趣事件,範例:DISPID_BEFORENAVIGATE2 (導航前) 實作網址過濾。
STDMETHODIMP CEyeOnIE::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
if (dispIdMember == DISPID_BEFORENAVIGATE2)
{
// 參數順序: pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel
if (pDispParams->cArgs >= 7)
{
VARIANT* pUrl = &pDispParams->rgvarg[5]; // URL 在倒數第 2 (索引 5)
VARIANT* pCancel = &pDispParams->rgvarg[0]; // Cancel 在索引 0
if (pUrl->vt == VT_BSTR && pCancel->vt == (VT_BYREF | VT_BOOL))
{
CString strUrl(pUrl->bstrVal);
strUrl.MakeLower();
if (strUrl.Find(_T("girl.com")) != -1) // 過濾關鍵字
{
*pCancel->pboolVal = VARIANT_TRUE; // 取消導航
MessageBox(NULL, _T("此網站被封鎖!"), _T("BHO 過濾"), MB_ICONWARNING);
}
}
}
return S_OK;
}
else if (dispIdMember == DISPID_QUIT) // 瀏覽器關閉
{
// 已在 SetSite(nullptr) 中處理取消註冊
}
return S_OK;
}
在 DllMain 判斷載入進程:
extern "C" BOOL WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL, szPath, MAX_PATH);
CString strPath(szPath); strPath.MakeLower();
if (strPath.Find(_T("explorer.exe")) != -1)
return FALSE; // 拒絕載入至檔案總管
}
return _Module.DllMain(dwReason, 0);
}
EyeOnIE.rgs 加入 BHO 登錄
HKLM
{
SOFTWARE
{
Microsoft
{
Windows
{
CurrentVersion
{
Explorer
{
'Browser Helper Objects'
{
'{YOUR_CLSID_HERE}' // 替換為實際 CLSID
}
}
}
}
}
}
}
BhoPlugin.dll。regsvr32 BhoPlugin.dll 註冊。girl.com 的網址,應彈出封鎖訊息並取消導航。regsvr32 /u BhoPlugin.dll 或刪除登錄表對應 CLSID。IHTMLDocument2 注入腳本、修改頁面內容。IDeskBand、IInputObject 建立 Explorer Bar。IContextMenu 註冊 Shell Extension。DISPID_DOWNLOADBEGIN 等事件。BHO 是 IE 時代客製化瀏覽器的標準技術。雖現代瀏覽器已棄用 (Edge/Chrome 使用 Extension API),但在企業舊系統維護、IE Mode 相容場景仍具實用價值。掌握 COM、連接點、事件處理流程,可快速建構強大的瀏覽器增益集。