c#内嵌浏览器,c语言写浏览器
C (1)中嵌入的ie浏览器概述——ie边框和上下文菜单
最近在项目中使用html作为接口,做了一些关于在wxwidget中嵌入浏览器的细节。mfc基本类似。由于wxwidget已经做了一个包wxie,所以在开发过程中也遇到了很多问题。这里有一个总结:
Ie边框和上下文菜单
一般嵌入程序的浏览器页面都会有灰色边框,放在程序里很难看。目前网上比较流行的添加css的方式是:
正文{
边框:0;
}
但是这种方法的缺点是必须删除页面头部的dtd声明。网上的错误提法类似于下面的链接:http://www.faceker.com/200801/webbrowser-no-border.html说到这里,就把头改成:
!DOCTYPEHTMLPUBLIC -//W3C//dtdhtml 4.01 transitional//EN
但是,目前很多页面使用的是较新的css。这个改了之后,页面就不能正常显示了。让网页美工改风格?有点难。
后来我继续查阅资料,找到了一个更好的方法,就是重载IDocHostUIHandler接口,其中实现了以下几个部分:
HRESULTSTDMETHODCALLTYPEFrameSite:get hostinfo(DOCHOSTUIINFO * pInfo)
{
pInfo-CB size=sizeof(DOCHOSTUIINFO);
pInfo-dw flags=DOCHOSTUIFLAG _ NO 3d border 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
开关(m_contextMenuMode)
{
casekDefaultMenuSupport:
打破;
casekNoContextMenu:
result=S _ OK
已处理=真;
打破;
casekTextSelectionOnly:
如果(dwID!=上下文菜单文本选择)
{
result=S _ OK
已处理=真;
}
打破;
casekAllowAllButViewSource:
if(dwID==上下文菜单默认值)
{
//result=ModifyContextMenu(dwID,ppt,pcmdtReserved);
已处理=真;
}
打破;
casekCustomMenuSupport:
if(dwID==上下文菜单默认值)
{
//result=CustomContextMenu(ppt,pcmdtReserved);
已处理=真;
}
打破;
}
如果(!已处理)
{
result=S _ FALSE
}
返回结果;
}
这里不仅可以控制右键菜单显示,m_contextMenuMode=knocontext菜单,还可以自定义菜单显示,m _ context菜单模式=其他值。因为还不需要自定义菜单,所以这里没有实现。
如果使用wxie,只需将这个接口添加到FrameSite类中,不关注的接口会直接返回S_FALSE或E _ NOTIMPL
如果使用sdk或mfc,可以调用IOleObject的SetClientSite方法来设置继承IOleClientSite和IDocHostUIHandler的接口。
嵌入式ie浏览器概述C(2)-双向通信
第一步解决边框和上下文菜单的问题,第二步解决C程序和html页面的交互问题。初步的想法是用C更新页面内容来完成c-html通信,通过BeforeNavigate2接口拦截页面的url地址来完成html- c通信。然而,这种方法有以下缺点:
(1)C-HTML的问题在于导致了C代码的复杂性,需要通过C代码来完成页面的生成。如果修改页面,会产生大量的工作量。虽然使用了模板的方法来解决问题,但是还是比较繁琐,而且在频繁交流的时候会导致页面频繁刷新,产生其他问题。
(2)HTML-C的问题是不方便传递参数,解析参数,获取返回值,在脚本中调用。
为了解决这些问题,谷歌之后找到了解决问题的办法:
(1) c-html,可以通过调用页面脚本方法实现。调用方法如下:
wxVariantwxIEHtmlWin:ExecScript(const wx string fun,conststd:vector wxString params)
{
wxVariantresult(false);
如果(!m _网络浏览器。Ok())
返回结果;
//getdocumentdispatchinterface
IDispatch * iDisp=NULL
HRESULThr=m _ webBrowser-get _ Document(iDisp);
如果(hr!=S_OK)
返回结果;
//QueryforDocumentInterface
wxAutoOleInterface ihtml document 2 HD(IID _ ihtml document 2,iDisp);
iDisp-Release();
如果(!高清. Ok())
返回结果;
IDispatch * spScript
HR=HD-get _ Script(sp脚本);
如果(失败(小时))
返回结果;
BSTRbstrMember=wxConvertStringToOle(fun);
DISPIDdispid=NULL
HR=sp script-GetIDsOfNames(IID _ NULL,bstrMember,1,
LOCALE_SYSTEM_DEFAULT,dispid);
如果(失败(小时))
{
返回结果;
}
//放置参数
DISPPARAMSdispparams
memset( dispparams,0,sizeofdisparams);
显示参数。货物=参数。size();
显示参数。rg varg=新变量[显示参数。货物];
显示参数。cname dargs=0;
for(inti=0;我参数。size();我)
{
CComBSTRbstr=wxConvertStringToOle(params[params。size()-1-I]);
//回读
bstr .CopyTo( dispparams.rgvarg[i]).bstr val);
dispparams.rgvarg[i].VT=VT _ BSTR;
}
excepinfo
memset( excepInfo,0,sizeofexcepInfo);
变量
UINTnArgErr=(UINT)-1;//initializetoinvalidarg
//调用JavaScriptfunction函数
hr=spScript- Invoke(dispid,IID_NULL,0,
调度方法,调度参数,
varRet,excepInfo,narg err);
删除[]个显示参数。rg varg
如果(失败(小时))
{
返回结果;
}
wxconvertoletovant(varRet,result);
返回结果;
}
这个方法实现了C对页面脚本调用,而且参数个数可以任意。比如页面脚本是:
functionfun(a,b,c)
{
}
C中的调用方法是:
STD:vector wx字符串参数;
参数。push _ back( a );
参数。push _ back( b );
参数。push _ back( c );
xxx- ExecScripts(fun ,params);
还可以获得脚本返回的结果。
(2) html- c语言语言通过脚本的窗口。外部方法,首先,在前文提到过的IDocHostUIHandler接口中,实现方法:
HRESULTSTDMETHODCALLTYPEFrameSite:get external(IDispatch * * PP分派)
{
IDispatch * pDisp=m _ window-get external();
中频(pDisp)
{
pDisp-AddRef();
* ppDispatch=pDisp
}
退货_确定
}
其中m _ window-get外部();
返回的是自定义的一个自动化接口接口类:
/*
* IDispimp .H
*IDispatch
*
*版权所有(c)1995-1999年微软公司,保留所有权利
*/
#ifndef_IDISPIMP_H_
#define_IDISPIMP_H_
#包含oaidl.h
classCustomFunction
类cimpidispatch:publicIDispatch
{
受保护:
ULONGm _ cRef
公共:
CImpIDispatch(void);
~ CImpIDispatch(void);
stdmethodpqueryinterface(ref iid,void * *);
STDMETHODIMP _(ULONG)AddRef(void);
STDMETHODIMP _(ULONG)Release(void);
//IDispatch
stdmethodmpgettypeinfocount(UINT * pctinfo);
stdmethodmpgettypeinfo(/*[in]*/UINTiTInfo,
/*[in]*/LCIDlcid,
/*[out]*/ITypeInfo * * ppTInfo);
STDMETHODIMPGetIDsOfNames(
/*[in]*/REFIIDriid,
/*[size _ is][in]*/LPOLESTR * rgszNames,
/*[in]*/UINTcNames,
/*[in]*/LCIDlcid,
/*[size _ is][out]*/DISPID * rg DISPID);
STDMETHODIMPInvoke(
/*[in]*/DISPIDdispIdMember,
/*[in]*/REFIIDriid,
/*[in]*/LCIDlcid,
/*[in]*/word标志,
/*[out][in]*/disp params * pdisp params,
/*[out]*/VARIANT*pVarResult,
/*[out]*/excel info * pExcepInfo,
/*[out]*/UINT * puArgErr);
voidsetCustomFunction(自定义函数* fun){ m _ fun=fun;}
私人:
自定义功能* m _ fun
};
#endif//_IDISPIMP_H_主要实现以下两个方法:
wx string cszCB _ custom function=wxT( CB _ custom function );
#定义DISPID _ CB _函数3
stdmethodcimpdispatch:GetIDsOfNames(
/*[in]*/REFIIDriid,
/*[size _ is][in]*/OLECHAR * * rgszNames,
/*[in]*/UINTcNames,
/*[in]*/LCIDlcid,
/*[size _ is][out]*/DISPID * rgDispId)
{
HRESULThr
尤因蒂
//assumesmedegreeofsccess
hr=NOERROR
for(I=0;i cNamesi ){
wxStringcszName=rgszNames[I];
if(csz name==cszCB _ custom函数)
{
rg DISPID[I]=DISPID _ CB _ custom函数;
}
否则{
//oneormorearenunknownsosetthereturncode相应地
HR=ResultFromScode(DISP _ E _未知名称);
rg DISPID[I]=DISPID _ UNKNOWN;
}
}
returnhr
}
stdmethodmpsimpidispatch:Invoke(
/*[in]*/DISPIDdispIdMember,
/*[in]*/REFIID/*riid*/,
/*[in]*/LCID/*lcid*/,
/*[in]*/word标志,
/*[out][in]*/disp params * pdisp params,
/*[out]*/VARIANT*pVarResult,
/*[out]*/excel info */* pExcepInfo */,
/*[out]*/UINT*puArgErr)
{
if(DISPID成员==DISPID _ CB _ custom函数)
{
if(wFlags DISPATCH_PROPERTYGET)
{
if(pVarResult!=空)
{
variant init(pVarResult);
v _ VT(pVarResult)=VT _ BOOL;
v _ BOOL(pVarResult)=true;
}
}
if(wFlags DISPATCH_METHOD)
{
//argumentscomeinreverseorder
//forsomereason
如果(!m_fun)返回_ OK
wxStringarg1,arg2
if(pDispParams- cArgs 1)返回_ FALSE
wxStringcmd=pdisp params-rg varg[pdisp params-cArgs-1]。bstrVal
STD:vector wx string args;
if(pDispParams- cArgs 1)
{
for(inti=pDispParams-cArgs-2;I=0;我-)
args . push _ back(pdisp params-rg varg[I])。bstr val);
}
wxStringre=m_fun- execute(cmd,args);
if(pVarResult!=空)
{
variant init(pVarResult);
v _ VT(pVarResult)=VT _ BSTR;
wxVariantwVar(re);
VariantToMSWVariant(wVar,* pVarResult);
}
}
}
returnS _ OK
}
CustomFunction定义如下:
#pragmaonce
#包含wx/wx.h
#包含矢量
classCustomFunction
{
公共:
自定义功能(无效)
{
}
virtual~CustomFunction(void)
{
}
virtualwxstring execute(const wxString cmd,const STD:vector wxString args)=0;
};然后,只要在自己的类中继承这个接口,就可以收到脚本的调用请求。
在脚本中编写函数:
窗户。_callFun=function()
{
var fun= window . external . CB _ custom function(;
for(I=0;一.论据长度;我)
{
如果(我!=0)
fun=fun ,;
fun=fun / arguments[I]/ ;
}
fun=fun )’;
//警戒(好玩);
return(eval(fun));
}
然后写在它被调用的地方:
_callFun(fun , param1 , param2 ,);
可以调用C的函数,得到返回值,这样就解决了HTML-C的通讯问题。
解决了双向沟通之后,页面就不需要刷新了。Web设计人员和C程序员只要分别定义通信接口和实现接口方法,就可以完成接口功能。
C (3)内嵌ie浏览器总结WXIE的bug解决和最终效果显示
要解决前两个问题,我心里想:太好了。解决了这两个问题,下一步做界面就容易多了。不像以前,直接用mfc或者第三方库,真的很难做出好看的效果。嗯,编译运行,发现效果不错。然而,当我意识到里面的脚本时,我发现不对劲,没有响应键盘消息。并在页面文本框中按tab键,光标不是运行到下一个文本框,而是丢失。之前也有这种现象,只是我忙着解决之前的问题,没有注意到。一切都结束了。你不能放弃你以前所有的工作吧?这是个大问题。
Google聊了很久,问了一个朋友,还是不知道为什么。因为wxIE和嵌入式浏览器本身都是比较偏的问题,确实很难找到答案。
每片乌云都有一线光明。最后在google上找到了类似的问题,得到的答案是这是wxIE的bug,已经在wxPython的项目中解决了。下载编译后,试着运行一下,就搞定了。
但是要和之前的修改合并,在合并的过程中又发现了一个问题。上一篇文章提到,通过IOleObject接口设置IDocHostUIHandler方法,我就是从这个方法开始的。合并结果后,键盘消息被找到或没有响应。
时间久了,发现wxIE已经实现了IOleClientSite接口。我自己设置了接口,替换了wxIE的,导致结果异常。经过一番调整,终于正常了。
自此,C程序与html接口的基础工作告一段落。下面是界面作品和页面脚本。我希望我们不要再遇到任何问题。
我在这里把这些问题记录下来,以备将来参考,也希望其他朋友不要再遇到我同样的烦恼。
剪下如下界面图:
这是一个对话框。完成前面的基础工作后,只要设计师设计好页面,我们几分钟就可以继承到C中,然后再花点时间写界面和脚本。和原来的mfc界面相比,不知道节省了多少时间。这个界面比较简单,但是只要是能设计的,我们都可以集成。
感兴趣的朋友也不妨试试这个接口方法。
C (4)嵌入式ie浏览器概述——对话框拖动
这个问题我之前忘记写了,就是对话框的拖动。就像我上图所示的对话框一样,一般的windows对话框都可以通过拖动标题栏来移动,但是我们这里没有任何原创的标题栏,只有html页面。怎么拖呢?似乎有一些麻烦。
苦苦思索后,我想到了一个办法,通过前台接口给C发送指令,让C移动窗口,计算页面上的拖动距离。把html页面拖进去相对简单。在C中移动意味着调用MoveWindow。因为之前的通讯方式比较灵活,所以实现这个功能并没有花太多时间。但是运行的时候就不太对了,拖拽过程中拖尾现象太明显了。可能C一直调用MoveWindow重绘,效率很低。这就麻烦了。这时我突然想到,普通对话框被拖动的时候,是一个虚拟的盒子移动到了那里,而原来的对话框并没有移动。放开鼠标后,对话框就移动了。你能做到吗?但windows如何实现这种方法的细节不得而知。怎么做?
谷歌还是不错的。经过一番搜索,我找到了答案:
voidTooltipDlg:move win(const STD:vector wx string args)
{
if(args.size() 2)返回;
longx,y;
参数[0]。托龙(x);
args[1]。托龙(y);
intix,iy;
IX=x;
iy=y;
ClientToScreen( ix,iy);
* SendMessage((HWND)this-GetHWND()、WM_NCMOUSEMOVE、HTCAPTION、MAKELPARAM(ix,iy));
}
搞定,简单,真没想到这么简单。运行后发现真的和windows的对话框运动一模一样,太棒了。
今天全力总结了前几天的工作,收工真的有点累。但是这些东西真的是非常规的方法,很难找到解决问题的方法。先记录到这里吧,免得以后找不到。以前有很多知识是用了又丢的。也希望能给有类似疑问的朋友一点帮助。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。