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