COM组件设计与应用(十三)(转载)
COM組件設(shè)計與應(yīng)用(十三)
事件和通知(VC6.0)
作者:楊老師
下載源代碼
一、前言
我的 COM 組件運行時產(chǎn)生一個窗口,當(dāng)用戶雙擊該窗口的時候,我需要通知調(diào)用者;
我的 COM 組件用線程方式下載網(wǎng)絡(luò)上的一個文件,當(dāng)我完成任務(wù)后,需要通知調(diào)用者;
我的 COM 組件完成一個鐘表的功能,當(dāng)預(yù)定時間到達(dá)的時候,我需要通知調(diào)用者;
... ... ... ...
本回書開始話說 COM 的事件、通知、連接點......這些內(nèi)容比較多,我分兩次(共四回)來介紹。
二、通知的方法
當(dāng)程序甲方內(nèi)部發(fā)生了某個事件的時候,需要通知乙方,無非使用幾個方法:
| 通知方式 | 簡單說明 | 評論 | |
| 直接消息 | PostMessage() PostThreadMessage() | 向窗口或線程發(fā)個消息 | 你什么時候執(zhí)行我就不管啦 |
| SendMessage() | 馬上執(zhí)行消息響應(yīng)函數(shù) | 不執(zhí)行完消息處理函數(shù)不會返回 | |
| SendMessage(WM_COPYDATA...) | 發(fā)消息的同時,還可以帶過去一些自定義的數(shù)據(jù) | 比較常用,所以單獨列了出來 | |
| 間接消息 | InvalidateRect() SetTimer() ...... | 被調(diào)用的函數(shù)會發(fā)送相關(guān)的一些消息 | 這樣的函數(shù)太多了 |
| 回調(diào)函數(shù) | GetOpenFileName()...... | 當(dāng)用戶改變文件選擇的時候,執(zhí)行回調(diào)函數(shù) | 嗨!哥們,這是我的電話,有事就言語一聲。 |
在 COM 的時代,以上這些方法就基本上不能玩轉(zhuǎn)了,因為...您想呀 COM 組件是運行在分布式環(huán)境中的,地球另一邊計算機(jī)上運行的組件,怎么可能給你的窗口發(fā)消息那?當(dāng)然不能!(但話又說回來,對于 ActiveX 這樣只能在本地運行的組件,當(dāng)然也可以發(fā)送窗口消息的啦。)
回調(diào)函數(shù)的方式,是設(shè)計 COM 通知方法的基礎(chǔ)?;卣{(diào)函數(shù),本質(zhì)上是預(yù)先把某一函數(shù)的指針告訴我,當(dāng)我有必要的時候,就直接呼叫該函數(shù)了,而這個回調(diào)函數(shù)做了什么,怎么做的,我是根本不關(guān)心的。好了,問你個問題:啥是 COM 的接口?接口其實就是一組相關(guān)函數(shù)的集合(這個定義不嚴(yán)謹(jǐn),但你可以這么理解哈)。因此,在COM中不使用“回調(diào)函數(shù)”而是使用“回調(diào)接口”(說的再清楚一些,就是使用一大堆包裝好的“回調(diào)函數(shù)”集) ,回調(diào)接口,我們也叫“接收器接口”。
圖一、客戶端傳遞接收器接口指針給COM。當(dāng)發(fā)生事件時,COM調(diào)用接收器接口函數(shù)完成通知
本回示例程序完成的功能是:
客戶端啟動組件(Simple11.IEvent1.1)并得到接口指針 IEvent1 *;
調(diào)用接口方法 IEvent1::Advise() 把客戶端內(nèi)部的一個接收器(sink)接口指針(ICallBack *)傳遞到組件服務(wù)器中;
調(diào)用 IEvent1::Add() 去計算兩個整數(shù)的和;
但是計算結(jié)果并不通過該函數(shù)返回,而是通過 ICallBack::Fire_Result() 返回給客戶端;
當(dāng)客戶端不再需要接受事件的時候,調(diào)用 IEvent1::Unadvise() 斷開和組件的聯(lián)系。
三、組件實現(xiàn)步驟
1、建立一個工作區(qū)(WorkSpace)
2、在工作區(qū)中,建立一個 ATL 工程(Project)。示例程序中工程名稱叫 Simple11,接受全部默認(rèn)選項。
3、ClassView 中,執(zhí)行鼠標(biāo)右鍵菜單命令 New Atl Object...,添加 ALT 類。
?? 3-1、左側(cè)分類 Category 選擇 Objects,右側(cè) Objects 選擇 SimpleObject(其實就是默認(rèn)項目)
?? 3-2、名稱 Name 卡片中,輸入組件名稱。示例程序中是 Event1(注1)
?? 3-3、屬性 Attributes 卡片中,修改接口類型 Interface 為定制的 Custom(注2)
4、ClassView 中,選擇接口(IEvent1),鼠標(biāo)右鍵菜單添加函數(shù) Add Method...
圖二、增加接口函數(shù) Add([in] long n1,[in] long n2)
圖三、增加接口函數(shù) Advise([in] ICallBack *pCallBack,[out] long *pdwCookie)
圖四、增加接口函數(shù) Unadvise([in] long dwCookie)
你應(yīng)該注意到了,在Add()函數(shù)中,并沒有[out]、[retval] 這樣的 IDL 屬性,嘿嘿,因為我們本來就不打算通過 Add() 函數(shù)直接得到計算結(jié)果。不然怎么演示回調(diào)接口呀:-) 另外,在函數(shù) Advise()中,需要返回一個整數(shù) dwCookie,這是干什么?道理很簡單,因為我們的組件想同時支持多個對象的回調(diào)連接。因此當(dāng)客戶端傳遞一個接口給我們組件的時候,我返回給它唯一的一個 cookie 號碼來表示身份,將來斷開連接的時候 Unadvise(),它需要把這個 cookie 身份號再給我,這樣我就知道是誰想斷開了。
5、增加回調(diào)接口 ICallBack 的 IDL 定義。打開 IDL 文件并手工輸入(黑體字部分為手工輸入的) ,然后保存:
6、增加回調(diào)接口函數(shù)
圖五、增加回調(diào)接口函數(shù)
其實和以前的方法一樣,只要注意別選錯了接口就好。
圖六、增加接口函數(shù) Fire_Result([in] long nResult)
我們計算整數(shù)和,得到結(jié)果后,就是要靠這個回調(diào)接口函數(shù)去反饋給客戶端呀。
7、添加組件內(nèi)部保存回調(diào)接口指針的數(shù)組
剛才已經(jīng)說過,我們這個組件打算支持多個對象的回調(diào)連接,因此我們要使用一個數(shù)組來保存。在 ClassView 中,選擇 CEvent1 類,增加成員變量 Add Member Variable...
圖七、增加保存 ICallBack * 的數(shù)組
當(dāng)然,保存一個數(shù)組可以有多種方式。示例程序比較簡單,定義了一個10個元素空間的成員數(shù)組變量。如果你已經(jīng)學(xué)會了使用 STL,那么你也可以用 vector 等容器來實現(xiàn)。注意!注意!注意!在構(gòu)造函數(shù)中別忘了初始化數(shù)組元素為 NULL。
8、好了,下面開始完成所有代碼
四、客戶端實現(xiàn)步驟
大家下載示例程序后,去瀏覽客戶端的實現(xiàn)程序吧。這里我只說明一下關(guān)于接收器是如何構(gòu)造的:
圖八、從 ICallBack 派生接收器類 CSink
從 ICallBack 派生一個類 CSink。確認(rèn)后 IDE 會有一個警告,說它找不到 ICallBack 的頭文件,不用理它,因為只有當(dāng)編譯的時候,#import 才會為我們生成 xxxx.tlh、xxxx.tli 文件,這些文件就有 ICallBack 的聲明啦。
這里 ICallBack 是 COM 接口,因此 CSink 是不能事例化的,如果你去編譯,會得到一坨一坨(注3)的錯誤,報告說你沒有實現(xiàn) virtual 函數(shù)。然后,我們可以按照錯誤報告,去實現(xiàn)所有的虛函數(shù):
五、小結(jié)
COM 組件實現(xiàn)事件、通知這樣的功能有兩個基本方法。今天介紹的回調(diào)接口方式非常好,速度快、結(jié)構(gòu)清晰、實現(xiàn)也不復(fù)雜;下回書介紹連接點方式(Support Connection Points),連接點方法其實并不太好,速度慢(如果是遠(yuǎn)程DCOM方式,要謹(jǐn)慎選擇它)、結(jié)構(gòu)復(fù)雜、唯一的好處就是 ATL 對它進(jìn)行了包裝,所以實現(xiàn)起來反而比較簡單。不介紹又不行,因為微軟絕大數(shù)支持事件的組件都是用連接點實現(xiàn)的,咳......討厭的微軟(注4)。
注1:本來設(shè)想多舉幾個例子,因此第一個叫 Event1,可寫完后,感覺程序已經(jīng)比較復(fù)雜了,就沒繼續(xù)再做了。
注2:當(dāng)然,你選擇使用雙接口 Dual 也沒有問題。但要注意到在下面的步驟,增加回調(diào)接口修改 IDL 文件的時候,我們是要使用 Custom(從IUnknown派生,而不是從IDispatch派生)的。
注3:一坨一坨經(jīng)常用來形容一堆一堆的狗屎。
注4:微軟的同志們,玩笑話不要當(dāng)真呀!我還靠著你來吃飯那。
轉(zhuǎn)載于:https://www.cnblogs.com/duzouzhe/archive/2009/07/22/1528899.html
總結(jié)
以上是生活随笔為你收集整理的COM组件设计与应用(十三)(转载)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [云框架]KONG API Gatewa
- 下一篇: oracle中rownum和row_nu