65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (9投票s)

2014年7月16日

CPOL

3分钟阅读

viewsIcon

37974

downloadIcon

1333

如何通过 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 日:第一个版本
© . All rights reserved.