技术员博客      html  css  js  c++  java
  • C++中嵌入ie浏览器总结 .

    C++中嵌入ie浏览器总结(1) - ie边框 及上下文菜单

    最近项目中用html 来做界面,也就折腾了一下在wxwidget中嵌入浏览器的若干细节工作,mfc也基本是类似的,由于wxwidget中已经做了一个封装wxie,但是开发过程中也遇到了不少问题,在此做一下总结:

    1. ie边框 及上下文菜单
      普通嵌入到程序里面的浏览器页面都会有一个灰色的边框,这样放到程序里面就很难看。目前网上流行的办法添加css:
      body{
      border
      :0;
      }

    但是这个方法的缺点是,必须要把页面头部的dtd申明去掉。网上的错误提法是类似以下链接的:http://www.faceker.com/200801/webbrowser-no-border.html 这里面讲,只要把头部改成:

    <!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">

    但是目前很多页面用了比较新的css,改成这样后,页面就无法正常显示了。让网页美工改样式? 真是有点困难。
    后来继续查阅了资料,发现比较好的办法,那就是重载 IDocHostUIHandler 接口,其中,实现以下部分:


    HRESULTSTDMETHODCALLTYPEFrameSite::GetHostInfo(DOCHOSTUIINFO
    *pInfo)
    {
    pInfo
    ->cbSize=sizeof(DOCHOSTUIINFO);
    pInfo
    ->dwFlags=DOCHOSTUIFLAG_NO3DBORDER|DOCHOSTUIFLAG_SCROLL_NO;
    pInfo
    ->dwDoubleClick=DOCHOSTUIDBLCLK_DEFAULT;
    returnS_OK;

    }

    其中 DOCHOSTUIFLAG_NO3DBORDER 就表示不要生成边框,DOCHOSTUIFLAG_SCROLL_NO 表示不要生成滚动条
    这样就可以比较完美的解决边框和滚动条的问题,不用依赖页面的调整 。让设计师爱用什么用什么。
    另外一个就是禁用右键菜单,网上也有不少办法,但是用这个接口可以很简单的实现:

    HRESULTSTDMETHODCALLTYPEFrameSite::ShowContextMenu(DWORDdwID,POINT*ppt,
    IUnknown
    *pcmdtReserved,IDispatch*pdispReserved)
    {
    HRESULTresult
    =S_FALSE;//DontInterfere
    BOOLhandled=FALSE;

    switch(m_contextMenuMode)
    {
    casekDefaultMenuSupport:
    break;

    casekNoContextMenu:
    result
    =S_OK;
    handled
    =TRUE;
    break;

    casekTextSelectionOnly:
    if(dwID!=CONTEXT_MENU_TEXTSELECT)
    {
    result
    =S_OK;
    handled
    =TRUE;
    }

    break;

    casekAllowAllButViewSource:
    if(dwID==CONTEXT_MENU_DEFAULT)
    {
    //result=ModifyContextMenu(dwID,ppt,pcmdtReserved);
    handled=TRUE;
    }

    break;

    casekCustomMenuSupport:
    if(dwID==CONTEXT_MENU_DEFAULT)
    {
    //result=CustomContextMenu(ppt,pcmdtReserved);
    handled=TRUE;
    }

    break;
    }


    if(!handled)
    {
    result
    =S_FALSE;
    }


    returnresult;
    }

    这里不仅可以控制右键菜单显示,m_contextMenuMode = kNoContextMenu,还可以做到自定义菜单显示,m_contextMenuMode =其他值。因为暂时还不需要自定义菜单,所以这里没有实现。

    如果用wxie,就在FrameSite类增加这个接口即可,不关注的接口直接返回S_FALSE 或E_NOTIMPL;

    如果用sdk或mfc,可以 调用IOleObject 的SetClientSite 方法,设置一个继承了IOleClientSite 和 IDocHostUIHandler 的接口。

    C++中嵌入ie浏览器总结(2) - 双向通讯

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

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

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

    wxVariantwxIEHtmlWin::ExecScript(constwxString&fun,conststd::vector<wxString>&params)
    {
    wxVariantresult(
    false);
    if(!m_webBrowser.Ok())
    returnresult;

    //getdocumentdispatchinterface
    IDispatch*iDisp=NULL;
    HRESULThr
    =m_webBrowser->get_Document(&iDisp);
    if(hr!=S_OK)
    returnresult;

    //QueryforDocumentInterface
    wxAutoOleInterface<IHTMLDocument2>hd(IID_IHTMLDocument2,iDisp);
    iDisp
    ->Release();

    if(!hd.Ok())
    returnresult;

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

    if(FAILED(hr))
    returnresult;
    BSTRbstrMember
    =wxConvertStringToOle(fun);
    DISPIDdispid
    =NULL;
    hr
    =spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,
    LOCALE_SYSTEM_DEFAULT,
    &dispid);
    if(FAILED(hr))
    {

    returnresult;
    }

    //Puttingparameters
    DISPPARAMSdispparams;
    memset(
    &dispparams,0,sizeofdispparams);
    dispparams.cArgs
    =params.size();
    dispparams.rgvarg
    =newVARIANT[dispparams.cArgs];
    dispparams.cNamedArgs
    =0;

    for(inti=0;i<params.size();i++)
    {
    CComBSTRbstr
    =wxConvertStringToOle(params[params.size()-1-i]);
    //backreading
    bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);

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

    EXCEPINFOexcepInfo;
    memset(
    &excepInfo,0,sizeofexcepInfo);
    VARIANTvarRet;
    UINTnArgErr
    =(UINT)-1;//initializetoinvalidarg
    //CallJavaScriptfunction
    hr=spScript->Invoke(dispid,IID_NULL,0,
    DISPATCH_METHOD,
    &dispparams,
    &varRet,&excepInfo,&nArgErr);
    delete[]dispparams.rgvarg;
    if(FAILED(hr))
    {

    returnresult;
    }


    wxConvertOleToVariant(varRet,result);
    returnresult;


    }


    这个方法实现了C++对页面脚本调用,而且参数个数可以任意。比如页面脚本是:

    functionfun(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 接口中,实现方法:

    HRESULTSTDMETHODCALLTYPEFrameSite::GetExternal(IDispatch**ppDispatch)
    {

    IDispatch
    *pDisp=m_window->getExternal();
    if(pDisp)
    {
    pDisp
    ->AddRef();
    *ppDispatch=pDisp;
    }


    returnS_OK;
    }

    其中 m_window->getExternal();
    返回的是自定义的一个IDispatch 接口类:

    /*
    *IDispimp.H
    *IDispatch
    *
    *Copyright(c)1995-1999MicrosoftCorporation,AllRightsReserved
    */



    #ifndef_IDISPIMP_H_
    #define_IDISPIMP_H_
    #include
    <oaidl.h>
    classCustomFunction;
    classCImpIDispatch:publicIDispatch
    {
    protected:
    ULONGm_cRef;

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

    STDMETHODIMPQueryInterface(REFIID,
    void**);
    STDMETHODIMP_(ULONG)AddRef(
    void);
    STDMETHODIMP_(ULONG)Release(
    void);

    //IDispatch
    STDMETHODIMPGetTypeInfoCount(UINT*pctinfo);
    STDMETHODIMPGetTypeInfo(
    /*[in]*/UINTiTInfo,
    /*[in]*/LCIDlcid,
    /*[out]*/ITypeInfo**ppTInfo);
    STDMETHODIMPGetIDsOfNames(
    /*[in]*/REFIIDriid,
    /*[size_is][in]*/LPOLESTR*rgszNames,
    /*[in]*/UINTcNames,
    /*[in]*/LCIDlcid,
    /*[size_is][out]*/DISPID*rgDispId);
    STDMETHODIMPInvoke(
    /*[in]*/DISPIDdispIdMember,
    /*[in]*/REFIIDriid,
    /*[in]*/LCIDlcid,
    /*[in]*/WORDwFlags,
    /*[out][in]*/DISPPARAMS*pDispParams,
    /*[out]*/VARIANT*pVarResult,
    /*[out]*/EXCEPINFO*pExcepInfo,
    /*[out]*/UINT*puArgErr);

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

    }
    ;
    #endif//_IDISPIMP_H_

    主要实现以下两个方法:

    wxString cszCB_CustomFunction = wxT("CB_CustomFunction");
    #define DISPID_CB_CustomFunction 3
    STDMETHODIMPCImpIDispatch::GetIDsOfNames(
    /*[in]*/REFIIDriid,
    /*[size_is][in]*/OLECHAR**rgszNames,
    /*[in]*/UINTcNames,
    /*[in]*/LCIDlcid,
    /*[size_is][out]*/DISPID*rgDispId)
    {
    HRESULThr;
    UINTi;

    //Assumesomedegreeofsuccess
    hr=NOERROR;


    for(i=0;i<cNames;i++){
    wxStringcszName
    =rgszNames[i];

    if(cszName==cszCB_CustomFunction)
    {
    rgDispId[i]
    =DISPID_CB_CustomFunction;
    }


    else{
    //Oneormoreareunknownsosetthereturncodeaccordingly
    hr=ResultFromScode(DISP_E_UNKNOWNNAME);
    rgDispId[i]
    =DISPID_UNKNOWN;
    }

    }

    returnhr;
    }


    STDMETHODIMPCImpIDispatch::Invoke(
    /*[in]*/DISPIDdispIdMember,
    /*[in]*/REFIID/*riid*/,
    /*[in]*/LCID/*lcid*/,
    /*[in]*/WORDwFlags,
    /*[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)
    {
    //argumentscomeinreverseorder
    //forsomereason
    if(!m_fun)returnS_OK;

    wxStringarg1,arg2;

    if(pDispParams->cArgs<1)returnS_FALSE;
    wxStringcmd
    =pDispParams->rgvarg[pDispParams->cArgs-1].bstrVal;
    std::vector
    <wxString>args;
    if(pDispParams->cArgs>1)
    {
    for(inti=pDispParams->cArgs-2;i>=0;i--)
    args.push_back(pDispParams
    ->rgvarg[i].bstrVal);
    }

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


    }


    }


    }




    returnS_OK;
    }


    其中 CustomFunction 定义如下:

    #pragmaonce
    #include
    <wx/wx.h>
    #include
    <vector>
    classCustomFunction
    {
    public:

    CustomFunction(
    void)
    {
    }


    virtual~CustomFunction(void)
    {
    }


    virtualwxStringexecute(constwxString&cmd,conststd::vector<wxString>&args)=0;
    }
    ;

    然后只要在自己类里面继承这个接口,就可以接收来之脚本的调用请求。
    脚本里面编写函数:

    window._callFun=function()
    {

    varfun
    ="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++编程人员只要定义好通讯接口,大家各自实现好接口方法就可以完成界面功能了。

    C++中嵌入ie浏览器总结(3) - wxIE 的 bug解决及最后效果展示

    前面两个问题的解决,心里想: 这下好了,解决了这两个问题,下一步做界面就简单多了,不像以前,直接用mfc或第三方库做,要做出好看效果真是很难。编译好,运行了,发现效果还不错。但是实现到里面的脚本的时候,发现出了问题,不响应键盘消息了。而且在页面文本框里面按tab键,光标不是跑到下一个文本框,而是不见了。之前有这个现象,但是忙于解决前面的问题,没有注意到。这下可完了,不会前工尽弃吧,那可麻烦大了。
    google了半天,问了朋友,还是不知道什么原因。因为wxIE及嵌入浏览器本身是比较偏门的问题,确实很难找到答案。
    山穷水尽疑无路,柳暗花明又一村。好不容易,在google上找到了一个类似的问题,回答的说,这是wxIE的bug,在wxPython的项目中,这个问题已经解决了。下载下来编译后,运行试试看,搞定了
    但是还得和之前的修改合并,合并过程中又发现了一个问题。前文中提到过,通过IOleObject 接口来设置IDocHostUIHandler方法,我开始就是用这种方法。结果合并完后,发现还是不响应键盘消息
    调了半天,才发现,wxIE原来已经实现过IOleClientSite 接口,我把自己的接口设上去,把wxIE的给替换掉了,所以导致了不正常的结果。 经过一番调整后,终于正常了
    自此,用html做c++程序界面的基础工作算是告一段落了,下面就是完成接口工作和页面脚本了。希望不要再遇到什么问题。
    我在这里把这些问题记录下来,以备以后查用,也愿其他朋友不要再遇到我一样的困扰。

    下面截一张做出来的界面图:


    这是一个对话框,完成前面的基础工作后,只要设计师设计好页面,我们几分钟就可以继承到c++里面,再花点写接口和脚本的时间,比原来用mfc做界面,不知要节省多少时间。这个界面比较简单,但是只要是能设计出的界面,我们都能让他集成进来。
    有兴趣的朋友也不妨试试这种做界面的方法。

    C++中嵌入ie浏览器总结(4) - 对话框拖动

    前面忘了写这个问题,就是对话框的拖动问题。就像我前面的图片展示的对话框,一般的windows对话框是可以拖着标题栏移动的,但是我们这里没有任何原来的标题栏了,只有html页面,怎么拖呢? 好像有点麻烦。
    冥思苦想之后,想到一种办法,通过前面的接口给c++发指令,让c++移动窗口,页面上计算好拖动的距离。html里面页面的拖动还是比较简单的,c++里面移动也就是调用 MoveWindow。由于前面的通讯方法还是比较灵活,没用多少时间,就把这个功能实现了。但是一运行看,不太对劲,拖动过程拖尾现象太明显。可能是c++不断调用 MoveWindow 重绘效率比较低。这可麻烦了。这时候,突然想到普通对话框拖动的时候,是一个虚框在那里动,原来的对话框是不动的,鼠标放开后,对话框才移过去。 能不能做到这样呢? 但是windows实现这个方法的细节不得而知,怎么做呢?
    还是google好啊,经过一番搜索,找到了答案:

    voidTooltipDlg::moveWin(conststd::vector<wxString>&args)
    {
    if(args.size()<2)return;
    longx,y;
    args[
    0].ToLong(&x);
    args[
    1].ToLong(&y);
    intix,iy;
    ix
    =x;
    iy
    =y;
    ClientToScreen(
    &ix,&iy);


    ::SendMessage((HWND)
    this->GetHWND(),WM_NCMOUSEMOVE,HTCAPTION,MAKELPARAM(ix,iy));

    }

    搞定了,简单吧,真是没想到这么简单。运行后发现,真的和windows的对话框移动一模一样了,太好了

    今天一鼓作气把前面几天的工作都总结了下来,还真是敲得手有点累。但是这些东西确实是不太常规的方法,找解决问题的方法很难,这里先把他们记录下来,免得以后找不到了。以前确实有很多知识都是用了就丢一边找不到了。 也希望给有类似疑问的朋友一个帮助。

  • 相关阅读:
    Swing 边框(一)
    HTML 5 来了,不需要 Flash 插件的 Youtube
    PHP程序员如何突破成长瓶颈(php开发三到四年)
    ocx开发小结 .
    艾伟:ASP.NET安全问题--Forms验证的具体介绍(上篇) 狼人:
    巧用IronPython做更灵活的网页爬虫
    headless=脑残?
    QT发布中遇到的问题 wufan的专栏 博客频道 CSDN.NET
    古哥,一路走好
    Groovy闭包深入学习 [203] 一直都有新高度 ITeye技术网站
  • 原文地址:https://www.cnblogs.com/lidabo/p/2795528.html
Copyright © 2011-2022 技术员博客