不能在客戶端建立的物件的研究
張元康(奉化方橋中學 浙江奉化 315500)
【摘要】元件技術伴隨電子商務的發展,近些年來進展很快,已成為電子商務應用開發技術的乙個主流方向。分層開發的技術思想使軟體開發有了明確的分工,從而有效地降低了開發的難度。為了減少層次之間的耦合性,經常需要在伺服器端實現客戶端不可直接建立的com(元件物件模型)物件。通過對com物件的內部分析及微軟提供的活動模板庫(atl)中的ccomobject類的詳細分析,敘述了在伺服器端正確建立只能由客戶端訪問但不能由客戶端建立的com物件的過程。
軟體系統的複雜性不斷增長,軟體人員的頻繁流動和軟體行業的激烈競爭迫使軟體企業提高軟體質量,積累和固化知識財富,並盡可能地縮短軟體產品的開發周期。於是集軟體復用、分布式物件計算、企業級應用開發等技術為一體的基於構件的軟體開發(cbsd,component based sortware development)應運而生。這種技術支援組裝式軟體的復用,大大提高了軟體生產效率和軟體質量。這種
應用程式體系結構可以分為前端層,中間層和後端層。中間層實現了乙個物件模型。在前端層只涉及使用者介面的設計,不用關心業務流程。所以物件模型中一般都存在不能由前端層直接建立的com類。例如,在微軟的office系統中就存在大量的不可由客戶建立的類。本文將介紹如何在伺服器端實現對於客戶來說是不可直接建立的但又是可訪問的com物件。
1 實現原理
由於客戶端不能直接建立這種物件,所以在物件模型中必須提供乙個用於建立這種物件的屬性和方法,該屬性和方法由層次結構中的其他物件輸出。而且,由於物件是在伺服器端**中定義,所以伺服器**不必要呼叫cocreateinstance方法去建立物件。
因為這種方法會使伺服器先訪問com系統,而com系統又需要定位物件的製造工廠並載入它,然後再呼叫製造工廠的cocreateinstance方法。這樣
效率會很低。而服務端的**應該盡可能地使用最高效的方式。使用下面介紹的方法把乙個物件變成乙個com客戶不可建立的物件,同時也禁止了伺服器端呼叫cocreatinstance來建立該物件。
首先要將相應的物件變成乙個對於com客戶不可建立但又是可訪問的。這通過下面兩個步驟來實現。
(1)在atl服務端的物件對映object_entry入口中去掉或注釋掉物件,可以使客戶無法呼叫cocreateinstance去建立該類例項。
(2)在型別庫的coclass定義中加入noncreatable屬性,可以防止微軟的intellisense技術提供給出的語句完成提示系統顯示出coclass類的名字。
因為不能在伺服器呼叫cocreateinstance方法去建立該com物件,所以採用直接建立伺服器端定義的物件。首先應該理解伺服器內部com物件怎麼建立和有關ccomobject物件的使用,否則可能會呼叫了過多的addref方法而導致介面洩露,或返回了乙個直接指向ccomobject物件的指標,而該指標卻無法被客戶端呼叫。因為實現com物件的c++類不是乙個com物件。如果用new來建立乙個物件,然後把它交給呼叫者,這樣不僅c++類的記憶體布局不對,而且它也沒有實現iunknown介面。這可以通過活動模板庫來解決。活動模板庫(atl)封裝了乙個模板類ccomclass,它提供了iunknown的實現,只要對應com物件的c++類的名字作為模板引數。因此可以通過建立乙個ccomclass類的例項來實現。**如下。
ccomobject*pcats;
hresult hr;
hr=ccomobject::createinstance(&pcats);
if(failed(hr)) return hr。
pcats指向的物件並不是乙個com物件。它是乙個普通的c++物件,只是它擁有繼承來的實現iunknown介面方法的成員函式,被建立時的「引用計數」為0。它所有的成員變數和函式都可以從pcats指標直接訪問。但為了使客戶可以訪問該物件,必須獲得乙個com介面指標。這可以通過呼叫ccomobject的queryinte***ce方法來實現。ccomobject通過模板技術從引數類中派生出自身。當然,這將導致對該物件的一次addref呼叫,但這是對該物件的第一次addref呼叫,因為初始化的引用計數為0。如果初始化時ccomobject的引用計數已經為1,那麼需要呼叫release將引用計數平衡,以免發生介面洩露。如果不理解伺服器內部com物件怎麼建立和有關ccomobject物件的使用,可能會呼叫了過多的addref方法而導致介面洩露,也有可能返回了乙個直接指向ccomobject物件的指標,但是該指標無法被客戶端呼叫。相應的**如下。
lpdispatch pdisp;
pcats->queryinte***ce(iid_idispatch,(void **)&pdisp);
pval->vt=vt_dispatch;
pval->pdispval=pdisp;
return s_ok。
2 示例分析
下面通過乙個簡單物件模型的實現來說明,它的實現目標為:乙個category物件,該物件只有乙個唯讀屬性categoryname;乙個categories物件,該物件是category的集合物件,它包含乙個item方法和乙個count屬性。客戶程式可以使用item對集合進行遍歷。
乙個應用程式或頂層物件warehouse,它被用做categories集合的容器。該物件通過唯讀屬性categories來輸出categories集合。該物件使用唯讀屬性categories來輸出categories物件。
(1)atl wizard產生的物件對映的檔案(waredemo.cpp)。修改如下。
begin_object_map(objectmap);
object_entry(clsid_warehouse, cwarehouse);
//object_entry(clsid_categories, ccategories);
//object_entry(clsid_category, ccategory);
end_object_map()。
(2)開啟waredemo.idl,在ccategories類、ccategory類中加入屬性noncreatable,修改如下。
uuid(96f20d20-a4fe-11d2-9a2d-204c4f4f5020);
helpstring("categories class");
noncreatable( 使客戶端在引用物件時,不能呼叫cocreateinstance)。
(3)實現warehouse物件、categories物件和category物件。按如下步驟。
1)往warehouse類中加入categories屬性,選擇lpdispatch為property type,並將function type框中的put function的選中標記去掉。單擊ok按紐關閉對話方塊。atl wizard將在cwarehouse類中產生乙個函式get_categories,加入的**如下。
#include "categories.h"
stdmethodimp cwarehouse::get_categories(lpdispatch *pval)
2)categories集合是乙個category類物件的可列舉的集合。categories集合將包含乙個count屬性和item屬性。count屬性將返回集合中的元素個數。item屬性將被客戶**用於在集合的category物件中遍歷。在icategories介面中加入乙個long型別的唯讀屬性count及乙個叫item的引數化屬性。在categories.cpp中加入**如下。
#include "waredemo.h"
#include "categories.h"
#include "category.h"
const int gnnames = 5;
const char* gszcatnames[gnnames] = ;
stdmethodimp ccategories::get_count(long *pval)
stdmethodimp ccategories::get_item(long index,variant *pval)
ccomobject*pcategory;
lpdispatch pdisp;
hresult hr;
hr = ccomobject::createinstance (&pcategory);
if (failed(hr)) return hr;
pcategory->m_bstrcategoryname = gszcatnames[index-1];
pcategory->queryinte***ce (iid_idispatch, (void **) &pdisp);
pval->vt = vt_dispatch;
pval->pdispval = pdisp;
return s_ok; }
3)category類實現相對簡單。它只有乙個叫做categoryname的唯讀屬性,以bstr作為property type。實現過程與上面相同。
3 結束語
在實際工程中,幾乎所有的物件模型中都存在不能由使用者介面模組直接建立的類。尤其是在大型專案中,軟體是由很多開發人員共同協作開發的,所以就需要在伺服器端實現對於客戶來說是不可直接建立的但又是可訪問的com物件,使開發人員之間的耦合性降低。例如,過期罰款物件是用來描述當借書人沒有按期歸還書籍時應收的罰金,只有當借書人在沒有按期歸還時,物件模型和com+伺服器才會建立乙個過期罰款物件。所以不應該允許使用者介面模組建立乙個過期罰款物件,因為這樣不僅很難達到介面和業務的分離而且還會導致一些令人困惑的問題。所以該技術在使用atl開發工程時很有實際價值。
(收稿日期:2004-02-24 email:[email protected])
參 考 文 獻
[1]田雨,劉雲,鄭軼峰.visual c++ dcom開發指南[m].北京:清華大學出版社,2000.56~68
[2]潘愛民.com原理和應用[m].北京:清華大學出版社,1999.10~230
核心研究 客戶端中的執行緒
static class w extends iwindow.stub為了驗證這一點,可以在eclipse中新建乙個hello android的程式,然後以debug的方式執行,在debug視窗中會看到如圖所示的介面。自定義thread和ui執行緒的區別在於,ui執行緒是從activitythrea...
RestTemplate HTTP客戶端的使用
1 resttemplate簡介 1 spring框架提供的http請求客戶端,簡化了http請求的 編寫 2 提供了泛型的支援,以及返回結果的自動型別轉換,大大提高 復用性,以及 的簡化 2 resttemplate的常用方法 1 getforentity 傳送乙個http get請求,返回的re...
訊息佇列的簡單實現(客戶端A,客戶端B)
下面是兩個相互通訊程序的簡單實現,乙個代表客戶端a,乙個代表客戶端b 客戶端a 傳送typeb型別的資料到客戶端b 讀取傳送到客戶端a的typea型別的資料 include include include include include include include include include...