VC操作Web Browser的若干技巧

♀ 获取文档的总高度和宽度以及页面当前显示的文档位置
    以下代码用于获取页面当前显示的文档位置,如需获取文档的总高度和宽度,只需将函数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》说明,该说明中也有相关演示代码。
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值