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   这里面讲,只要把头部改成:
   

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

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


HRESULT STDMETHODCALLTYPE FrameSite::GetHostInfo(DOCHOSTUIINFO *pInfo)
{
    pInfo->cbSize = sizeof(DOCHOSTUIINFO);
    pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER|DOCHOSTUIFLAG_SCROLL_NO;
    pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
    return S_OK;

}

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

HRESULT STDMETHODCALLTYPE FrameSite::ShowContextMenu(DWORD dwID, POINT *ppt, 
                                                             IUnknown *pcmdtReserved, IDispatch *pdispReserved)
{
    HRESULT result    = S_FALSE; //Dont Interfere
    BOOL    handled    = FALSE;

    switch ( m_contextMenuMode )
    {
    case kDefaultMenuSupport:
        break;

    case kNoContextMenu:
        result    = S_OK;
        handled    = TRUE;
        break;

    case kTextSelectionOnly:
        if (dwID != CONTEXT_MENU_TEXTSELECT)
        {
            result    = S_OK;
            handled    = TRUE;
        }
        break;

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

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

    if (! handled)
    {
         result = S_FALSE;
    }

    return result;
}

    这里不仅可以控制右键菜单显示,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  ,可以通过调用页面脚本方法来实现,调用方法如下:

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;

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

 

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好啊,经过一番搜索,找到了答案: 
  

void TooltipDlg::moveWin( const std::vector<wxString> &args )
{
    if(args.size()<2) return;
    long x,y;
    args[0].ToLong(&x);
    args[1].ToLong(&y);
    int ix,iy;
    ix = x;
    iy = y;
    ClientToScreen(&ix,&iy);
 

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

}

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

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

时间: 2024-08-29 17:57:01

C++中嵌入ie浏览器总结 .的相关文章

WPF框架中使用CefSharp嵌入web浏览器的方法教程

首先先介绍一下CefSharp嵌入式浏览器,它是基于Google浏览器的一个组件,我们可以在WPF/WinForm客户端软件中使用它.CefSharp的代码托管在GitHub上,.NET (WPF and Windows Forms) bindings for the Chromium Embedded Framework. 目前最新版本的CefSharp是41.0版本,如果你的客户端软件需要支持WIN XP操作系统,建议使用CefSharp.Wpf 1.25.7及之前的版本.可以从Nuget上

浏览器-Objective-C编写app中嵌入h5页面 geolocation定位问题

问题描述 Objective-C编写app中嵌入h5页面 geolocation定位问题 我制作了一个用到geolocation定位的h5页面,让别人嵌到他开发的app中,用Safari浏览器时可以实现定位的,但是在app却做不到定位.貌似在app中嵌入网页是调用webkit,目前找不到问题所在,而且对objectc没有很多的了解. 开发的那个人让我找原因,网上搜了一圈没找到什么可用的,想问一下: 1.问题应该是出在哪里? 2.我这个H5页面需要改吗?还是要app那边做调整?

javascript-如何在iframe中嵌入mht文件

问题描述 如何在iframe中嵌入mht文件 除了IE浏览器可以直接显示外其它浏览器都会弹出下载,该如何解决这样的问题? 解决方案 如果你有php服务器端,可以将mht文件作为mime编码的html解析,然后返回客户端,而不是直接嵌入(和读取邮件附件其实是一个道理,mht其实就是邮件附件的格式).因为mht是微软自己用的,所以别的浏览器不认. 解决方案二: Iframe不能解析mht格式文件

JavaScript解析:HTM中嵌入嵌入JavaScript语言引擎

文章简介:JavaScript解析:让搜索引擎看到更真实的网页. 长期以来,站长们选择使用JavaScript来实现网页的动态行为,这样做的原因是多种多样的,如加快页面的响应速度.降低网站流量.隐藏链接或者嵌入广告等.由于早期的搜索引擎没有相应的处理能力,导致在索引这类网页上往往出现问题,可能无法收录有价值的资源,也可能出现作弊. 引入JavaScript解析的目的,正是为了解决上述两方面的问题,其结果也就是使搜索引擎可以更为清晰的了解用户实际打开该网页时看到的效果.比如有些网站会将用户评论.评

在Flex中嵌入完整HTML页面

页面 有时候我们需要在Flex应用中嵌入HTML代码,根据嵌入HTML要求的不同有以下两种方法: 1.Flex文本组件(Label.Text.TextArea)的htmlText属性支持一些基本的HTML代码,例如: <mx:TextArea>   <mx:htmlText>     <![CDATA[       <p align="center"><font size="15" color="#3399f

服务器-关于网页中嵌入视频播放插件的问题

问题描述 关于网页中嵌入视频播放插件的问题 我想知道,在网页中打开别人上传到服务器的资源是直接用本地的播放器打开好,还是在网页中用插件打开好,我上网看了一些插件但是只支持很少的格式,而我又不会转码,谁知道应该怎么解决? 解决方案 可以尝试用html5 转码的话用转码软件 解决方案二: 既然是网页,当然是用网页插件来播放比较好了,调用用户的本地浏览器就有损web开发的优势了,至于转码,推荐用ffmpeg,网上相关资料很多,最好建立一个带转换列表,xml或者数据库都行,写个服务程序调用ffmpeg进

一种在BIOS中嵌入应用程序的方法及实现

本文针对Award公司开发的计算机系统BIOS提出了一种嵌入应用程序的方法,其基本原理对别的品牌的BIOS也一样适用,仅需稍加修改.文中作者给出并讨论一个完整的例子程序,该程序已经通过实验验证.  正文 一. BIOS简述 这里所讲的BIOS是指计算机主板上的BIOS,是整个计算机的关键和灵魂,计算机一启动就是执行BIOS程序,它负责加电自检,初始化计算系统,响应用户对系统配置的修改,记录数据到CMOS中,将常驻程序库(Runtime Program)常驻于内存中,提供给系统和应用程序调用,经过

中移动手机浏览器已进入内测阶段

每经记者 陈未临 发自上海 OPhone操作系统计划的不尽如人意,加速了中移动移动互联网战略的调整.作为移动互联网的入口,手机浏览器成为合适的落点. 近期,市场传出消息称,中国移动将于近日发布自有品牌手机浏览器"冲浪".据一位接近中国移动的消息人士向 <每日经济新闻>透露,该浏览器当前已进入内测阶段.对于中国移动的该动作,该消息人士认为,"浏览器是互联网入口,运营商在手机浏览器上布局,可直接接触用户,加之未来云计算时代的到来,浏览器与操作系统之间的主次有可能调换,

java程序和“嵌入的浏览器”交互的问题

问题描述 java程序和"嵌入的浏览器"交互的问题★JAVA普通程序和JS交互比较完整用JDK1.6或者RHINO包"ScriptEngineManagerscriptEngineMgr=newScriptEngineManager();jsEngine=scriptEngineMgr.getEngineByName(""JavaScript"");jsEngine.put(""app"",thi