<!-- /* Font Definitions */ @font-face {font-family:"MS 明朝"; panose-1:2 2 6 9 4 2 5 8 3 4; mso-font-alt:"MS Mincho"; mso-font-charset:128; mso-generic-font-family:roman; mso-font-pitch:fixed; mso-font-signature:-1610612033 1757936891 16 0 131231 0;} @font-face {font-family:SimSun; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:宋体; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:Century; panose-1:2 4 6 3 5 7 5 2 3 3; mso-font-alt:"Times New Roman"; mso-font-charset:0; mso-generic-font-family:roman; mso-font-format:other; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} @font-face {font-family:"/@MS 明朝"; panose-1:2 2 6 9 4 2 5 8 3 4; mso-font-charset:128; mso-generic-font-family:roman; mso-font-pitch:fixed; mso-font-signature:-1610612033 1757936891 16 0 131231 0;} @font-face {font-family:"/@SimSun"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0mm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:12.0pt; font-family:Century; mso-fareast-font-family:"MS 明朝"; mso-bidi-font-family:"Times New Roman"; mso-font-kerning:1.0pt;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:595.3pt 841.9pt; margin:99.25pt 30.0mm 30.0mm 30.0mm; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:18.0pt;} div.Section1 {page:Section1;} -->
要包含的 头 文件
#include "stdafx.h"
#import <mshtml.tlb> // Internet Explorer 5
#import <shdocvw.dll>
#include "Shlwapi.h"
#pragma comment(lib,"Shlwapi.lib")
//--------------------
#include <mshtml.h>
#include <atlbase.h>
#include <oleacc.h>
//---------------------
下面是代 码 , 我是用 webbrowser 控件做和 浏览 器 ,CHTMLVIEW 其他也是可以的
实现 的功能是根据内容插入表 单 , 保存也是一 样 的道理 .
注 释 因 为 会乱 码 , 所以没加上 , 如果看不懂 , 可以看下面我从我博客上 复 制来的一篇文章
void CHelpDlg::OnButtonSubmit()
{
// TODO: Add your control notification handler code here
HWND hWnd = AfxGetMainWnd()->m_hWnd;
CoInitialize(NULL); // 初始化 COM
SHDocVw::IShellWindowsPtr m_spSHWinds;
if(m_spSHWinds.CreateInstance(__uuidof(SHDocVw::ShellWindows)) == S_OK)
{
IDispatchPtr spDisp;
long nCount = m_spSHWinds->GetCount();
for(long i =0;i<nCount;i++)
{
_variant_t va(i, VT_I4);
spDisp = m_spSHWinds->Item(va);
SHDocVw::IWebBrowser2Ptr spBrowser(spDisp);
if (spBrowser != NULL)
{
IDispatchPtr spDisp;
if(spBrowser->get_Document(&spDisp) == S_OK && spDisp!= 0 )
{
MSHTML::IHTMLDocument2Ptr spHtmlDocument(spDisp);
MSHTML::IHTMLElementPtr spHtmlElement;
if(spHtmlDocument==NULL)
continue;
spHtmlDocument->get_body(&spHtmlElement);
if(spHtmlDocument==NULL)
continue;
HRESULT hr;
MSHTML::IHTMLElementCollection* pColl=NULL;
hr=spHtmlDocument->get_all(&pColl);
if(pColl!=NULL&&SUCCEEDED(hr))
{
long lcount = 0;
pColl->get_length(&lcount);
for(int i=0;i<lcount;i++)
{
_variant_t index;
index.vt=VT_I4;
index.intVal=i;
IDispatchPtr disp;
disp=pColl->item(index,index);
if(disp==NULL)
hr=E_FAIL;
else
{
MSHTML::IHTMLInputElementPtr pInput(disp);
if(pInput)
{
BSTR bstrtype;
pInput->get_type(&bstrtype);
//printf(_bstr_t(bstrtype));
if(StrCmpW(bstrtype,L"text")==0)
{
MSHTML::IHTMLElementPtr e(pInput);// 以下是 关 于表 单 的 , 上面已 经 可以得到整个网 页 的内容了 , 把下面的表 单 部份改成你自已要 实现 的功能就可以了
// }
}
SysFreeString(bstrtype);
}
}
}
pColl->Release();
}
}
}
}
}
else
{
printf("Shell Windows interface is not avilable/n");
}
CoUninitialize();
}
下面是 讲 解
实现 和 IE 浏览 器交互的几 种 方法的介 绍
---- 1 .引言
---- 如何 实现对 IE 浏览 器中 对 象的操作是一个很有 实际 意 义问题 ,通 过 和 IE 绑 定的 DLL 我 们 可以 记录 IE 浏览过 的网 页 的 顺 序,分析用 户 的使用行 为 和模式。我 们 可以 对 网 页 的内容 进 行 过滤 和翻 译 ,可以自 动 填写网 页 中 经 常需要用 户 填写的 Form 内容等等 , 我 们 所有的例子代 码 都是通 过 VC 来表示的,采用的原理是通 过 和 IE 对 象的接口的交互来 实现对 IE 的 访问 。 实际 上是采用 COM 的技 术 ,我 们 知道 COM 是和 语 言无 关 的一 种 二 进 制 对 象交互的模式,所以 实际 上我 们 下面所描述 的内容都可以用其他的 语 言来 实现 ,比如 VB , DELPHI , C++ Builder 等等。
---- 2 . IE 实 例遍 历实现
---- 首先我 们 来看系 统 是如何知道当前有多少个 IE 的 实 例在运行。
---- 我 们 知道在 Windows 体系 结 构下,一个 应 用程序可以通 过 操作系 统 的运行 对 象表来和 这 些 应 用的 实 例 进 行交互。但是 IE 当前的 实现 机制是不在运行 对 象表中 进 行注册,所以需要采用其他的方法。我 们 知道可以通 过 ShellWindows 集合来代表属于 shell 的当前打 开 的窗口的集合,而 IE 就是属于 shell 的一个 应 用程序。
---- 下面我 们 描述一下用 VC 实现对 当前 IE 实 例的 进 行遍 历 的方法。 IShellWindows 是 关 于系 统 shell 的一个接口,我 们 可以定 义 一个如下的接口 变 量:
SHDocVw::IShellWindowsPtr m_spSHWinds;
然后 创 建 变 量的 实 例:
m_spSHWinds.CreateInstance
(__uuidof(SHDocVw::ShellWindows));
通 过 IShellWindows 接口的方法 GetCount
可以得到当前 实 例的数目:
long nCount = m_spSHWinds- >GetCount();
通 过 IShellWindows 接口的方法 Item
可以得到 每 一个 实 例 对 象
IDispatchPtr spDisp;
_variant_t va(i, VT_I4);
spDisp = m_spSHWinds->Item(va);
然后我 们 可以判断 实 例 对 象是不是
属于 IE 浏览 器 对 象,通 过 下面的 语 句 实现 :
SHDocVw::IWebBrowser2Ptr spBrowser(spDisp);
assert(spBrowser != NULL)
---- 在得到了 IE 浏览 器 对 象以后,我 们 可以 调 用 IWebBrowser2Ptr 接口的方法来得到当前的文档 对 象的指 针 : MSHTML::IHTMLDocument2Ptr spDoc(spBrowser->GetDocument());
---- 然后我 们 就可以通 过这 个接口 对这 个文档 对 象 进 行操作,比如通 过 Gettitle 得到文档的 标题 。
---- 我 们 在 浏览 网 络 的 时 候,一般 总 会同 时开 很多 IE 的 实 例,如果 这 些 页 面都是很好的 话 ,我 们 可能想保存在硬 盘 上, 这样 ,我 们 需要 对每 一个 实 例 进 行保存,而如果 我 们 采用上面的 原理,我 们 可以得到 每 一个 IE 的 实 例及其网 页对 象的接口, 这样 就可以通 过 一个 简单 的程序来批量的保存当前的所有打 开 的网 页 。采用上面介 绍 的 方法 实现 了 对 当前 IE 实 例的遍 历 ,但是我 们 希望得到 每 一个 IE 实 例所 产 生的事件, 这 就需要通 过 DLL 的机制来 实现 。
---- 3 .和 IE 相 绑 定的 DLL 的 实现
---- 我 们 介 绍 一下如何建立和 IE 进 行 绑 定的 DLL 的 实现 的 过 程。 为 了和 IE 的运行 实 例 进 行 绑 定,我 们 需要建立一个能 够 和 每 一个 IE 实 例 进 行 绑 定的 DLL 。 IE 的启 动过 程是 这样 的,当 每 一个 IE 的 实 例启 动 的 时 候,它都会在注册表中去 寻 找 这 个的一个 CLSID ,具体的注册表的 键 位置 为 :
HKEY_LOCALL_MACHINE/SOFTWARE/Microsoft/Windows
/CurrentVersion/Explorer/Browser Helper Objects
---- 当在 这 个 键 位置下存在 CLSIDs 的 时 候, IE 会通 过 使用 CoCreateInstance() 方法来 创 建列在 该键 位置下的 每 一个 对 象的 实 例。 注意 对 象的 CLSIDs 必 须 用子 键 而非名字 值 的形式表 现 ,比如 {DD41D66E-CE4F-11D2-8DA9-00A0249EABF4} 就是一个有效的子 键 。我 们 使用 DLL 的形式而非 EXE 的形式的原因是因 为 DLL 和 IE 实 例运行在同一个 进 程空 间 里面。 每 一个 这种 形式的 DLL 必 须实现 接口 IObjectWithSite ,其中方法 SetSite 必 须 被 实现 。通 过这 个方法,我 们 自己的 DLL 就可以得到一个指向 IE COM 对 象的 IUnknown 的指 针 , 实际 上通 过这 个指 针 我 们 就可以通 过 COM 对 象中的方法 QueryInterface 来遍 历 所有可以得到的接口, 这 是 COM 的基本的机制。当然我 们 需要的只是 IWebBrowser2 这 个接口。
---- 实际 上我 们 建立的是一个 COM 对 象, DLL 只不 过 是 COM 对 象的一 种 表 现 形式。我 们 建立的 COM 对 象需要建立和 实现 的方法有:
----1 . IOleObjectWithSite 接口的方法 SetSite 必 须实现 。 实际 上 IE 实 例通 过这 个方法向我 们 的 COM 对 象 传递 一个接口的指 针 。假 设 我 们 有一个接口指 针 的 变 量,不妨 设为 :
----CComQIPtr< IWebBrowser2, &IID_IWebBrowser2 > m_myWebBrowser2;
---- 我 们 就可以在方法 SetSite 中把 这 个 传进 来的接口指 针赋给 m_myWebBrowser2 。 2 . 在我 们 得到了指向 IE COM 对 象的接口后,我 们 需要把自己的 DLL 和 IE 实 例所 发 生的事件相 关连 , 为 了 实现这 个目的,需要介 绍 两个接口:
---- ( 1 ) IConnectionPointContainer 。 这 里使用 这 个接口的目的是用来根据它得到的 IID 来建立和 DLL 的一个特定的 连 接。比如我 们 可以 进 行如下的定 义 :
CComQIPtr< IConnectionPointContainer,
&IID_IConnectionPointContainer >
spCPContainer(m_myWebBrowser2);
---- 然后,我 们 需要把所有 IE 中 发 生的事件和我 们 的 DLL 进 行通 讯 ,可以使用 IConnectPoint 。
-- -- ( 2 ) IConnectPoint 。通 过这 个接口,客 户 可以 对连 接的 对 象 开 始或者是 终 止一个 advisory 循 环 。 IConnectPoint 有两个主要的方 法,一个 为 Advice ,另一个 为 Unadvise 。 对 于我 们 的 应 用来 说 , Advise 是用来在 每 一个 IE 发 生的事件和 DLL 之 间 建立一个通道。而 Unadvise 就是用来 终 止以前用 Advise 建立的通知 关 系。比如我 们 可以定 义 IConnectPoint 接口如下: CComPtr< IConnectionPoint > spConnectionPoint;
---- 然后,我 们 要使所有在 IE 实 例中 发 生的事件和我 们 的 DLL 相 关 ,可以使用 如下的方法:
hr = spCPContainer->FindConnectionPoint(
DIID_DWebBrowserEvents2, &spConnectionPoint);
---- 然后我 们 通 过 IConnectPoint 接口的方法 Advice 使 每 当 IE 有一个新的事件 发 生的 时 候,都能 够让 我 们 的 DLL 知道。可以用如下的 语 句 实现 :
hr = spConnectionPoint- >Advise(
(IDispatch*)this, &m_dwIDCode);
---- 在把 IE 实 例中的事件和我 们 的 DLL 之 间 建立 联 系以后,我 们 可以通 过 IDispatch 接口的 Invoke() 方法来 处 理所有的 IE 的事件。
-- --3 . IDispatch 接口的 Invoke() 方法。 IDispatch 是从 IUnknown 中 继 承的一个接口的 类 型,通 过 COM 接口提供的任何服 务 都可以通 过 IDispatch 接口来 实现 。 IDispatch::Invoke 的工作方式同 vtbl 幕后的工作方式是 类 似的, Invoke 将 实现 一 组 按索引来 访问 的函数,我 们 可以 对 Invoke 方法 进 行 动态 的定制以提供不同的服 务 。 Invoke 方法的表示如下:
STDMETHOD(Invoke)(DISPID dispidMember,REFIID
riid, LCID lcid, WORD wFlags,
DISPPARAMS * pdispparams, VARIANT * pvarResult,
EXCEPINFO * pexcepinfo, UINT * puArgErr);
-- -- 其中, DISPID 是一个 长 整数,它 标识 的是一个函数。 对 于 IDispatch 的某一个特定的 实现 , DISPID 都是唯一的。 IDispatch 的 每 一个 实现 都有其自己的 IID, 这 里 dispidMemeber 实际 上是可以 认为 是和 IE 实 例所 发 生的 每 一个事件相 关 的方法,比如: DISPID_BEFORENAVIGATE2 , DISPID_NAVIGATECOMPLETE2 等等。 这 个方法中另外一个比 较 重要的参数是 DISPPARAMS ,它的 结 构如下:
typedef struct tagDISPPARAMS
{
VARIANTARG* rgvarg;
//VARIANTARG 是同 VARAIANT 相同的,可以在
//OAIDL.IDL 中找到。所以 实际 上 rgvarg 是一个参数数
// 组
DISPID* rgdispidNameArgs; // 命名参数的 DISPID
unsigned int cArgs; // 表示数 组 中元素的个数
unsigned int CnameArgs; // 命名元素的个数
}DISPPARAMS
-- -- 要注意的是 每 一个参数的 类 型都是 VARIANTARG ,所以在 IE 和我 们 DLL 之 间 可以 传递 的参数 类 型的数目是有限的。只有那些能 够 被放到 VARIANTARG 结 构中的 类 型才可以通 过调 度接口 进 行 传递 。比如 对 于事件 DISPID_NAVIGATECOMPLETE2 来 说 :第一个参数表示 IE 在 访问 的 URL 的 值 , 类 型是 VT_BYREF|VT_VARIANT 。注意 DISPID_NAVIGATECOMPLETE2 等 DISPID 已 经 在 VC 中被定 义 ,我 们 可以直接 进 行使 用。如上 说 述,我 们 在方法 Invoke 中可以得到所有 IE 实 例所 发 生的事件 ,我 们 可以把 这 些数据放到文件中 进 行事后的分析,也可以放到一个列表框中 实时 的 显 示。
---- 4 .微 软 的 HTML 文档 对 象模型和 应 用分析
---- 下面我 们 来看如何得到网 页 文档的接口:网 页 文档的接口 为 IHTMLDocument2 ,可以通 过调 用 IE COM 对 象的 get_Document 方法来得到网 页 的接口。使用如下的 语 句:
hr = m_spWebBrowser2- >get_Document(&spDisp);
CComQIPtr< IHTMLDocument2,
&IID_IHTMLDocument2 > spHTML;
spHTML = spDisp;
---- 这样 我 们 就得到了网 页对 象的接口,然后我 们 就可以 对 网 页进 行分析,比如通 过 IHTMLDocument2 提供的方法 get_URL 我 们 可以得到和 该 网 页 相 关 的 URL 的地址 值 ,通 过 get_forms 方法可以 该 网 页 中所有的 Form 对 象的集合。 实际 上 W3C 组织 已 经 制定了一个 DOM ( Document Objec Model ) 标 准,当然 这 个 标 准不 仅仅 是 针对 HTML ,同 时还 是 针对 XML 制定的。 W3C 组织 只是定 义 了网 页对 象的接口,不同的公司可以采用不同的 语 言和 方法 进 行具体的 实现 。按照 W3C 组织 定 义 的网 页对 象被 认为 是 动态 的,即用 户 可以 动态 的 对 网 页对 象里面所包含的 每 一个 对 象 进 行操作。 这 里的 对 象可以是指一个 输 入框,也可以是 图 象和声音等 对 象。同 时 按照 W3C 的正式文档的 说 明,网 页对 象是可以 动态 增加和 删 除的。事 实 上,很少有厂商 实现 了 DOM 定 义 的所有功能。 微 软对 网 页对 象的定 义 也基本上是按照 这 个 标 准 实现 的。但是当前的接口 还 不支持 动态 的增加和 删 除元素,但是可以 对 网 页 中的基本元素 进 行属性的修改。比如 IHTMLElementCollection 表示网 页 中一些基本的元素的集合, IHTMLElement 表示网 页 中的一个基本的元素。而象 IHTMLOptionElement 接口就表示一个特定的元素 Option 。基本的元素都有 setAttribute 和 geAttribute 方法来 动 态 的 设 置和得到元素的名称和 值 。
---- 较为 常 见 的一个 应 用是我 们 能 够 分析网 页 中是否有需要填写的 Forms ,如果 这 个网址的 Forms 以前已 经 填写 过 而且数据我 们 已 经 保存下来的 话 ,我 们 就可以 把数据自 动 放到和 该 URL 下的 Forms 的相 关 的位置中去。另外,我 们 可以 总结 网 页 上需要填写的 Form 的数据 项 ,先 对这 些数据 项进 行 赋值 ,以后碰到有相 同的数据 项 的 时 候就自 动 把我 们赋值 的内容填写 进 去。 实际 上 Form 是 对 象, Form 中包含的元素,比如 INPUT , OPTION , SELECT 等 类 型的 输 入元素都是 对 象。
---- 另外一个可以想到的 应 用是自 动对 网 页 中的文本 进 行翻 译 ,因 为 我 们 可以修改网 页 中任何 对 象的属性,所以我 们 可以把里面不属于本国 语 言的部分自 动 翻 译 成本国 语 言,当然真正的 实现还 要靠自然 语 言理解方面技 术 的突破,但是 IE 浏览 器的接口和 对 象的形式使我 们 能 够 灵活的控制整个 IE ,无 论 是从事件 对 象 还 是到网 页对 象。
---- 5 .小 结
---- 上面我 们 分析了如何得到所有 IE 的 实 例,同 时 介 绍 了和 IE 实 例相捆 绑 的 DLL 的 详细 的 实现 机制,同 时对 网 页 的 对 象化 进 行了分析。并且介 绍 了几个相 关 的 应 用 和 实现 的方法及存在的技 术问题 。 IE 是一个 组 件化的以 COM 为 基 础 的 浏览 器,它具有 强 大的功能,同 时为应 用 开发 者留下了广 阔 的空 间 ,当然它也存在体 积 比 较 大,速度相 对 比 较 慢的缺点。但是它的体系 结 构代表了微 软 先 进 的 创 新的技 术 ,因此具有 强 大的生命力 。