COM組件設(shè)計與應(yīng)用(十八)——屬性包 下載源代碼 <object classid="clsid:232E456A-87C3-11D1-8BE3-0000F8754DA1" id="MonthView1"> <param name="_ExtentX" value="9393"> <param name="_ExtentY" value="4974"> <param name="_Version" value="393216"> <param name="ForeColor" value="0"> <param name="MaxSelCount" value="7"> <param name="MonthColumns" value="1"> <param name="CurrentDate" value="38632"> <param name="MaxDate" value="2958465"> <param name="MinDate" value="-53688"> </object>以文本方式保存組件屬性,,比較直觀,、容易修改,上面 HTML 示例中的 <param name="屬性名" value="值"> 就很清晰,。下面開始介紹如何在組件中實現(xiàn) IPersistPropertyBag 接口,。 二,、組件的實現(xiàn) (1)vc6.0 開發(fā)步驟 1、建立一個工作空間(WorkSpace),。 2,、在這個工作空間中,建立 ATL 工程,,示例程序工程為 Simple18,。 3、增加 ATL 對象類,,默認全部選項,。示例程序中的 ATL 對象短名稱是 Property。 4,、增加一些屬性,。在以前的章回中,我們只介紹了增加接口函數(shù)的方法,,由于今天是首次增加接口屬性,,所以稍微細致一些。步驟是,,在ClassView卡片中選擇接口(IProperty)后,,執(zhí)行鼠標右鍵菜單"Add Property..." 5、增加 BSTR 類型的接口屬性 str,,同樣的方式,,再增加一個 long 型的接口屬性 interger。在示例程序中,,這兩個屬性其實只為演示,,并沒有實際的意義。 6,、接口中的屬性,,多數(shù)情況下會對應(yīng)對象內(nèi)部的一個成員變量,因此我們現(xiàn)在要添加成員變量,。選擇對象類名,,執(zhí)行鼠標右鍵菜單"Add Member Variable...." 7、添加兩個成員變量,,一個是 CComBSTR m_str 對應(yīng)于接口屬性 str,;另一個是 long m_integer 對應(yīng)于接口屬性 integer。 (2)vc.net 開發(fā)步驟 1,、建立一個空白解決方案,。 2、在解決方案中,新增 ATL 項目,。示例程序中項目名稱叫 Simple18,, 注意不要選擇“屬性化編程”方式。 3,、添加 ATL 類,。選擇 “ATL 的簡單對象”。默認全部選項,。示例程序中 ATL 類短名稱為 Property,,類名稱為 CMyProperty。(注1) 4,、 增加一些屬性,。在以前的章回中,我們只介紹了增加接口函數(shù)的方法,,由于今天是首次增加接口屬性,,所以稍微細致一些。步驟是,,在類視圖卡片中選擇接口(IProperty)后,,執(zhí)行鼠標右鍵菜單"添加屬性..." 5、增加 BSTR 類型的接口屬性 str,,同樣的方式,,再增加一個 long 型的接口屬性 interger。在示例程序中,,這兩個屬性其實只為演示,,并沒有實際的意義。 6,、接口中的屬性,,多數(shù)情況下會對應(yīng)對象內(nèi)部的一個成員變量,因此我們現(xiàn)在要添加成員變量,。選擇對象類名,執(zhí)行鼠標右鍵菜單"添加變量...." 7,、添加兩個成員變量,,一個是 CComBSTR m_str 對應(yīng)于接口屬性 str;另一個是 long m_integer 對應(yīng)于接口屬性 integer,。 (3)實現(xiàn)代碼 至此,,我們組件的框架已經(jīng)完成,下面該完成函數(shù)函數(shù)的實現(xiàn)了: STDMETHODIMP Cxxx::get_str(BSTR* pVal) { *pVal = m_str.Copy(); return S_OK; } STDMETHODIMP Cxxx::put_str(BSTR newVal) { m_str = newVal; return S_OK; } STDMETHODIMP Cxxx::get_integer(LONG* pVal) { *pVal = m_integer; return S_OK; } STDMETHODIMP Cxxx::put_integer(LONG newVal) { m_integer = newVal; return S_OK; }沒有什么復(fù)雜的,,就是實現(xiàn) str,、integer 兩個屬性值的設(shè)置和讀取功能。 (4)添加 IPersistPropertyBag 接口 還記得我們在上回書中如何添加 IPersistStreamInit 的嗎?添加 IPersistPropertyBag 的方法也一樣,,但這次我們換一個方式,,即我們不從 IPersistPropertyBag 派生,而是從 IPersistPropertyBagImpl<> 派生,。在 ATL 中,,系統(tǒng)幫我們已經(jīng)完成了很多接口的默認實現(xiàn),我們只要從 IxxxImpl<> 派生,,然后再添加一些必要的映射和變量,,就可以了。這樣顯然要比自己去實現(xiàn)接口的所有函數(shù)要簡單許多了,。其實,,如果你明白了本回 IPersistPropertyBagImpl<> 派生的方法后,你完全可以修改前回書中的實現(xiàn)方法,,從 IPersistStreamInit 派生改進為從 IPersistStreamInitImpl<> 派生,。 class ATL_NO_VTABLE Cxxx : public CComObjectRootEx<...>, public CComCoClass<...>, public IDispatchImpl<...>, public IPersistPropertyBagImpl<Cxxx> // 手工添加派生類 { ... ... ... BEGIN_COM_MAP(Cxxx) ... ... ... COM_INTERFACE_ENTRY(IPersistPropertyBag) // 手工添加接口表 END_COM_MAP() ... ... ... // 手工添加屬性映射表,這是 IPersistXXXImpl 所必須的,。 // 將來你在寫 ActiveX 的時候,,ATL 向?qū)臀覀兲砑訉傩杂成浔? BEGIN_PROP_MAP(Cxxx) // 參數(shù):"屬性名稱", 接口屬性序號(見IDL文件), 屬性頁對話窗 PROP_ENTRY("str", 1, CLSID_NULL) PROP_ENTRY("integer", 2, CLSID_NULL) END_PROP_MAP() ... ... ... public: ... ... ... // 這個成員變量,是 IPersistXXXImpl 所必須的 bool m_bRequiresSave; // 表示屬性數(shù)據(jù)是否已經(jīng)改變而需要保存 };我們只要手工添加以上內(nèi)容,,而不用自己寫任何 IPersistPropertyBag 接口的函數(shù),,多簡單呀!天空出彩霞呀,,地上開紅花呀......會唱這只歌的同學(xué)請舉手,,每個人獎勵 vckbase 的專家分 500 ! 三,、調(diào)用者的實現(xiàn) 我們在閱讀 MSDN 關(guān)于 IPersistPropertyBag 接口函數(shù)的時候,,你會發(fā)現(xiàn)還需要一個接口 IPropertyBag 與之配合才能實現(xiàn)屬性包功能。而 IPropertyBag 則需要我們在調(diào)用者(容器)中來實現(xiàn)該接口,。它們之間的關(guān)系如下: 前面幾回書中,,我們已經(jīng)學(xué)會了從 IUnknown 派生類,也學(xué)會了從 IDispatch 派生類,,也學(xué)會了從 ICallBack 派生類......同樣,,這回我們要從 IPropertyBag 派生了。在示例程序中,,我們添加了一個類 CPropertyBag::public IPropertyBag,,同時重載了所有的虛函數(shù)。
STDMETHODIMP CPropertyBag::QueryInterface(const struct _GUID &iid,void ** ppv) { *ppv = this; return S_OK; } ULONG __stdcall CPropertyBag::AddRef(void) { return 1; } // 做個假的就可以,,因為反正這個對象在程序結(jié)束前是不會退出的 ULONG __stdcall CPropertyBag::Release(void) { return 0; } // 做個假的就可以,,因為反正這個對象在程序結(jié)束前是不會退出的 STDMETHODIMP CPropertyBag::Read(LPCOLESTR pszPropName,VARIANT *pVar,IErrorLog *pErrorLog) { // 根據(jù) pszPropName 指定的屬性名稱,,你要提供該屬性的值。 // 而值的數(shù)據(jù)類型已經(jīng)在 pVal->vt 中指定了,。 if( 如果能提供指定的數(shù)據(jù) ) return S_OK; else return E_FAIL; } STDMETHODIMP CPropertyBag::Write(LPCOLESTR pszPropName,VARIANT *pVar) { // 根據(jù) psaPropName 指定的屬性名稱和 pVar 提供的值 // 你保存到文本中去吧,。 return S_OK; }以上是調(diào)用者(容器)程序的關(guān)鍵部分,其它的管理和協(xié)調(diào)部分,,讀者去閱讀示例程序代碼,。編譯注冊組件,并運行調(diào)用者示例程序,,顯示如下: 在編輯窗口中你可以隨便指定 str 和 interger 的值,,然后“啟動組件”,那么你設(shè)定的屬性值就會在啟動組件的同時,,通過 IPersistPropertyBag 接口設(shè)置到組件中(還原了持續(xù)性的環(huán)境),。而后,你就可以在下面的 Property 分組操作中,,“設(shè)置/讀取”組件的屬性了,。當(dāng)“關(guān)閉組件”的時候,程序通過調(diào)用 IPersistPropertyBag 接口函數(shù),,又重新取得組件的屬性名稱和值保存到編輯窗的文本中了,。 四、小結(jié) 注1:在 vc.net 中,由于系統(tǒng)已經(jīng)有 CProperty 類,,所以這里我們改換名稱為 CMyProperty,。 注2:通過十八回的學(xué)習(xí),我們已經(jīng)了解組件的一些常用接口,,為我們學(xué)習(xí) ActiveX 的組件編程打下了基礎(chǔ),。下回書,我們就開始學(xué)習(xí) ActiveX,。 |
|