使用 C++ BHO 将 HTML 内容写入网页的 DOM






4.78/5 (9投票s)
如何通过 C++ BHO 将您自己的 HTML 内容写入任何网页
引言
本文将涵盖在 Internet Explorer 中创建插件的整个过程。该插件将在浏览器打开的每个页面中放置一个广告。该广告将使用 HTML/CSS 制作,并在页面加载时写入页面的 DOM。使用 C++ 的目标是移除 .NET 依赖,以便覆盖更广泛的受众。 当我的 C# 插件安装在 4 万台电脑上,而其中一些电脑没有安装 .NET 时,这对我来说很有意义。 我在花了很多时间在网上搜索几乎找不到任何信息后,决定写这篇文章。 我希望这对您有所帮助。 此示例的最终目标是编写一个带有绝对定位和背景图像的 <div>
,以及另一个充当关闭按钮的 <div>
。 因此,也需要插入一些 JavaScript 代码。
编写 Internet Explorer 插件的唯一方法是通过浏览器助手对象(也称为 BHO)。它们是充当 Internet Explorer 插件的 COM 组件。BHO 可用于在任何程度上自定义 Internet Explorer:从用户界面修改到 Web 过滤器到下载管理器。
本文主要基于 这篇文章。我强烈建议阅读它,以便详细了解本文。诸如 BHO 安装之类的课题在这里没有涉及。
Using the Code
示例中的大部分代码都在参考文章中进行了说明。唯一真正重要的部分是 CEventSink
类。这个类是捕获浏览器触发的所有事件的类。我想捕获的事件是 OnDocumentComplete
,因为我将在页面加载时将 HTML 内容写入 DOM。这是捕获事件的函数。
// This is called by Internet Explorer to notify us of events
// Full documentation about all the events supported by DWebBrowserEvents2
// can be found at http://msdn.microsoft.com/en-us/library/aa768283(VS.85).aspx
STDMETHODIMP CEventSink::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,
WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,
EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(wFlags);
UNREFERENCED_PARAMETER(pVarResult);
UNREFERENCED_PARAMETER(pExcepInfo);
UNREFERENCED_PARAMETER(puArgErr);
VARIANT v[5]; // Used to hold converted event parameters before passing them
// onto the event handling method
int n;
bool b;
PVOID pv;
LONG lbound,ubound,sz;
if(!IsEqualIID(riid,IID_NULL)) return DISP_E_UNKNOWNINTERFACE; // riid should
// always be IID_NULL
// Initialize the variants
for(n=0;n<5;n++) VariantInit(&v[n]);
// This event is not used in this code sample but is used for informative purposes
if(dispIdMember==DISPID_BEFORENAVIGATE2) { // Handle the BeforeNavigate2 event
VariantChangeType(&v[0],&pDispParams->rgvarg[5],0,VT_BSTR); // URL
VariantChangeType(&v[1],&pDispParams->rgvarg[4],0,VT_I4); // Flags
VariantChangeType(&v[2],&pDispParams->rgvarg[3],0,VT_BSTR); // TargetFrameName
VariantChangeType(&v[3],&pDispParams->rgvarg[2],0,VT_UI1|VT_ARRAY); // PostData
VariantChangeType(&v[4],&pDispParams->rgvarg[1],0,VT_BSTR); // Headers
if(v[3].vt!=VT_EMPTY) {
SafeArrayGetLBound(v[3].parray,0,&lbound);
SafeArrayGetUBound(v[3].parray,0,&ubound);
sz=ubound-lbound+1;
SafeArrayAccessData(v[3].parray,&pv);
} else {
sz=0;
pv=NULL;
}
b=Event_BeforeNavigate2((LPOLESTR)v[0].bstrVal,v[1].lVal,
(LPOLESTR)v[2].bstrVal,(PUCHAR)pv,sz,(LPOLESTR)v[4].bstrVal,
((*(pDispParams->rgvarg[0].pboolVal))!=VARIANT_FALSE));
if(v[3].vt!=VT_EMPTY) SafeArrayUnaccessData(v[3].parray);
if(b) *(pDispParams->rgvarg[0].pboolVal)=VARIANT_TRUE;
else *(pDispParams->rgvarg[0].pboolVal)=VARIANT_FALSE;
}
//This is where the event is captured,
//note I only need the second parameter which is the loaded URL
if(dispIdMember==DISPID_DOCUMENTCOMPLETE){
VariantChangeType(&v[0],&pDispParams->rgvarg[0],0,VT_BSTR); // URL
OnDocumentComplete(NULL,(BSTR)v[0].bstrVal);
}
// Free the variants
for(n=0;n<5;n++) VariantClear(&v[n]);
return S_OK;
}
到目前为止,我已经成功捕获了 OnDocumentComplete
事件。现在“魔术”在 OnDocumentComplete
函数中执行。这是代码
void STDMETHODCALLTYPE CEventSink::OnDocumentComplete(IDispatch *pDisp, BSTR url)
{
if(UrlSite != NULL){
BSTR result = wcsstr(UrlSite, url);
if(result != NULL)
return;
}
HRESULT hr = S_OK;
IHTMLDocument2 *pDocument;
pSite->get_Document((IDispatch**)&pDocument);
// This is the only way to make JavaScript work after the document is loaded
CComQIPtr<IHTMLWindow2> pWindow;
pDocument->get_parentWindow(&pWindow);
BSTR bstrT1 = NULL;
bstrT1 = SysAllocString (L"function hidediv()
{document.getElementById(\"absoluteID123\").style.visibility = \"hidden\";}");
BSTR bstrT2 = NULL;
bstrT2 = SysAllocString (L"javascript");
CComVariant v;
hr = pWindow->execScript(bstrT1, bstrT2, &v);
IHTMLElement *pElementBody;
pDocument->get_body((IHTMLElement**)&pElementBody);
if(CheckInsert(pElementBody))
return;
BSTR bstrBegin = NULL;
bstrBegin = SysAllocString ( L"afterBegin" );
/* this is the code inserted on the page
<div id="absoluteID123" style="float:right;padding: 20px;z-index:2000000">
<table><tr><td>
<div id="div123456" style="z-index:2000002;width:307px;height:158px;
background-image: url
(http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif);"/>
<div id="divButton123" onclick="alert()" style="z-index:2000003;
position: relative; left: 279px;top: 6px; width:23px;height:24px;"/></div>
</div>
</td></tr></table></div>
*/
BSTR bstrL2 = NULL;
bstrL2 = SysAllocString ( L"<div id=\"absoluteID123\" style=\"position:absolute;
top:0;right:0;padding: 20px;z-index:2000000\">" );
BSTR bstrL3 = NULL;
bstrL3 = SysAllocString ( L"<table><tr><td>");
BSTR bstrL4 = NULL;
bstrL4 = SysAllocString ( L"<div id=\"div123456\" style=\"z-index:2000002;
width:307px;height:158px;background-image:
url(http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif);\"/>");
BSTR bstrL5 = NULL;
bstrL5 = SysAllocString ( L"<div id=\"divButton123\" onclick=\"hidediv()\"
style=\"z-index:2000003; position: relative;
left: 279px;top: 6px; width:23px;height:24px;\"/>
</div></div>");
BSTR bstrL6 = NULL;
bstrL6 = SysAllocString ( L"</td></tr></table></div>");
BSTR bstrFinal = NULL;
bstrFinal = Concat(bstrFinal,bstrL2);
bstrFinal = Concat(bstrFinal,bstrL3);
bstrFinal = Concat(bstrFinal,bstrL4);
bstrFinal = Concat(bstrFinal,bstrL5);
bstrFinal = Concat(bstrFinal,bstrL6);
SysFreeString(bstrL2);
SysFreeString(bstrL3);
SysFreeString(bstrL4);
SysFreeString(bstrL5);
pElementBody->insertAdjacentHTML(bstrBegin, bstrFinal);
SysFreeString(bstrBegin);
SysFreeString(bstrFinal);
UrlSite = Concat(UrlSite,url);
}
关于这个函数,我想解释一些细节。首先,我添加了 JavaScript 代码和 HTML 代码,并将 defer 属性放在 script
标签上。 当我发现它在高于 Internet Explorer 8 的版本中不起作用时,我不得不更改它。 另一个细节是,在某些使用框架的页面中,OnDocumentComplete
函数会被触发多次,因此您需要一种方法来判断是否已经插入了插件,示例中的 CheckInsert
函数就是这样做的。
关注点
我在研究使用 C++ BHO 移除浏览器中图像的 MSDN 示例时感到非常沮丧。 我不明白为什么他们没有提供可下载的示例,并且花费了大量时间将代码与本文所基于的文章集成。 我还使用 NSIS 制作了一个不错的安装程序。 如果您需要它,请在下面的评论中提出要求。
历史
- 2014 年 7 月 18 日:第一个版本