本文簡單介紹COM以及.Net之間的差異性
我不得不說,中文版的「使用Microsoft.NET 做COM程式設計」一書真的翻譯的很xx,看都看不懂,因此,我特別買了英文原文書回來,看過一遍後,覺得還比中文版翻譯書更容易理解。因此我將我理解的語意,按照原文的排版方式,重新整理了一遍,方便我日後閱讀,如果你也是對Com Interop有興趣,建議你購買原文書回來參照,我想這樣會讓你在學習Com Interop的過程中更容易上手。
==========================================================================================
COM與.NET就好像二個不同的種族,彼此之間並不相容。而微軟提供一種叫做Com Interop的技術,來做到Com與.NET之間的互通性。
換句話說,透過Com Interop,我們可以在.NET的應用程式中使用Com元件,同樣的也可以在Com的元件中,使用由.NET程式開發的組件。
當然,要做到上述能力,並沒有想像中那麼簡單,首先,我們必須先了解什麼叫做RCW及CCW這二個名詞。
RCW全文叫做Runtime Callable Wrapper,你可以把它視為「COM元件喬裝的.NET 類別」,而相對的,CCW(Com Callable Wrapper)就是「.NET組件喬裝的COM介面」了。
上面我們已經簡單介紹了Com Interop的”需求”了,接下來,我們先來分析COM與.NET之間的差異性,知已知彼才能百戰百勝嘛~了解二者之間文化上的差異性,使我們更容易了解當我們在二個平台中互用時,該去注意到哪些地方,以必免不必要的”種族衝突”。
存放位置的差異
COM元件與其「描述資訊」被放在二個不同的地方;COM元件可以放在任何地方,而其描述資訊則一定是被放在Windows Registry中。
而.NET組件一般放在GAC中,但較為不同的是,其「描述資訊」永遠緊跟著它。
COM使用Windows Registry來存放描述資訊有幾個比較嚴重的問題:
1.Windows Registry維護不易,不管是透過程式,還是人為操作,都不是一件簡單的事。
2.Windows Registry如果不小心動到,可能會導致COM元件無法正常運作。
3.由於「描述資訊」與COM元件分隔二地,因此,可能更新了COM 元件,忘了同步更新描述資訊,導致描述資訊過期而無從得知。
再來看看.NET組件怎麼做,首先要犛清的問題是:「這個.NET組件是要自用?還是公用?」。
如果.NET組件是我們自已的程式要用到的,那麼只要跟我們的程式放在相同路徑下面就好。如果是想開放給大家使用,那麼就可以考慮放在GAC中。GAC 的路徑是C:Windwos\Assembly。
識別系統
COM統一使用GUID來識別介面、介面方法等等,而.NET不使用 GUID來做識別,而改用「領域名稱」來識別;這二者的差別有點像一個使用IP,一個使用DomainName。
.NET組件的識別系統,不僅僅只靠「領域名稱」來解決,它還可以包括組件的名稱、版本、語系等等資訊,因此,.NET組件的彈性相對來說就比COM元件來的高得許多。
元件的生命週期
COM元件透過「參考計數」的方式來管理元件自已的生命週期,Client程式在使用COM元件時,要記得去增加或減少計數,當然這種機制對Client程式來說,無疑多了一個麻煩事。而.NET就相對簡單許多了,Client 程式完全不管組件的存活期,組件的生死統一由GC,也就是Runtime的垃圾收集機制來做管理。
例外處理
COM使用HRESULT這個結構來傳送錯誤資訊給Client程式,而.NET 則使用Exception機制來傳送錯誤資訊。Exception在.NET中也是一種類別,因此它可以被繼承改寫,相對於HRESULT來說,Exception顯得彈性許多。
型別資訊
元件要讓別人使用,一定要有自我解釋能力,要做到如此,不管是.NET或COM都必須設法公開其型別資訊。
COM的型別資訊包括COM Coclass以及一整組的介面方法。型別資訊是編譯過的二元資料檔,因此無法被人為辨識出來。
(COM型別資料檔叫做MIDL,其編譯前的檔案叫做IDL)
.NET透過Metadata來公開其型別資訊,Metadata永遠緊跟著.NET組件。因此只要你拿到某個組件,就可以很簡單透過.NET所提供的Refection機制來讀取其型別資訊。
能見度
COM元件是透過Interface的方式來實做的,Interface內的方法基本上都是公開的, 因此沒有能見度的問題。
在.NET的開發環境中,我們是透過CCW機制將.NET組件”偽裝”成COM元件,而預設一個COM Wrapper類別內的所有方法、屬性、欄位(Field)、事件等都時必須設為public。如果類別裏面某些方法,屬性等不想公開給Client程式碼看到,雖然不可避免的,我們還是要將其設為public,但透過C#的Attribute機制,我們可以告知編譯器隱藏某一類別、或方法不給Client程式知道,程式碼如下:
[ComVisible(false)]
Public class MyClass
{
[ComVisible(false)]
Public void foo()
{
}
}
資料的傳遞
COM元件有許多特別的型別,例如BSTR或SAFEARRAY,因此要在.NET 與COM之間傳送資料並不是一件簡單的事。
.Net Interop提供了一種名叫 “.Net Interop馬歇爾機制”,這個機制可以做到在不同的Process中傳送資料。這個機制也可以在”拖管記憶體推疊”與”非拖管記憶體推疊”中進行資料傳遞。
在COM Client中使用 .NET
如果一個.NET類別,打算在COM中使用,那麼就要注意這個Com類別必須是Thread-Safe,也就是這說,當這個類別實例被配置出來時,能夠支援多執行緒的存取。
在.NET 中使用COM元件
在使用Com Interop技術時,預設.NET應用程式的記憶體配置機制是支援多執行緒執行環境的(MTA)。如果程式想改為單一執行緒記憶體配置(STA),可以在程式的進入點上宣告STAThread Attribute,如下所示
Class Class1
{
[STAThread]
Static void Main(string [] args)
{
}
}
此外,這個Attribute的宣告並不會引響一般的.NET應用程式。
事件處理
COM和.NET都支援事件驅動,但二者的做法有很大的差異。
COM透過connection points,而.NET使用delegate。