将COM+ Events进行到底[转]
將COM+ Events進行到底(一)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
Article last modified on 2002-7-4
----------------------------------------------------------------
The information in this article applies to:
- Microsoft Windows 2000 Advanced Server
- Microsoft Windows 2000 Server
- Microsoft Windows 2000 Professional
- Microsoft Component Service 1.0
----------------------------------------------------------------
概要
通常,我們在組件和客戶程序之間實現事件通知與訂閱時,會采用這兩種方法:回調(Callback),和連接點(ConnectionPoint)。他們的本質其實都是,訂閱者將接口傳遞給發布者,當有事情發生時,發布者調用接口的方法。這種類型的事件叫Tightly Coupled Event(TCE),屬于Request-Reply方式。
TCE的限制在于:
1.?????? 訂閱者必須知道它所請求的通知來自于哪一個發布者。發布者和訂閱者緊緊綁定在一起,雙方程序代碼依賴于接口的定義,我們必須在編譯時刻知道對方的信息(CLSID,或ProgID);
2.?????? 要求訂閱者和發布者必須同時在線。也就是說,在生存周期上必須重疊(overlap);
3.?????? 不包含過濾或截取機制。
?
COM+提供了一種全新的服務:COM+ Events或者Loosely Coupled Events(LCE)。不過此事件非彼事件。用微軟的話說就是“COM+ 事件不與傳統 ConnectionPoints 事件關聯,并且在完全不同的方案中使用。”
它的思路是,發布者親自維護一個外部數據庫(我們管它叫EventList),其中存儲了訂閱的所有事件的列表。發布者通過這個表知道該如何發送事件通知。訂閱者也可以讀入此列表,并選擇它感興趣的事件。發布者再維護另一個數據庫(我們管它叫SubscribtionList),其中存儲了訂閱者的CLSID。從概念上講,這個數據庫相當于一個郵件列表。訂閱者可以向這個庫中添加自己的CLSID。發布者想要激發事件時,會檢查這個數據庫,找到所有訂閱這個事件的訂閱者的CLSID,創建每一個相關類的新對象,并調用對象的方法。
這個屬于Publisher-Subscriber方式。
?
?
(圖1可參看http://www.microsoft.com/CHINA/msdn/library/techart/com+agvb4.gif)
具體執行的順序,圖2說得非常明白:
?
(圖2可參看http://www.idevresource.com/images/articles/com+eventsintro1.gif)
Fig 2 shows a typical COM+ events life cycle.
???????
LCE的優點就在于:
n?????? 訂閱者不必知道上層事件處理的細節;
n?????? 發布者不在運行時,訂閱者也可以發出訂閱請求;發布者不用理會訂閱者是否在家,就可以發布通知;
n?????? 過濾或者截取。訂閱者可以選擇訂閱事件中哪些他更感興趣,比如,一個訂戶訂閱了出版社的新書通知,但是他還可以告訴出版社只有當國外作家的新書到達時才給他通知,或者只有當價格低于50元的新書到達時候才通知他。
更多細節
發布者的事件方法執行情況:
當一個事件的方法最終返回時,?
非QC的訂閱者中:
臨時訂閱者的情況:訂閱者對象已經被調用,而且方法已經返回!
永久訂閱者的情況:訂閱者對象已經被創建、調用、返回,而且已經被釋放!
????
QC訂閱者的情況:
所有對QC訂閱者的調用已經被記錄下來;
對于永久訂閱者,這些記錄已經被送到隊列準備傳送出去。
????
所以非QC訂閱者的執行情況會影響到發布者,而QC不會。比如非QC的訂閱者中如果在接收到事件通知的方法中Sleep個一分鐘,那發布者的事件方法就必須老老實實等1分鐘,然后再調用下一個訂閱者對象。
訂閱者的疑問一:
如果我的訂閱者程序已經在處理一個事件了,這時候又來了一個事件通知,那么這第二個事件會被誰接收呢?
是新創建一個訂閱者對象呢?還是等著當前這個事件處理完?
我認為是后者。
發布者的疑問一:
發布者可不可以位于不同的機器?
發布者分布到遠程服務器上,但是事件通知仍然在中心服務器上。這種情況可以通過下面兩種方法做到:
u????? 將EventClass所在的COM+應用導出為應用程序代理。在遠程服務器上安裝這個代理。
u????? 在遠程服務器上調用該EventClass發布事件時,調用代碼改為:CreateObject(“%YourEventClassProgID%”, “\\%YourCenterServerName%”)。
這樣,在遠程服務器上的事件發布行為,都會被轉到中心服務器上的EventClass應用。
訂閱者的疑問二:
訂閱者是否可以位于不同的機器呢?
下面的文字是引用MSDN的《COM+ Events Architecture》:
Note???This version of COM+ Events does not support a distributed event store. A subscriber must subscribe to an event on each computer from which it wants to receive notification. As an alternative, you can register the EventClass and subscriptions on a central computer and instantiate this EventClass from the remote computers on which you will publish events. Delivery of events is provided either by DCOM or queued components. For more information on using queued components, see Composing Events with Queued Components.
曾經有人做過下面的步驟試圖遠程訂閱:
ü??????? 遠程訂閱者創建一個“COMAdmin.COMAdminCatalog”的實例;
ü??????? 遠程訂閱者調用COMAdmin.COMAdminCatalog的Connect方法連結至中心服務器;
ü??????? 遠程訂閱者通過向中心服務器的臨時訂閱數據庫中添加一條記錄來訂閱;
但是,結果是中心服務器的其他本地訂閱者都接收到了事件通知,而遠程訂閱者卻沒有。
建議如果真的想遠程訂閱的話,可以自己實現DCOM,或者用QC。
?
EventClass組件的調試:
如果在VC IDE或者VB IDE中設斷點調試EventClass組件,那么當事件發布時,訂閱者將接收不到事件通知。
這是由于在COM+應用中EventClass組件的注冊地址已經被更改為:
D:\Program Files\Microsoft Visual Studio\VB98\VB6DEBUG.DLL了!
雖然,EventCalss組件的CLSID沒有變,但是不是和訂閱數據庫中記錄的不一樣了呢,所以沒有通知訂閱者。
?
制作實錄:
第一步,先做一個EventClass組件:
新建一個VB DLL,Project Name為TomoTrace,Class Module Name為Trace。
代碼極其簡單,為:
Public Function Publish(ByVal strAuthor As String, _
??????????????????????? ByVal strTraceType As String, _
??????????????????????? ByVal strEventCategory As String, _
??????????????????????? ByVal nEventID As Integer, _
??????????????????????? ByVal nOther As Integer, _
??????????????????????? ByVal strTraceContent As String) As Integer
???????????????????????
??? ' Do Nothing
???????????????????????
End Function
在組件服務中新建一個COM+應用,在該應用下新建組件TomoTrace.Trace,注意要選擇安裝為新的事件類。
記錄下這個接口的CLSID。
第二步,做一個訂閱者:
新建一個VB EXE,Project Name為Trace2File。
首先要引用TomoTrace組件,并實現它的Trace接口:
Implements Trace
?
聲明一些全局變量:
‘? g_oAdmin將是COMAdmin.COMAdminCatalog的實例對象:
Dim g_oAdmin As Object
?
‘? 使用ICatalogObject接口將允許讀寫COM+ Catalog中的對象暴露出來的屬性:
Dim SubObj As ICatalogObject
?
Const TransientSubscription = "Transient Subscription"
?
‘ 這是TomoTrace.Trace事件接口的ClassID:
Const EVENTCLSID = "{8EDBE260-A7C7-4000-BF95-CA4CC8FCA6C2}"
?
Dim g_CascadeApp, g_TransID
?
‘? 你可以用ICatalogCollection接口枚舉、添加、刪除和獲得Collection中的條目:
Dim Subcoll As ICatalogCollection
?
在Form初始化時,提交訂閱請求:
Private Sub Form_Load()
' Create COMAdmin object
??? Set g_oAdmin = CreateObject("COMAdmin.COMAdminCatalog.1")
???
??? ' Get the TRANSIENTSUBSCRIPTIONS collection
??? Set Subcoll = g_oAdmin.GetCollection("TransientSubscriptions")
???
??? ' Add a new subscription
??? Set SubObj = Subcoll.Add
??? ‘ 將我們的EventClass的CLSID加進去
??? SubObj.Value("EventCLSID") = EVENTCLSID
??? ‘ 臨時訂閱時,這個Name條目的值一定是“Transient Subscription”
??? SubObj.Value("Name") = TransientSubscription
??? SubObj.Value("SubscriberInterface") = Me
?
?????? ‘ 將所做的改變存入COM+ Catalog Data Store中:
??? Subcoll.SaveChanges
?
?????? ‘ 實際計算中g_TransID的值將為
’ {ADA4AFA1-6B54-4C4B-A74A-9EFFF4F7DC3F},這就是這次訂閱的唯一標識
‘ 我們將在取消訂閱時用到這個標識
??? g_TransID = SubObj.Value("ID")
End Sub
?
Form退出時,取消臨時訂閱:
Private Sub Form_Unload(Cancel As Integer)
On Error Resume Next
?
‘ 對于Collection中的所有對象,讀取數據
??? Subcoll.Populate
?
??? ' Remove the subscription
??? Dim k
??? For k = 0 To Subcoll.Count - 1
??????? Dim oObject
??????? Set oObject = Subcoll.Item(k)
???????
?????????????? ‘ 如果Collection中的條目的ID和我們先前保存的ID一樣,則刪除
??????? If oObject.Value("ID") = g_TransID Then
??????????? Subcoll.Remove (k)
??????????? Subcoll.SaveChanges
??????? End If
??? Next
??? On Error GoTo 0
???
End Sub
?
實現事件的方法:
Private Function Trace_Publish(ByVal strAuthor As String, _
??????????????????????????????? ByVal strTraceType As String, _
??????????????????????????????? ByVal strEventCategory As String, _
??????????????????????????????? ByVal nEventID As Integer, _
??????????????????????????????? ByVal nOther As Integer, _
??????????????????????????????? ByVal strTraceContent As String) As Integer
??? ‘ 將Trace的內容添加到listbox中:
??? Dim lCount As Long
??? listTrace.AddItem Now & " :: " & strTraceType
???
??? If (Len(strTraceContent) < 60) Then
??????? listTrace.AddItem "--------" & strTraceContent
??? Else
??????? listTrace.AddItem "--------" & Left(strTraceContent, 60) & "..."
??? End If
???
??? lCount = listTrace.ListCount - 1
??? listTrace.ListIndex = lCount
??? If lCount > 20 Then
??????? listTrace.RemoveItem 0
??? End If
???????????????????????
End Function
第三步,做一個發布者:
新建一個VBScript腳本,它將調用EventClass組件TomoTrace.Trace:
Dim obj
Set obj = CreateObject("TomoTrace.Trace")
strAuthor = "TomoEvent"
strTraceType= "Information"
strEventCategory = "Application"
nEventID = 0
nOther = 0
strTraceContent = "這里我們的例子中,EventClass就是TomoTrace.DLL組件,訂閱者是Trace2File.EXE。發布者就是本腳本。"
obj.Publish strAuthor, strTraceType, strEventCategory, nEventID, nOther, strTraceContent
PublisheràDistributoràSubscriber:
先啟動幾個Trace2File.EXE,然后運行VBScript腳本。你就會看到這幾個Trace2File上顯示出發布的Trace內容。
然后將這個腳本放置在遠程機器上,修改一下腳本:CreateObject(“TomoTrace.Trace”) àCreateObject(“TomoTrace.Trace”,”\\zhengyun”)。這樣就可以在其他機器上向中心服務器發布事件通知了。
?
Written by zhengyun (at) tomosoft.com
轉載于:https://www.cnblogs.com/vboy/articles/172677.html
總結
以上是生活随笔為你收集整理的将COM+ Events进行到底[转]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【网】关于 Blog 和 RSS 的全面
- 下一篇: 为ActiveX制作数字签名 -- 介绍