国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

一種全新的軟件界面設計方法(摘)

2024-07-21 02:16:18
字體:
來源:轉載
供稿:網友
關鍵字:com myspy ie setuihanlder icustomdoc idochostuihandler getexternal

前言

作者在解決各種問題的時候喜歡首先使用c++ builder來嘗試,這篇文章也是這樣,但這并不影響其他開發(fā)工具的使用者閱讀,因為這都是微軟的開發(fā)技術,選擇什么工具并不重要,我們理解了他的原理可以使用任何工具實現同樣的功能。

正文

使用過vc.net的朋友可能知道,在vc.net中全新提供了一種基于web的界面設計方法,不過可能真正用到的人很少,至少我在國內的軟件中沒有看到過這樣的界面設計方法。當初使用vc.net的時候就希望bcb的下個版本可以加入這樣靈活的界面設計方法,但是到現在還沒有等到,我想也不能一直這樣等下去,于是就自己研究其中的實現方法,終于讓我研究出來。這篇文章就是討論這樣方法,以及在軟件設計設計中的可行性。

說了這么多,可能還有朋友不知道這樣的界面到底有什么不同,有什么優(yōu)點呢?如果你也有同樣的好奇感的話,請你繼續(xù)看下去。

在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>





嗬嗬,是不是很神奇呢,這只是一個應用的例子,其實還有很多軟件的界面使用了上面的方法來創(chuàng)建界面,比如norton antivirsu,ms visual studio.net,c# builder等。其實深入仔細思考的話,這樣的界面最困難的是如何和本地代碼交互,為什么在網頁里點一個按鈕能執(zhí)行自己的代碼呢?有過com編程經驗的人,可能會想到用com編寫一個外部對象,在網頁中使用腳本創(chuàng)建這個對象,然后調用對象的方法似乎可以完成這樣的功能?但是這里有很多不好的地方:

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的問題。

說句實話設計這樣界面還是有一定難度的,那么它在實際開發(fā)中到底有什么好處呢?我想至少有以下幾點:

1. 界面設計和程序邏輯設計分離,美工可以和程序員一起工作,界面設計再也不是沒有審美細胞程序員的問題;

2. 輕松實現skin功能,界面的改變不需要重新編譯代碼,只需要換一個不同htm代碼文件就可以;

3. 再也無法使用spy工具獲得窗體handler做各種hook,使你的程序運行的更安全;

4. 充分使用ie現有技術,搭建功能更強大的軟件;

5. 使你的軟件看起來更酷,更專業(yè)。

商業(yè)源碼熱門下載www.html.org.cn

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 峨边| 定襄县| 旬阳县| 南京市| 大宁县| 璧山县| 沙雅县| 清镇市| 兴海县| 泗洪县| 梁山县| 巫山县| 广平县| 泌阳县| 额尔古纳市| 昆山市| 贵德县| 丹棱县| 安多县| 芦溪县| 来宾市| 略阳县| 阳西县| 虎林市| 邛崃市| 容城县| 长泰县| 新津县| 阆中市| 高台县| 遵化市| 二连浩特市| 湾仔区| 乌什县| 喜德县| 赫章县| 涟水县| 牟定县| 登封市| 甘谷县| 尚义县|