IE浏览器内容过滤插件的设计与实现.doc
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
具体的实现过程:
MIME过滤器:理解这个是实现BHO过滤功能的关键。
MIME类型是指某种扩展名的文件用一种应用程序来打开的方式类型,在文件系统中通俗的讲,就是某种具体的软件与某种扩展名对应,在打开这种扩展名文件就会去打开这种软件,只不过这种情况是使用在Internet条件下。
我们知道一个网页面中有很多很多的文件类型,每种文件都关联着一种对应软件来打开它,也就是有很多的MIME类型,所以MIME过滤器就是实现在打开文件时,去加载对应的组件,此时在组件与浏览器交互的过程中,我们就可以实现对浏览器文本的操作,通常情况下MIME过滤器实现了IinternetProtocol,IinternetProtocolSink,其中接口IinternetProtocolSink发挥着连接器的作用。
WEB处理器:其本质上就是一个名字叫做urlmon.dll的组件,浏览器在解析并下载页面时,都要使用相关组件提供的功能,该组件实现了IinternetProtocol与IinternetProtocolSink接口。
MIME过滤器与WEB处理器之间的交互过程:
1.WEB处理器在处理指定MIME类型的文件时,就会去加载对应的组件,并
且创建组件的实例,并且调用组件IinternetProtocolRoot的start()的方
法,传递相关的指针,此处IinternetProtocol继承自IinternetProtocolRoot接口。
2.WEB处理器调用MIME过滤器的IinternetProtocoSink的ReportProcess()与
IinternertProtocolSink的ReportData()方法。
3.MIME过滤器调用WEB处理器的IinternetProtocol的Read方法,来获得相应
的数据。
4.MIME过滤器调用WEB处理器的IinternetProtocolSink的ReportData()方法。
5.WEB处理器调用MIME过滤器的IinternetProtocol的Read()方法。
网络数据
下面介绍一下各个接口的函数信息:
class IInternetProtocolRoot
{
STDMETHOD(Start)(
LPCWSTR szUrl,
IInternetProtocolSink *pIProtSink,
IInternetBindInfo *pIBindInfo,
DWORD grfSTI,
DWORD dwReserved)=0;
STDMETHOD(Continue)(PROTOCOLDATA *pStateInfo)=0;
STDMETHOD(Abort)(HRESULT hrReason,DWORD dwOptions)=0;
STDMETHOD(Term inate)(DWORD dwOptions)=0;
STDMETHOD(Suspend)()=0;
STDMETHOD(Resum e)()=0;
}
功能介绍:该接口主要是对MIME过滤器的操控
1.start函数的是MIME过滤器与WEB处理器交互的开始,主要是为了让组件获得处理器
的相关接口指针。
2.Continue方法允许MIME处理器在套件线程内继续处理数据,这个方法主要是在
switch之后调用。
3.Abort方法中止当前的操作过程。
4.Term inate方法释放处理器所申请的所有资源
5.Suspend目前没有实现
6.Resum e方法目前没有实现
class IInternetProtocol
{
STDMETHOD(Read)(void *pv,ULONG cb,ULONG *pcbRead)=0;
STDMETHOD(Seek)(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER *plibNewPosition)=0;
STDMETHOD(LockRequest)(DWORD dwOptions)=0;
STDMETHOD(UnlockRequest)()=0;
}
功能介绍:该接口主要是用于数据的读取,以及操作的控制
1.Read方法主要用于数据的读取,MIME过滤器与WEB处理器的数据读取。
2.Seek方法主要是用于移动当前的流指针。
3.LockRequest主要是将请求过程进行锁定。
4.UnlockRequest主要是释放请求过程,与上面的函数是对应的。
class IinternetProtocolSink
{
STDMETHOD(Switch)(PROTOCOLDATA __RPC_FAR *pProtocolData)=0;
STDMETHOD(ReportProgress)(ULONG ulStatusCode, LPCWSTR szStatusTex t)=0;
STDMETHOD(ReportData)(DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax)=0;
STDMETHOD(ReportResult)(HRESULT hrResul t, DWORD dwError, LPCWSTR szResult)=0;
}
功能简介: 主要是实现接收器功能。
1.Switch方法主要是实现在工作线程与套件线程的转换。
2.ReportProgress方法是为了给出缓冲文件夹的名字
3.ReportData方法反应当前的下载进度
4.ReportResult方法主要是为了反映当前的操作结构
其中的使用到的结构体:
typedef struct _tagPROTOCOLFILTERDA TA {
DWORD cbSize;//该结构体的大小
IInternetProtocolSink *pProtocolSink;//WEB处理器的接口
IInternetProtocol *pProtocol;//WEB处理器的接口
IUnknown *pUnk;
DWORD dwFilterFlags;//操作参数
} PROTOCOLFILTERDA TA;
下面讲述具体的代码实现:
用A TL实现的基本步骤:
1.创建一个组件,继承IinternetProtocolSink, IInternetProtocol接口,并定义基本的
接口成员函数。
class A TL_NO_VTABLE CHttpContentFilterPP :
public CCom ObjectRootEx<CCom SingleThreadModel>,
public CCom CoClass<CHttpContentFilterPP, &CLSID_HttpContentFilterPP>,
public IInternetProtocol,//定义基本接口
public IinternetProtocolSink//定义接收器
{……}
BEGIN_COM_MAP(CHttpContentFilterPP)
COM_INTERFACE_EN TRY_IID(IID_IInternetProtocol, IInternetProtocol)
COM_INTERFACE_EN TRY_IID(IID_IInternetProtocolRoot, IInternetProtocol)
COM_INTERFACE_EN TRY_IID(IID_IInternetProtocolSink, IInternetProtocolSink) END_COM_MAP()
//具体函数的功能,参看上面的定义:
STDMETHOD(Start)(
LPCWSTR szUrl,
IInternetProtocolSink *pIProtSink,
IInternetBindInfo *pIBindInfo,
DWORD grfSTI,
DWORD dwReserved);
STDMETHOD(Continue)(PROTOCOLDATA *pStateInfo);
STDMETHOD(Abort)(HRESULT hrReason,DWORD dwOptions);
STDMETHOD(Term inate)(DWORD dwOptions);
STDMETHOD(Suspend)();
STDMETHOD(Resum e)();
STDMETHOD(Read)(void *pv,ULONG cb,ULONG *pcbRead);
STDMETHOD(Seek)(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER *plibNewPosition);
STDMETHOD(LockRequest)(DWORD dwOptions);
STDMETHOD(UnlockRequest)();
// IInternetProtocolSink interface
public:
STDMETHOD(Switch)(PROTOCOLDATA __RPC_FAR *pProtocolData);
STDMETHOD(ReportProgress)(ULONG ulStatusCode, LPCWSTR szStatusTex t);
STDMETHOD(ReportData)(DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax);
STDMETHOD(ReportResult)(HRESULT hrResult, DWORD dwError, LPCWSTR szResult);
2.当有匹配的MIME类型时,就要加载指定的组件,并调用组件的IInternetProtocolRoot
接口的start方法,并传递WEB处理器的连接点指针,保存下来。
STDMETHODIMP CH ttpContentFilterPP::Start(
LPCWSTR szUrl,
IInternetProtocolSink *pIProtSink,
IInternetBindInfo *pIBindInfo,
DWORD grfSTI,
DWORD dwReserved)
{
USES_CONVERSION;
ATL TRACE(_T("Start - url=%s, sti=%x\n"),OLE2T((LPWSTR)szUrl), grfSTI);
static BOOL bLoaded=FALSE;
if (!bLoaded)
{
LoadKeyW ords();//这个函数是为了加载在数据库中定义的单词。
bLoaded=TRUE;//显示加载成功。
}
HRESULT hr = E_FAIL;
m_grfSTI = grfSTI; //浏览器显示网页的模式
m_bIncom ingIsUnicode = false;//用来判断是否是uni code编码模式
m_bSplitUniWord = false;
if (!(grfSTI & PI_FIL TER_MODE))//是否支持过滤模式
{
hr = E_INVALIDARG;
}
else
{
// get the protocol pointer from reserved pointer
PROTOCOLFIL TERDATA* ProtFiltData =(PROTOCOLFIL TERDATA*) dwReserved;
_ASSER TE(NULL == m_pIncom ingProt);
if (NULL == ProtFiltData->pProtocol)
{
// !! We can't do anything wi thout an interface to read from
_ASSER TE(false);
return E_INVALIDARG;
}
m_pIncom ingProt = ProtFiltData->pProtocol;//保存Iinterprotol协议接口
m_pIncom ingProt->AddRef();//添加引用
// hold onto the sink as well
_ASSER TE(NULL == m_pOutgoingProtSink);
m_pOutgoingProtSink = pIProtSink;//保存WEB处理器的接收器接口
m_pOutgoingProtSink->AddRef();//添加引用
m_bFirstRead = true;//是否是第一次读这个数据
m_pHTMLBuffer = new CStringDataBuffer(2048);//定义数据的大小
hr = S_OK;
}
return hr;
}
3.由前面的定义可知,最终的WEB处理器要调用MIME过滤器的Read方法,所以在这个函数中
我们可以先调用WEB处理器的Read,获取有关数据,在根据具体的过滤条件,对获取的数据
进行处理,下面我们来看看具体的Read方法的实现方式。
本处选取其中的片段作为讲解:
STDMETHODIMP CHttpContentFilterPP::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
ATL TRACE(_T("IIP::Read - pv = 0x%x, cb = %d\n"), pv, cb);
HRESULT hr = S_OK;
HRESULT hrFrom Prot;
BYTE* pbuff = g_buffer;
ULONG cbReadFrom Prot;
DWORD dwIsUnicode;
ULONG cbUniCheck;
if (!m_pHTMLBuffer->IsDone())//buffer还没有完成
{
// If there was a split unicode word on the last read from the prot handler, fix it by
// sticking the last byte onto the front of this read
if (m_bIncom ingIsUnicode && m_bSplitUniWord)//如果当前的网页内容是uni code 编码,并且单词处于分割状态,就是一个unicode字符处于分割状态,那么就需要填充{
pbuff[0] = m_byteSplit;//那么就需要将上次的最后面一个字符赋值
pbuff++;//向后面移动一个字节
m_bSplitUniWord = false;//当前的单词并没有处于分割状态,那么就不用设置}
// Retrieve Requested Am ount of Data from Protocol Handler
hrFrom Prot =m_pIncom ingProt->Read((void*)pbuff, BUFFER_READ_SIZE, &cbReadFrom Prot);//从WEB处理器获取相关的数据
if (E_PENDING != hrFrom Prot) // We m ay have som e extra data in our buffer, so continue
{
if (FAILED(hrFrom Prot))
return hrFrom Prot;
else
{
// Parse Data into String Data Buffer
if (cbReadFrom Prot > 0)
{
if (m_bFirstRead)
{
if (cbReadFrom Prot >= 2 && pbuff[0] == 0xFF && pbuff[1] == 0xFE)//判断是不是unicode编码的方式,至少有两个字节
{
m_bIncom ingIsUnicode = true;//是unicode编码,向后移动两个字节
pbuff += 2;
}
else
{
if (cbReadFrom Prot & 0x1)//判断是不是有奇数个字节
cbUniCheck = cbReadFrom Prot - 1;
else
cbUniCheck = cbReadFrom Prot;//从而确定具体的长度
dwIsUnicode = IsTextUnicode(pbuff, cbUniCheck, NULL);
if (0 != dwIsUnicode)
m_bIncom ingIsUnicode = true;
}
Htm lStart();
m_bFirstRead = false;
}
hr = Ht m lFilter(pbuff, cbReadFrom Prot);//完成具体的解析过程
if (FAILED(hr))
return hr;
}
// Prot Handler will let us know when there is no m ore data left
if (S_FALSE == hrFrom Prot)
{
Htm lEnd();
m_pHTMLBuffer->Done();
}
}
}
}
m_pOutgoingProtSink->ReportProgress(BINDSTA TUS_VERIFIEDMIMETYPEAVAILA BLE, CONTEN T_TYPE);
hr = m_pHTMLBuffer->ReadData(pv, cb, pcbRead);
return hr;
}
这里我们要着重讲述一下关于H tm lFilter这个方法的作用,
HRESULT CHttpContentFilterPP::H tm lFilter(BYTE* pInBuff, ULONG cbBufferSize)
{
ATL TRACE(_T("Htm lFilter(BYTE* pInBuff, ULONG cbBufferSize)\n"));
USES_CONVERSION;
HRESULT hr = E_FAIL;
if (NULL == pInBuff)
{
_ASSER TE(false);
return E_INVALIDARG;
}
TCHAR* pbuff = (TCHAR*)pInBuff;
ULONG cchBuffer = cbBufferSize;
// Conversion from whatever is in buffer (we don't know if it is uni or not) to TCHAR
// TODO: Obviously our conversion of data here is very sim plistic - coding an im provem ent
// over A2T and W2T conversion routines of the data would be a ni ce exercise for the // exuberant MIME filter developer
if (m_bIncom ingIsUnicode)//如果是uni code编码怎么处理
{
// Prot Handler split last uni code word in m iddle
if (cbBufferSize & 0x1)//如果有奇数个字节,那么就要减去字节的数目
{
m_bSplitUniWord = true;//要分割
m_byteSplit = pInBuff[cbBufferSize-1];//保存分割字符
cchBuffer--;
pInBuff[cbBufferSize-1] = 0;//重新设置长度
pInBuff[cbBufferSize] = 0;
}
else
{
pInBuff[cbBufferSize] = 0;
pInBuff[cbBufferSize+1] = 0;
}
//注意Unicode字符串,末尾有两个零
cchBuffer = cchBuffer / sizeof(TCHAR);
pbuff = W2T((LPWSTR)pbuff);
}
else
{
pInBuff[cbBufferSize] = 0;
pbuff = A2T((LPSTR)pbuff);
}
CString csH tm lFind=pbuff;
BOOL bIsKeyword=FALSE;
CString csKeyWord;
int nIndex=0;
for (;nIndex<g_KeyWordsArray.GetSize();nIndex++)//然后就是具体的遍历比较过程{
csKeyW ord=g_KeyWordsArray[nIndex];
if(bIsKeyword=(csH tm lFind.Find(csKeyWord)>=0))
{
break;
}
}
if (bIsKeyword)//如果是最终的关键字,那么酒吧当前的提示信息显示给终端用户看
{
CString csH tm l=_T("");
csHtm l+=_T("<html>\r\n");
csHtm l+=_T("\r\n");
csHtm l+=_T("<head>\r\n");
csHtm l+=_T("<m eta http-equiv=\"Content-Type\" content=\"text/htm l; charset=gb2312\" />\r\n");
csHtm l+=_T("<title>马槊提示您:您访问的站点中包含不被允许的内容</title>\r\n");
csHtm l+=_T("</head>\r\n");
csHtm l+=_T("\r\n");
csHtm l+=_T("<body>\r\n");
csHtm l+=_T("\r\n");
csHtm l+=_T("<span style=\"font-size='10.5pt'\">您访问的站点中包含不被允许的内容:</span><br/>\r\n");
csHtm l+=_T("<hr color=\"#FF0000\" size=\"1\" noshade />\r\n");
csHtm l+=_T("<span style=\"font-size='10.5pt'\">");
csHtm l+=csKeyW ord;
csHtm l+=_T("</span>");
csHtm l+=_T("\r\n");
csHtm l+=_T("</body>\r\n");
csHtm l+=_T("\r\n");
csHtm l+=_T("</html>\r\n");
m_pHTMLBuffer->WriteString(csH tm l.GetBuffer(csH tm l.GetLength()));
m_pHTMLBuffer->Done();
csHtm l.ReleaseBuffer();
}
Else//如果不是那么就继续加载相关的网页,然后将所有的内容都返回。
{
m_pHTMLBuffer->WriteString(pbuff);
}
return S_OK;。