概述創建C# COM對象
直接成員聲明的類
可能很多程序員對于這個不太理解這是什么意思,沒關系咱還是"用代碼來說話".
- [ComImport,Guid("DE88C160-FF2C-11D1-BB6F-00C04FAE22DA")]
- publicclassJetEngineClass
- {
- }
- [ComImport,CoClass(typeof(JetEngineClass)),Guid("9F63D980-FF25-11D1-BB6F-00C04FAE22DA")]
- publicinterfaceIJetEngine
- {
- voidCompactDatabase(
- [In,MarshalAs(UnmanagedType.BStr)]stringSourceConnection,
- [In,MarshalAs(UnmanagedType.BStr)]stringDestconnection
- );
- voidRefreshCache([In,MarshalAs(UnmanagedType.Interface)]objectConnection);
- }
- JetEngineClassengine=newJetEngineClass();
- IJetEngineiengine=engineasIJetEngine;
- //iengine即是所要用的接口的引用
大家看到了上面聲明的JetEngineClass類只有一個單單的類聲明,但是沒有一個成員聲明,但是和一般的類聲明有些不一樣的是這個類多了兩個特性(Attribute),把這個類和COM對象聯系在一起的就是這兩個特性了,其中一個是ComImportAttribute,這個特性指明了所作用的類是從COM對象中來的,GuidAttribute指明了COM對象的GUID,也就是說明了創建C# COM對象需用到的GUID。有了這兩個特性以后,這個類就不是一個普通的類了,當我們使用new去創建實例的時候,CLR看到了聲明的這兩特性就知道要創建的是一個COM對象,根據提供的GUID也就能創建出指定的COM對象,并和new返回的對象實例關聯在一起了。
終上4種方法我們可以看出來,***種方式只對特定的COM對象有效,不具有通用性;第二種方式只需要知道COM對象的CLSID或PROGID就可以了,是我們在.NET里平時比較常用的創建C# COM對象的方法;第三種方式需要自己聲明一個外部方法,而且需要傳入若干的參數,還需要知道COM對象模型,是單線程呢還是多線程,進程內呢還是進程外,兩個字"麻煩"。對CoCreateInstance這個方法不是很熟悉的人來說,用起來就不那么順手了;第四種方式用起來最像是.NET的方式,也最簡單省事,和其它.NET對象的創建方式最為接近。四種方法各有各有好處,我覺得簡單的COM對象,用第二種和第四種是***的(我個人來說最喜歡第四種)又不生成額外的程序集;要是COM對象相關的比較多,比如說Excel之類的COM對象,我建議還是用導入類型庫包裝吧,雖然是有可能出現版本問題,但這種應該很容易要求目標機器上運行的COM版和開發的時候一致的,更何況版本問題也不是100%出現,只是很少一部分會出這樣的問題。最不推薦的就是第三種方式了,這種方式在我看來唯一用到的地方就是使用IntPtr作為COM對象和接口的指針的時候,或者是想要在創建C# COM對象的時候,對參數作最靈活的控制的時候. 因為其它三種方式既不能返回IntPtr指針(其實也可以通過前面提到的的Marshal類的方法把.NET包裝的COM對象轉成指針),也不能提供與直接調用CoCreateInstance函數提供最全面的參數相匹配的方式。
***提個小問題
1 讀者有興趣的話可以去看看這幾種方式(不包括第三種)生成的COM對象的引用的類型是否是一致的,也就是用GetType得到的Type是否是一致的
2 大家猜猜這段代碼運行后,iengine的類型會是什么(GetType的結果), 會和engine的類型一樣嗎?
- JetEngineClass engine = new JetEngineClass();
- IJetEngine iengine = engine as IJetEngine;
- // iengine即是所要用的接口的引用
- IntPtr p = Marshal.GetIUnknownForObject(engine);
- iengine = Marshal.GetObjectForIUnknown(p) as IJetEngine;
以上介紹創建C# COM對象成員聲明我這里就不給出結果了,留給讀者自行去驗證吧。另外如果大家還發現.NET中有其它的創建C# COM對象的方式也盡指教一二,本人將不甚感激.
【編輯推薦】