♀ 获取文档的总高度和宽度以及页面当前显示的文档位置
以下代码用于获取页面当前显示的文档位置,如需获取文档的总高度和宽度,只需将函数get_scrollLeft()和get_scrollTop()换为get_scrollWidth()和get_scrollHeight()即可(注意,获取高度和宽度只有当收到WebBrowser的DocumentComplete事件后才有效)。其中m_pUiWeb为指向WebBrowser控件的指针。
因为在WebBrowser中右键菜单是由Docment对象管理的,所以要屏蔽或者修改右键菜单,必须从Docment对象入手,WebBrowser向我们提供了IDocHostUIHandler接口以完成对Docment对象UI的相关操作,在该接口包含有一个ShowContextMenu()函数,通过对该函数的重载我们就可完成屏蔽或者修改右键菜单的任务。那么,我们只剩下了如何将Docment对象原有的UIHandler替换为我们自定义的UIHandler的任务,请参考下面的例子。其中,CDocHostUIHandler为自定义的UIHandler类。
♀ 加载内存中的页面(来自网络,未验证)
或者因为网页保密的考虑;或者因为软件分发的考虑,有的时候就需要让IE或IE浏览器控件显示内存或资源中的HTML网页。在 MFC 中,CHtmlView::LoadFromResource() 可以显示程序资源中的 HTML 内容。我们都知道MFC的CHtmlView其实是对IWebBrowser2的一个包装,但是在 IWebBrowser2 中却没有类似的方法。那么它是如何实现的呢?步骤如下:
1、首先通过IWebBrowser2::Navigate2()显示一个网页,其目的是产生有效的对象,从而得到IHTMLDocument2接口;
2、IWebBrowser2::get_Document() 得到 IHTMLDocument2 接口指针;
3、IHTMLDocument2::QueryInterface() 得到 IPersistStreamInit 接口指针;
4、IPersistStreamInit::InitNew() 初始化接口对象;
5、IPersistStreamInit::Load() 装载内存中的 HTML 数据流(IStream *);
内存指针转换为流的方法是:
I、 GlobalAlloc() 申请内存;
II、 复制 HTML 字符串内容到上述的内存中;
III、CreateStreamFromHGlobal() 转换内存为 IStream 指针;
原理性代码如下:
把 HTML 文件加入到程序资源的方法比较简单,在资源卡片中,鼠标右键弹出菜单,执行 Import...(引入),选择指定的 HTML 文件,然后给一个资源名称即可。(在这里,最方便的资源名称用字符串比较好,如果使用整数ID,那么将来在使用的时候是这样的格式:res://文件名/#101,这里假设 101 是资源的ID号。真麻烦!我不太喜欢这样的方式。)对于图片文件等其它的附件,则需要手工编辑资源 RC 文件(用 IDE 环境引入,它会试图用文本方式打开一个2进制文件,多数情况下会“死机”)。
手工编辑 RC 文件的部分是:
WebBrowser的事件主要包括Control事件、Window事件及Docment事件三大类,微软分别提供了三个虚拟Event结构表示相关事件,分别是:DWebBrowserEvents、HTMLWindowEvents、HTMLDocumentEvents。使用时只需继承、实现相关Event结构,并安装相关事件即可。以下代码演示了如何继承、实现Docment事件。
用VC在网页上绘图主要通过WebBrowser的IElementBehavior接口和IHTMLPainter接口来实现。具体内容参照MSDN中的《Using Rendering Behaviors》说明,该说明中也有相关演示代码。
以下代码用于获取页面当前显示的文档位置,如需获取文档的总高度和宽度,只需将函数get_scrollLeft()和get_scrollTop()换为get_scrollWidth()和get_scrollHeight()即可(注意,获取高度和宽度只有当收到WebBrowser的DocumentComplete事件后才有效)。其中m_pUiWeb为指向WebBrowser控件的指针。
BOOL GetDocPos(LONG &nPosX, LONG &nPosY)
{
nPosX = 0;
nPosY = 0;
IHTMLDocument2 *pWebDoc = (IHTMLDocument2*)m_pUiWeb->get_Document();
IHTMLDocument3 *pWebDoc3 = NULL;
HRESULT hRes = pWebDoc->QueryInterface(IID_IHTMLDocument3,
(void**)&pWebDoc3);
if( FAILED(hRes) || NULL == pWebDoc3 )
{
return FALSE;
}
IHTMLElement *pHtmlBody = NULL;
hRes = pWebDoc3->get_documentElement(&pHtmlBody);
if( FAILED(hRes) || NULL == pHtmlBody )
{
return FALSE;
}
IHTMLElement2 *pHtmlElement = NULL;
hRes = pHtmlBody->QueryInterface(IID_IHTMLElement2, (void**)&pHtmlElement);
if( FAILED(hRes) || NULL == pHtmlElement )
{
return FALSE;
}
hRes = pHtmlElement->get_scrollLeft(&nPosX);
if( FAILED(hRes) )
{
nPosX = 0;
return FALSE;
}
hRes = pHtmlElement->get_scrollTop(&nPosY);
if( FAILED(hRes) )
{
nPosX = 0;
nPosY = 0;
return FALSE;
}
return TRUE;
}
♀ 屏蔽或者修改鼠标右键菜单
因为在WebBrowser中右键菜单是由Docment对象管理的,所以要屏蔽或者修改右键菜单,必须从Docment对象入手,WebBrowser向我们提供了IDocHostUIHandler接口以完成对Docment对象UI的相关操作,在该接口包含有一个ShowContextMenu()函数,通过对该函数的重载我们就可完成屏蔽或者修改右键菜单的任务。那么,我们只剩下了如何将Docment对象原有的UIHandler替换为我们自定义的UIHandler的任务,请参考下面的例子。其中,CDocHostUIHandler为自定义的UIHandler类。
void NavigateComplete2(LPDISPATCH pDisp, VARIANT* URL)
{
// 判断事件通知对象
LPUNKNOWN pUnknown = m_pUiWeb->GetControlUnknown();
if( NULL == pUnknown )
{
return;
}
LPUNKNOWN pUnknownWeb = NULL;
LPUNKNOWN pUnknownDisp = NULL;
HRESULT hr = pUnknown->QueryInterface(IID_IUnknown, (LPVOID*)&pUnknownWeb);
if( FAILED(hr) )
{
return;
}
hr = pDisp->QueryInterface(IID_IUnknown, (LPVOID*)&pUnknownDisp);
if( FAILED(hr) || pUnknownWeb != pUnknownDisp)
{
pUnknownWeb->Release();
return;
}
pUnknownWeb->Release();
pUnknownDisp->Release();
// 修改Document对象
if( NULL == m_pDocHostUiHandler )
{
m_pDocHostUiHandler = new CDocHostUIHandler();
}
IHTMLDocument2 *pHtmlDoc2 = (IHTMLDocument2*)m_pUiWeb->get_Document();
ICustomDoc *pCustomDoc = NULL;
hr = pHtmlDoc2->QueryInterface(IID_ICustomDoc,
reinterpret_cast(&pCustomDoc));
if( FAILED(hr) )
{
return;
}
pCustomDoc->SetUIHandler(m_pDocHostUiHandler);
pCustomDoc->Release();
}
以下代码演示了如何屏蔽右键菜单:
STDMETHODIMP CDocHostUIHandler::QueryInterface(REFIID riid, PVOID* pInterface)
{
*pInterface = NULL;
if (riid==IID_IUnknown)
{
*pInterface=this;
}
else if (riid==IID_IDocHostUIHandler)
{
*pInterface=this;
}
if(*pInterface)
{
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP CDocHostUIHandler::ShowContextMenu(DWORD dwID, POINT* pPt,
IUnknown* pcmdtReserved, IDispatch* pdispreserved)
{
return S_OK;
}
注意,由于微软提供的IDocHostUIHandler接口是继承自IUnknown的虚拟结构,所以如果要实现该接口,则必须实现该结构下的所有函数,对于不必做修改,只需保持原有功能的函数直接返回“E_NOTIMPL”即可。
♀ 加载内存中的页面(来自网络,未验证)
或者因为网页保密的考虑;或者因为软件分发的考虑,有的时候就需要让IE或IE浏览器控件显示内存或资源中的HTML网页。在 MFC 中,CHtmlView::LoadFromResource() 可以显示程序资源中的 HTML 内容。我们都知道MFC的CHtmlView其实是对IWebBrowser2的一个包装,但是在 IWebBrowser2 中却没有类似的方法。那么它是如何实现的呢?步骤如下:
1、首先通过IWebBrowser2::Navigate2()显示一个网页,其目的是产生有效的对象,从而得到IHTMLDocument2接口;
2、IWebBrowser2::get_Document() 得到 IHTMLDocument2 接口指针;
3、IHTMLDocument2::QueryInterface() 得到 IPersistStreamInit 接口指针;
4、IPersistStreamInit::InitNew() 初始化接口对象;
5、IPersistStreamInit::Load() 装载内存中的 HTML 数据流(IStream *);
内存指针转换为流的方法是:
I、 GlobalAlloc() 申请内存;
II、 复制 HTML 字符串内容到上述的内存中;
III、CreateStreamFromHGlobal() 转换内存为 IStream 指针;
原理性代码如下:
// 显示一个空白网页
m_ie.Navigate2( &CComVariant(_T("about:blank")),NULL,NULL,NULL,NULL);
// 得到 IHTMLDocument2 指针
CComPtr< IDispatch > spDoc(m_ie.GetDocument());
// 得到 IPersistStreamInit 指针
CComQIPtr< IPersistStreamInit, &IID_IPersistStreamInit > spPSI( spDoc );
// 申请内存,复制 HTML 字符串
LPTSTR lpMem = (LPTSTR)::GlobalAlloc( GPTR, ::lstrlen( lpHtml )+1 );
lstrcpy( lpMem, "xxx xxx" );
// 转换内存为流对象指针
CComPtr< IStream > spStream;
CreateStreamOnHGlobal( lpMem, TRUE, &spStream );
// 初始化后,装载显示
spPSI->InitNew();
spPSI->Load( spStream );
IE 所能支持的数据传输协议,除了大家所熟悉的 http、ftp、file......还有一个协议是 res ,它表示浏览显示文件中的 HTML 资源。你可以在 IE 的地址栏上直接输入这样格式的 URL:"res://文件名/资源名"。
把 HTML 文件加入到程序资源的方法比较简单,在资源卡片中,鼠标右键弹出菜单,执行 Import...(引入),选择指定的 HTML 文件,然后给一个资源名称即可。(在这里,最方便的资源名称用字符串比较好,如果使用整数ID,那么将来在使用的时候是这样的格式:res://文件名/#101,这里假设 101 是资源的ID号。真麻烦!我不太喜欢这样的方式。)对于图片文件等其它的附件,则需要手工编辑资源 RC 文件(用 IDE 环境引入,它会试图用文本方式打开一个2进制文件,多数情况下会“死机”)。
手工编辑 RC 文件的部分是:
......
/
//
// HTML
//
// 这两个是HTML文件,可以引入
HTML_TOWORD HTML DISCARDABLE "res\\ToWord.htm"
HTML_DLG HTML DISCARDABLE "res\\html_dlg.htm"
// 下面的是GIF文件,需要手工加入
~SEND_R1_C1.GIF HTML DISCARDABLE "res\\~Send_r1_c1.gif"
~SEND_R1_C2.GIF HTML DISCARDABLE "res\\~Send_r1_c2.gif"
LOGO.GIF HTML DISCARDABLE "res\\Logo.gif"
SEND_R1_C1.GIF HTML DISCARDABLE "res\\Send_r1_c1.gif"
SEND_R1_C2.GIF HTML DISCARDABLE "res\\Send_r1_c2.gif"
SPACER.GIF HTML DISCARDABLE "res\\spacer.gif"
#endif // Chinese (P.R.C.) resources
/
♀ 响应事件
WebBrowser的事件主要包括Control事件、Window事件及Docment事件三大类,微软分别提供了三个虚拟Event结构表示相关事件,分别是:DWebBrowserEvents、HTMLWindowEvents、HTMLDocumentEvents。使用时只需继承、实现相关Event结构,并安装相关事件即可。以下代码演示了如何继承、实现Docment事件。
// 事件类定义class
CHtmlDocEvents : public HTMLDocumentEvents
{
public:
CHtmlDocEvents();
~CHtmlDocEvents();
DWORD m_dwCount;
protected:
virtual STDMETHODIMP QueryInterface(REFIID,void**);
virtual STDMETHODIMP_(DWORD) AddRef();
virtual STDMETHODIMP_(DWORD) Release();
virtual STDMETHODIMP GetTypeInfoCount(UINT*);
virtual STDMETHODIMP GetTypeInfo(UINT,LCID,ITypeInfo**);
virtual STDMETHODIMP GetIDsOfNames(REFIID,LPOLESTR*,UINT,LCID,DISPID*);
virtual STDMETHODIMP Invoke(DISPID,REFIID,LCID,WORD,DISPPARAMS*,
VARIANT*,EXCEPINFO*,UINT*);
};
// 事件类实现
STDMETHODIMP CHtmlDocEvents::QueryInterface(REFIID riid, PVOID* ppInterface)
{
*ppInterface = 0;
if (riid==IID_IUnknown)
{
*ppInterface = this;
}
else if (riid==IID_IDispatch)
{
*ppInterface = this;
}
else if (riid==DIID_HTMLDocumentEvents)
{
*ppInterface = this;
}
if (*ppInterface)
{
(*reinterpret_cast(ppInterface))->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(DWORD) CHtmlDocEvents::AddRef()
{
m_dwCount++;
return m_dwCount;
}
STDMETHODIMP_(DWORD) CHtmlDocEvents::Release()
{
m_dwCount--;
return m_dwCount;
}
STDMETHODIMP CHtmlDocEvents::GetIDsOfNames(REFIID, LPOLESTR*, UINT,
LCID, DISPID*)
{
return E_NOTIMPL;
}
// ……
// ……
STDMETHODIMP CHtmlDocEvents::Invoke(DISPID dispidMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS* pdispparams,
VARIANT* pvarResult, EXCEPINFO* pexcepinfo,
UINT* puArgErr)
{
switch (dispidMember)
{
case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEMOVE:
case DISPID_HTMLDOCUMENTEVENTS_ONHELP:
case DISPID_HTMLDOCUMENTEVENTS_ONCLICK:
case DISPID_HTMLDOCUMENTEVENTS_ONDBLCLICK:
case DISPID_HTMLDOCUMENTEVENTS_ONKEYDOWN:
case DISPID_HTMLDOCUMENTEVENTS_ONKEYUP:
case DISPID_HTMLDOCUMENTEVENTS_ONKEYPRESS:
case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEDOWN:
case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEUP:
case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOUT:
case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOVER:
case DISPID_HTMLDOCUMENTEVENTS_ONREADYSTATECHANGE:
case DISPID_HTMLDOCUMENTEVENTS_ONBEFOREUPDATE:
case DISPID_HTMLDOCUMENTEVENTS_ONAFTERUPDATE:
case DISPID_HTMLDOCUMENTEVENTS_ONROWEXIT:
case DISPID_HTMLDOCUMENTEVENTS_ONROWENTER:
case DISPID_HTMLDOCUMENTEVENTS_ONDRAGSTART:
case DISPID_HTMLDOCUMENTEVENTS_ONSELECTSTART:
case DISPID_HTMLDOCUMENTEVENTS_ONERRORUPDATE:
default:
return E_NOTIMPL;
}
return NOERROR;
}
// ……
以下代码演示了如何安装Window和Docment事件,其中m_pUiWeb为指向WebBrowser对象的指针,m_pHtmlWindowEvents为指向自定义Window事件对象的指针,m_pHtmlDocEvents为指向CHtmlDocEvents对象的指针:
void DocumentCompleteUiWeb(LPDISPATCH pDisp, VARIANT* URL)
{
LPUNKNOWN pUnknown = m_pUiWeb->GetControlUnknown();
if( NULL == pUnknown )
{
return;
}
LPUNKNOWN pUnknownWeb = NULL;
LPUNKNOWN pUnknownDisp = NULL;
HRESULT hr = pUnknown->QueryInterface(IID_IUnknown, (LPVOID*)&pUnknownWeb);
if( FAILED(hr) )
{
return;
}
hr = pDisp->QueryInterface(IID_IUnknown, (LPVOID*)&pUnknownDisp);
if( FAILED(hr) || pUnknownWeb != pUnknownDisp)
{
pUnknownWeb->Release();
return;
}
pUnknownWeb->Release();
pUnknownDisp->Release();
IHTMLDocument2 *pHtmlDoc2 = (IHTMLDocument2*)m_pUiWeb->get_Document();
IConnectionPointContainer* pCPC = NULL;
DWORD dwCookie = 0;
{// 安装Web Window事件
IHTMLWindow2 *pHtmlWnd2 = NULL;
hr = pHtmlDoc2->get_parentWindow(&pHtmlWnd2);
if( FAILED(hr) )
{
return;
}
hr = pHtmlWnd2->QueryInterface(IID_IConnectionPointContainer,
reinterpret_cast(&pCPC));
if(FAILED(hr))
{
return;
}
IConnectionPoint* pCP = NULL;
pCPC->FindConnectionPoint(DIID_HTMLWindowEvents2, &pCP); // 找到安装点
if( NULL == m_pHtmlWindowEvents )
{
m_pHtmlWindowEvents = new CHtmlWindowEvents(this);
}
hr = pCP->Advise(m_pHtmlWindowEvents, &dwCookie); //安装
if( SUCCEEDED(hr) )
{
pCP->Release();
}
pCPC->Release();
}
{// 安装Web Document事件
hr = pHtmlDoc2->QueryInterface(IID_IConnectionPointContainer,
reinterpret_cast(&pCPC));
if(FAILED(hr))
{
return;
}
IConnectionPoint* pCP = NULL;
pCPC->FindConnectionPoint(DIID_HTMLDocumentEvents2, &pCP); // 找到安装点
if( NULL == m_pHtmlDocEvents )
{
m_pHtmlDocEvents = new CHtmlDocEvents(this, m_pMaskWnd, m_pUiWeb,
m_pPosIconWnd);
}
hr = pCP->Advise(m_pHtmlDocEvents, &dwCookie); // 安装
if( SUCCEEDED(hr) )
{
pCP->Release();
}
pCPC->Release();
}
}
♀ 用VC在网页中绘图
用VC在网页上绘图主要通过WebBrowser的IElementBehavior接口和IHTMLPainter接口来实现。具体内容参照MSDN中的《Using Rendering Behaviors》说明,该说明中也有相关演示代码。