首 页 网络编程
网页制作 图形图象 操作系统 冲浪宝典
软件教学 认证考试

网络安全 网络办公 行业资讯 评测对比
您当前位置:站长天空 -> 冲浪宝典-> 局域网
一种全新的软件界面设计方法(摘)-.NET教程,Asp.Net开发
作者:网友供稿 点击:440
推荐
西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!可在线rar解压,自动数据恢复设置虚拟目录等.免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金
站内搜索
文章页数:[1] 
关键字:com myspy ie setuihanlder icustomdoc idochostuihandler getexternal

前言

作者在解决各种问题的时候喜欢首先使用c++ builder来尝试,这篇文章也是这样,但这并不影响其他开发工具的使用者阅读,因为这都是微软的开发技术,选择什么工具并不重要,我们理解了他的原理可以使用任何工具实现同样的功能。

正文

使用过vc.net的朋友可能知道,在vc.net中全新提供了一种基于web的界面设计方法,不过可能真正用到的人很少,至少我在国内的软件中没有看到过这样的界面设计方法。当初使用vc.net的时候就希望bcb的下个版本可以加入这样灵活的界面设计方法,但是到现在还没有等到,我想也不能一直这样等下去,于是就自己研究其中的实现方法,终于让我研究出来。这篇文章就是讨论这样方法,以及在软件设计设计中的可行性。

说了这么多,可能还有朋友不知道这样的界面到底有什么不同,有什么优点呢?如果你也有同样的好奇感的话,请你继续看下去。

在windows2000下,大家经常使用控制面板/添加、卸载软件的对话框就是基于这样的界面(xp下暂时不清楚),我不说出来可能很少有人知道-那个对话框整个就是个网页?什么你不相信?如果是网页为什么能和本地的计算机程序交互?为什么不能选择网页里面的文字?为什么不能弹出右键菜单?如果是网页,那它的html代码在那里?

为了证明上面的说法,我们需要一些特殊的软件,这个软件就是作者写的myspy,可以到作者的站点(http://siney.yeah.net)免费下载使用,我们可以从myspy的界面中看到添加/删除程序的对话框是个internet explorer_server,这说明它是个网页,


在myspy的web页面还可以看到这个页面的地址是:res://sp3res.dll/default.hta,




近一步使用myspy得到这个网页的代码(不能直接右键获取代码),部分如下:




<html xmlns:ctls><head><title id=arp>添加/删除程序</title>

<meta http-equiv=content-type content="text/html; charset=gb2312"><base href=res://appwiz.cpl/><link href="arp.css" type=text/css rel=stylesheet>

<style>>ctls\:places { behavior: url(places.htc); }ctls\:listbox { behavior: url(listbox.htc); }ctls\:accel { behavior: url(accel.htc); }.placesbar {background-color:threedshadow}.hide {display:none}.nonclientbackground { background-color: buttonface;}.header { padding-bottom: 5px;vertical-align: text-top; }.groupimage { margin-right: 5px;}.groupdesc {padding-left: 1em;padding-right: 1em;}.appnamerow {}.appimagetd {width: 20px; padding: 4px 2px 2px 2px;}.infopane { padding-top:4px; vertical-align: top;}.proplabel {width: 7em;padding-top: 2px;padding-bottom: 2px;padding-right: 3px;text-align: right;}.propvalue {width: 6em;text-align: right;padding-right: 7px;}.addproplabel {padding-top: 2px;padding-bottom: 2px;padding-right: 3px;text-align: right;}.addpropvalue {width: 13em;text-align: right;padding-right: 7px;}.buttondescpane { padding-top: 5px; padding-bottom: 7px;padding-right: 5px;}.buttonpane { width: 15em; padding: 5px; text-align: right;}.fakeanchor {cursor:hand;}#idclientcatname {font-weight: bold;padding-bottom: 1ex;}.disabled {color: graytext;}#idtblextendedprops.focus {color: highlighttext;}</style>





嗬嗬,是不是很神奇呢,这只是一个应用的例子,其实还有很多软件的界面使用了上面的方法来创建界面,比如norton antivirsu,ms visual studio.net,c# builder等。其实深入仔细思考的话,这样的界面最困难的是如何和本地代码交互,为什么在网页里点一个按钮能执行自己的代码呢?有过com编程经验的人,可能会想到用com编写一个外部对象,在网页中使用脚本创建这个对象,然后调用对象的方法似乎可以完成这样的功能?但是这里有很多不好的地方:

1. 需要注册com的本地运行安全,否则ie会有安全警告,这肯定是最终用户不愿意看到的;

2. 用户可以轻松从html代码里获得com对象的使用方法(就像上面用myspy获得代码一样),这样他们可以轻松使用你的com对象完成他们自己的界面,这样不够隐蔽,不安全。

也许还有更多不好的地方,但暂时作者没有想到,因为微软及其他软件公司都不是这样做的,他们也许知道更多。下面我们就来讨论一种既安全又隐蔽的实现方法。

从ie4开始,微软提供了一个icustomdoc接口,icustomdoc的setuihandler允许用户设置一个基于idochostuihandler的接口来接管界面处理器,在idochostuihandler提供了很多的虚拟方法,需要程序员来重载他们实现不同的定制功能,这里有一篇文章详细介绍了这些信息http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/hosting/wbcustomization.asp,在这里我们需要重载getexternal方法来扩展ie dom,如果我们成功的扩展了dom,那么我们就这可以这样编写html代码来实现与本地程序交互,例如:






<html>

<head>

<script language="jscript">

function myfunc()

{

external.helloworld(); //helloworld是我们扩展的方法

}

</script>

</head>

<body>

<input type="button" value="show hello world" onclick="myfunc();" />

</body>

</html>



helloworld就是我们扩展的一个方法,当点击按钮的时候external对象会调用helloworld方法调用本地代码,对于external对象则会调用上面提到的getexternal方法来查询是否提供了扩展,下面是如何实现getexternal方法来实现扩展external对象,代码如下:

class mydochandler :public idochostuihandler

{

long refcount;

public:

mydochandler() :refcount(1){ }

virtual hresult stdmethodcalltype queryinterface(refiid classid, void** intf) {

if (classid == iid_iunknown)

{

*intf = (iunknown*)this;

addref();

}

else if (classid == iid_idochostuihandler)

{

*intf = (idochostuihandler*)this;

addref();

}

else if (classid == iid_idispatch)

{

*intf = (idispatch*)this;

addref();

}

else

return e_nointerface;

return s_ok;

}

virtual ulong stdmethodcalltype addref() {

interlockedincrement(&refcount);

return refcount;

}

virtual ulong stdmethodcalltype release() {

interlockeddecrement(&refcount);

if (refcount == 0)

delete this;

return refcount;

}

//返回s_ok,屏蔽掉右键菜单

virtual hresult stdmethodcalltype showcontextmenu(

/* [in] */ dword dwid,

/* [in] */ point __rpc_far *ppt,

/* [in] */ iunknown __rpc_far *pcmdtreserved,

/* [in] */ idispatch __rpc_far *pdispreserved) {

return s_ok;

}

virtual hresult stdmethodcalltype gethostinfo(

/* [out][in] */ dochostuiinfo __rpc_far *pinfo) {

return e_notimpl;

}

virtual hresult stdmethodcalltype showui(

/* [in] */ dword dwid,

/* [in] */ ioleinplaceactiveobject __rpc_far *pactiveobject,

/* [in] */ iolecommandtarget __rpc_far *pcommandtarget,

/* [in] */ ioleinplaceframe __rpc_far *pframe,

/* [in] */ ioleinplaceuiwindow __rpc_far *pdoc) {

return e_notimpl;

}

virtual hresult stdmethodcalltype hideui( void) {

return e_notimpl;

}

virtual hresult stdmethodcalltype updateui( void) {

return e_notimpl;

}

virtual hresult stdmethodcalltype enablemodeless(

/* [in] */ bool fenable) {

return e_notimpl;

}

virtual hresult stdmethodcalltype ondocwindowactivate(

/* [in] */ bool factivate) {

return e_notimpl;

}

virtual hresult stdmethodcalltype onframewindowactivate(

/* [in] */ bool factivate) {

return e_notimpl;

}

virtual hresult stdmethodcalltype resizeborder(

/* [in] */ lpcrect prcborder,

/* [in] */ ioleinplaceuiwindow __rpc_far *puiwindow,

/* [in] */ bool framewindow) {

return e_notimpl;

}

virtual hresult stdmethodcalltype translateaccelerator(

/* [in] */ lpmsg lpmsg,

/* [in] */ const guid __rpc_far *pguidcmdgroup,

/* [in] */ dword ncmdid) {

return e_notimpl;

}

virtual hresult stdmethodcalltype getoptionkeypath(

/* [out] */ lpolestr __rpc_far *pchkey,

/* [in] */ dword dw) {

return e_notimpl;

}

virtual hresult stdmethodcalltype getdroptarget(

/* [in] */ idroptarget __rpc_far *pdroptarget,

/* [out] */ idroptarget __rpc_far *__rpc_far *ppdroptarget) {

return e_notimpl;

}

virtual hresult stdmethodcalltype getexternal(

/* [out] */ idispatch __rpc_far *__rpc_far *ppdispatch) {

*ppdispatch = new mycommandhandler();

return s_ok;

}

virtual hresult stdmethodcalltype translateurl(

/* [in] */ dword dwtranslate,

/* [in] */ olechar __rpc_far *pchurlin,

/* [out] */ olechar __rpc_far *__rpc_far *ppchurlout) {

return e_notimpl;

}

virtual hresult stdmethodcalltype filterdataobject(

/* [in] */ idataobject __rpc_far *pdo,

/* [out] */ idataobject __rpc_far *__rpc_far *ppdoret) {

return e_notimpl;

}

};


上面重载了showcontextmenu方法屏蔽掉右键菜单,使用户不能得到网页代码,关于getexternal是这样实现的:

virtual hresult stdmethodcalltype getexternal(

/* [out] */ idispatch __rpc_far *__rpc_far *ppdispatch) {

*ppdispatch = new mycommandhandler();

return s_ok;

}


可以看到只是简单返回了mycommandhandler对象,mycommandhandler必须继承自idispatch接口来实现支持自动化的调用方式,它是这样实现的:

class mycommandhandler : public idispatch

{

long refcount;

public:

mycommandhandler() :refcount(1){ }

virtual hresult stdmethodcalltype gettypeinfocount(

/* [out] */ uint *pctinfo){

return s_ok;

}

virtual hresult stdmethodcalltype gettypeinfo(

/* [in] */ uint itinfo,

/* [in] */ lcid lcid,

/* [out] */ itypeinfo **pptinfo){

return s_ok;

}

virtual hresult stdmethodcalltype getidsofnames(

/* [in] */ refiid riid,

/* [size_is][in] */ lpolestr *rgsznames,

/* [in] */ uint cnames,

/* [in] */ lcid lcid,

/* [size_is][out] */ dispid *rgdispid){

*rgdispid=1;

return s_ok;

}

virtual /* [local] */ hresult stdmethodcalltype 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==1)

{

messagebox(0,"hello world","hello",0); //place your code here

frmweb->edit1->text="hello world(这也可以?)";

}

return s_ok;

}

virtual hresult stdmethodcalltype queryinterface(refiid classid, void** intf) {

if (classid == iid_idispatch)

{

*intf = (idispatch*)this;

addref();

}

else

return e_nointerface;

return s_ok;

}

virtual ulong stdmethodcalltype addref() {

interlockedincrement(&refcount);

return refcount;

}

virtual ulong stdmethodcalltype release() {

interlockeddecrement(&refcount);

if (refcount == 0)

delete this;

return refcount;

}

};


如果大家了解一些com知识,我们知道这里关键的是getidsofnames和invoke方法的实现,因为自动化对象只能通过这样的方式来调用,而不能使用函数指针直接调用虚拟方法,getidsofnames查询指定的函数名的调用id,就是说如果有一个方法是“helloworld”,那么它会先调用getidsofnames方法来查询这个方法是否支持,如果支持则给出该方法的调用id(通过修改rgdispid[out]参数),如果不支持则返回e_notimpl,他的实现简单如下:

virtual hresult stdmethodcalltype getidsofnames(

/* [in] */ refiid riid,

/* [size_is][in] */ lpolestr *rgsznames,

/* [in] */ uint cnames,

/* [in] */ lcid lcid,

/* [size_is][out] */ dispid *rgdispid){

*rgdispid=1;

return s_ok;

}


这里我们假定了只有一个扩展函数helloworld,如果有多个,我们需要比较rgsznames参数的函数名返回不同的调用id,有了调用id,实现invoke方法就很简单了:

virtual hresult stdmethodcalltype 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==1)

{

messagebox(0,"hello world","hello",0); //place your code here

frmweb->edit1->text="hello world(这也可以?)";

}

return s_ok;

}


根据dispidmember的不同实现不同的代码,如果方法是有参数的可以在pdispparams中取得,如果有什么不明白可以参考msdn和一些com的书籍,这里就不详细解释了。

最后我们要做的就是使我们的浏览器知道我们扩展了external,代码如下:

dochandler = new mydochandler;

webbrowser->navigate(widestring(l"e:\\projects\\extweb\\ext.htm"));

while(webbrowser->busy)

application->processmessages();

icustomdoc *custdoc;

webbrowser->document->queryinterface(&custdoc); //取得icustomdoc接口

if (custdoc)

{

custdoc->setuihandler(dochandler); //设置我们自己的界面处理器

custdoc->release();

}


注意上面的粗体“我们的浏览器”,因为这样的扩展仅针对与自己程序里使用webbrowser控件,不影响ie本身的扩展,也就是说那个ext.htm文件只能在我们的程序中有效,就算其他用户得到了这段htm代码也不能正常运行的,如果你想测试,你得到的是:


因为他们并不知道如何扩展external对象,这点就解决了刚才我们说的使用com的问题。

说句实话设计这样界面还是有一定难度的,那么它在实际开发中到底有什么好处呢?我想至少有以下几点:

1. 界面设计和程序逻辑设计分离,美工可以和程序员一起工作,界面设计再也不是没有审美细胞程序员的问题;

2. 轻松实现skin功能,界面的改变不需要重新编译代码,只需要换一个不同htm代码文件就可以;

3. 再也无法使用spy工具获得窗体handler做各种hook,使你的程序运行的更安全;

4. 充分使用ie现有技术,搭建功能更强大的软件;

5. 使你的软件看起来更酷,更专业。


文章整理:站长天空 网址:http://www.z6688.com/
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

文章页数:[1] 


放大字体显示 缩小字体显示 打印文章 推荐给朋友
热门文章
·Java串行端口技术协议-JSP教程,Java技巧及代码
·VB6中水晶报表(Crystal Report 4.6)的使用经验-.NET教程,报表/图形/Office
·一种全新的软件界面设计方法(摘)-.NET教程,Asp.Net开发
·RSA加密算法在VB中的实现-.NET教程,VB.Net语言
·ubb代码转化html代码-ASP教程,脚本编码
·用C#创建可拖动窗体-.NET教程,C#语言
·ASP技术访问WEB数据库-ASP教程,数据库相关
·浅谈基站蓄电池的维护与保养
·VB中枚举指定目录下所有文件的方法-.NET教程,评论及其它
·ADO.NET 2.0 Dataset和Datatable 新功能新特性-.NET教程,Asp.Net开发
最新文章
·解决局域网遭遇盗用mac地址上网的问题_局域网教程
·photoshop将照片处理为艺术插画特效_photoshop教程
·狂人!用日ip不到500的站一年赚了10万元_网赚技巧
·李治国:急躁公司做不了分类信息_站长访谈
·李彦宏教你创业实用的七大招_站长心得
·新站如何被百度快速的收录_站长心得
·影响搜索排名的77种因素_站长心得
·网站站长把你懂的东西卖给不懂的人,才能赚钱_站长心得
·个人站点提高访问量谋略1_站长心得
·做adsense的一些经验_google推广
相关主题
  • 一种全新的分组数据业务——cdpd
  • 一种全新的分组数据业务——cdpd
  • 西部数码虚拟主机

    友情链接
    CNNIC 西部数码
    万网 自助建站
    虚拟主机 asp空间
    域名注册 域名
    域名申请 主页空间
    论坛空间 网站空间
    国际域名 虚拟空间
    空间租用 DDOS防火墙
    成都主机托管 四川主机托管
    主机租用 服务器租用
    网站目录 自助建站
    虚拟主机 网址大全
    软件下载
    自助链接
    虚拟主机资讯 特价虚拟主机
    版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
    关于我们:站长天空:专业提供最新的站长资讯、在线教程、虚拟主机权威评测、虚拟主机性能对比、网站制作教程,开发教程,站长工具。包括网页制作教程、冲浪宝典、编程参考、操作系统、软件教学、行业动态等。
    特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
    发表评论 打印  刷新     关闭