C++中嵌入IE浏览器(二)

双向通讯

   第一步解决了边框和上下文菜单问题,第二部就是要解决c++程序和html页面交互的问题。最开始的想法是通过c++去更新页面内容的方式来完成c++->html的通讯,通过BeforeNavigate2 接口,截获页面url地址的方式来完成html->c++的通讯。但是这种方式存在以下缺点: 

       (1) c++->html 的问题在于导致c++代码复杂,需要通过c++代码来完成页面生成,如果修改页面,将产生很大的工作量。虽然尝试用了模板方法解决,但是还是比较繁琐,而且会导致经常通讯的时候,页面会经常刷新,产生其他的一些问题。
      (2) html->c++ 的问题在于 传递参数不方便,解析也不方便、无法获取返回值、脚本中要调用不方便

     为了解决这些问题,经过google后找到了问题的解决办法 : 
    (1) c++->html  ,可以通过调用页面脚本方法来实现,调用方法如下: 

wxVariant wxIEHtmlWin::ExecScript( const wxString &fun, const std::vector<wxString> & params )
{
    wxVariant result(false);
    if (! m_webBrowser.Ok())
        return result;

    // get document dispatch interface
    IDispatch *iDisp = NULL;
    HRESULT hr = m_webBrowser->get_Document(&iDisp);
    if (hr != S_OK)
        return result;

    // Query for Document Interface
    wxAutoOleInterface<IHTMLDocument2> hd(IID_IHTMLDocument2, iDisp);
    iDisp->Release();

    if (! hd.Ok())
        return result;

    IDispatch *spScript;
    hr = hd->get_Script(&spScript);

    if(FAILED(hr))
        return result;
    BSTR bstrMember = wxConvertStringToOle(fun);
    DISPID dispid = NULL;
    hr = spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,
        LOCALE_SYSTEM_DEFAULT,&dispid);
    if(FAILED(hr))
    {
        
        return result;
    }

    //Putting parameters
    DISPPARAMS dispparams;
    memset(&dispparams, 0, sizeof dispparams);
    dispparams.cArgs      = params.size();
    dispparams.rgvarg     = new VARIANT[dispparams.cArgs];
    dispparams.cNamedArgs = 0;

    forint i = 0; i < params.size(); i++)
    {
        CComBSTR bstr = wxConvertStringToOle(params[params.size() - 1 - i]);
        // back reading
        bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);

        dispparams.rgvarg[i].vt = VT_BSTR;
    }

    EXCEPINFO excepInfo;
    memset(&excepInfo, 0, sizeof excepInfo);
    VARIANT   varRet;
    UINT nArgErr = (UINT)-1;      // initialize to invalid arg
    
//Call JavaScript function
    hr = spScript->Invoke(dispid,IID_NULL,0,
        DISPATCH_METHOD,&dispparams,
        &varRet,&excepInfo,&nArgErr);
    delete [] dispparams.rgvarg;
    if(FAILED(hr))
    {
        
        return result;
    }


    wxConvertOleToVariant(varRet,result);
    return result;


}

  这个方法实现了C++对页面脚本调用,而且参数个数可以任意。比如页面脚本是 :
function fun(a,b,c)
{
}

  C++中的调用方法是 :
std::vector<wxString>  params;
params.push_back("a");
params.push_back("b");
params.push_back("c");
xxx->ExecScripts("fun", params);

还可以获得脚本返回的结果。

  (2) html->c++  通过脚本的window.external 方法,首先,在前文提到过的IDocHostUIHandler 接口中,实现方法: 
HRESULT STDMETHODCALLTYPE FrameSite::GetExternal(IDispatch **ppDispatch)
{
     
    IDispatch * pDisp = m_window->getExternal();
    if(pDisp)
    {
        pDisp->AddRef();
        *ppDispatch = pDisp;
    }


    return S_OK;
}
其中 m_window->getExternal();
 返回的是自定义的一个IDispatch 接口类: 
/*
 * IDispimp.H
 * IDispatch
 *
 * Copyright (c)1995-1999 Microsoft Corporation, All Rights Reserved
 
*/
 


#ifndef _IDISPIMP_H_
#define _IDISPIMP_H_
#include <oaidl.h>
class CustomFunction;
class CImpIDispatch :  public IDispatch
{
    protected:
        ULONG               m_cRef;

    public:
        CImpIDispatch(void);
        ~CImpIDispatch(void);

        STDMETHODIMP QueryInterface(REFIID, void **);
        STDMETHODIMP_(ULONG) AddRef(void);
        STDMETHODIMP_(ULONG) Release(void);

        //IDispatch
        STDMETHODIMP GetTypeInfoCount(UINT* pctinfo);
        STDMETHODIMP GetTypeInfo(/* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo** ppTInfo);
        STDMETHODIMP GetIDsOfNames(
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID *rgDispId);
        STDMETHODIMP Invoke(
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS  *pDispParams,
            /* [out] */ VARIANT  *pVarResult,
            /* [out] */ EXCEPINFO *pExcepInfo,
            /* [out] */ UINT *puArgErr);

            void setCustomFunction(CustomFunction *fun) {m_fun = fun;}
private:
    CustomFunction *m_fun;

}
;
#endif  // _IDISPIMP_H_
主要实现以下两个方法: 
wxString cszCB_CustomFunction = wxT("CB_CustomFunction");
  #define DISPID_CB_CustomFunction 3
STDMETHODIMP CImpIDispatch::GetIDsOfNames(
             /* [in] */ REFIID riid,
             /* [size_is][in] */ OLECHAR** rgszNames,
             /* [in] */ UINT cNames,
             /* [in] */ LCID lcid,
             /* [size_is][out] */ DISPID* rgDispId)
{
    HRESULT hr;
    UINT    i;

    // Assume some degree of success
    hr = NOERROR;


        for ( i=0; i < cNames; i++) {
        wxString cszName  = rgszNames[i];
 
        if(cszName == cszCB_CustomFunction)
        {
            rgDispId[i] = DISPID_CB_CustomFunction;
        }
    
     
        else {
            // One or more are unknown so set the return code accordingly
            hr = ResultFromScode(DISP_E_UNKNOWNNAME);
            rgDispId[i] = DISPID_UNKNOWN;
        }

    }

    return hr;
}


STDMETHODIMP CImpIDispatch::Invoke(
             /* [in] */ DISPID dispIdMember,
             /* [in] */ REFIID  /*riid*/,
             /* [in] */ LCID  /*lcid*/,
             /* [in] */ WORD wFlags,
             /* [out][in] */ DISPPARAMS* pDispParams,
             /* [out] */ VARIANT* pVarResult,
             /* [out] */ EXCEPINFO*  /*pExcepInfo*/,
             /* [out] */ UINT* puArgErr)
{

     
    
    if(dispIdMember == DISPID_CB_CustomFunction) 
    {
        if(wFlags & DISPATCH_PROPERTYGET)
        {
            if(pVarResult != NULL)
            {
                
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BOOL;
                V_BOOL(pVarResult) = true;
            }

        }

        
        if ( wFlags & DISPATCH_METHOD )
        {
            //arguments come in reverse order
            
//for some reason
            if(!m_fun) return S_OK;

            wxString arg1,arg2;
         
            if(pDispParams->cArgs<1) return S_FALSE;
            wxString cmd = pDispParams->rgvarg[pDispParams->cArgs-1].bstrVal;
            std::vector<wxString> args;
            if(pDispParams->cArgs>1)
            {
                for(int i=pDispParams->cArgs-2;i>=0;i--)
                    args.push_back(pDispParams->rgvarg[i].bstrVal);
            }

            wxString re = m_fun->execute(cmd,args);
            if(pVarResult != NULL)
            {
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BSTR;
                wxVariant wVar(re);
                VariantToMSWVariant(wVar,*pVarResult);
                
                 
            }

         
        }

        
    }


     

    return S_OK;
}

  其中 CustomFunction 定义如下:
#pragma once
#include <wx/wx.h>
#include <vector>
class CustomFunction
{
public:

    CustomFunction(void)
    {
    }


    virtual ~CustomFunction(void)
    {
    }


    virtual wxString execute(const wxString &cmd, const std::vector<wxString> &args) = 0;
}
;
  然后只要在自己类里面继承这个接口,就可以接收来之脚本的调用请求。
脚本里面编写函数:
window._callFun = function()
                 {
                     
                    var fun = "window.external.CB_CustomFunction(";
                    for(i=0;i<arguments.length;i++)
                    {
                        if(i!=0)
                            fun = fun+",";
                        fun = fun+"\""+arguments[i]+"\"";
                    }
    
                    fun = fun+")";
                    //alert(fun);
                    return (eval(fun));
                }
   然后调用的地方写: 
 
_callFun("fun","param1","param2", );
就可以调用c++的函数,并且可以得到返回值,从而解决了html->c++的通讯问题 

   解决了双向通讯后,页面就不需要用刷新来解决,网页设计师和c++编程人员只要定义好通讯接口,大家各自实现好接口方法就可以完成界面功能了。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值