C#創建COM對象的方法

http://www.csharpwin.com/csharpspace/7056r9732.shtml

是最直接的.net 中 使用 com對象的例子。

另外一篇是 COM .NET Interoperability (http://www.cnblogs.com/DylanWind/archive/2010/11/24/1886714.html) 比較複雜。但是提供了一種從tlb 到C#接口描述的方法。

 

 

 

用C#做WinForm程序,時間長了難免會遇到和COM組件打交道的地方,用什麼方式創建C# COM對象也成了我們必須面對的一個問題.據我所知道的創建C# COM對象的方法一共有以下幾種:

1.使用.NET包裝COM組件

    這是最簡單的就是導入COM組件所在的DLL,讓IDE生成。NET一個IL包裝加到項目中,這樣原來COM裏面所有實現了IDispatch,Dual的COM類型及其相關類型就可以直接在。NET程序裏面使用,比如以前在2003時代,想要寫自己的基於IE的瀏覽器,就得手動加入與IWebBrowser2接口相關的DLL,這種方式是大家最常用的,也是最傻瓜化的,因此也沒什麼可解釋的。

    但是這種方式有個至命的缺點——不是所有的C# COM對象都能用這種方式導出。正如前面所說的,只有實現了IDispatch,Dual類型的接口才支持被導出,而且面對不同版本的COM或許會生成不一樣的導出DLL,比如說A機器上寫代碼時導入了一個Jet2.6版本的包裝DLL,代碼編譯了拿到B機器上去運行,但是B機器上的Jet版本是2.8的,就可能會出現運行時錯誤。

2.用反射動態創建

    包括使用Type.GetTypeFromCLSID和Type.GetFromProgID兩種方法獲取COM對象的Type再創建.這種方式也好理解,就是說使用這兩個方法之前,必須得知道COM對象的GUID或ProgID,好在這也不是什麼難事,一般我們要使一個COM對象,多多少少都瞭解一些這個COM對象的GUID或ProgID信息.用這種方獲取到了一個Type對象後,就可以用.NET裏面通用的反射創建對象的方法來做了.

    這裏給出一個創建JetEngine 的COM對象的代碼實例:

  1. public object GetActiveXObject(Guid clsid)
  2. {
  3. Type Typet=Type.GetTypeFromCLSID(clsid);
  4. if(t==null) return null;
  5. return Activator.CreateInstance(t);
  6. }
  7. Guid g=new Guid("DE88C160-FF2C-11D1-BB6F-00C04FAE22DA");//JetEngine
  8. object jet=GetActiveXObject(g);

    是不是覺得最後調用GetActiveXObject(g)的地方和IE裏面Javascript裏面用new ActiveXOjbect創建COM對象的方法很相像?

3.聲明CoCreateInstance外部函數,用這個函數去創建相應的COM實例

    M$在2005裏面包裝的WebBrowser控件內部就是用這個函數去創建的, 使用這種方式創建COM,就跟在C++裏面不什麼兩樣了.有一點需要說明的是,一般我們在代碼中引入外部方法的時候,方法的參數和返回值的類型不一定是唯一的一種,只要在邏輯上相互能轉化,一般都可以使用.

    比如說如下幾種聲明都是正確的:

  1. [return:MarshalAs(UnmanagedType.Interface)]
  2. [DllImport("ole32.dll",ExactSpelling=true,PreserveSig=false)]
  3. public static extern object CoCreateInstance([In]ref Guid clsid,
  4. [MarshalAs(UnmanagedType.Interface)]object punkOuter,intc ontext,[In]ref Guid iid);
  5. [DllImport("ole32.dll",ExactSpelling=true,PreserveSig=false)]
  6. public static extern IntPtr CoCreateInstance([In]ref Guid clsid,
  7. IntPtr punkOuter,int context,[In]ref Guid iid);
  8. [DllImport("ole32.dll",ExactSpelling=true)]
  9. public static extern int CoCreateInstance([In]ref Guid clsid,
  10. IntPtr punkOuter,int context,[In]ref Guid iid,[Out]out IntPtr pVoid);
  11. [DllImport("ole32.dll",ExactSpelling=true)]
  12. public static extern int CoCreateInstance([In]ref Guid clsid,
  13. [MarshalAs(UnmanagedType.Interface)]object punkOuter,int context,
  14. [In]ref Guid iid,[MarshalAs(UnmanagedType.Interface),Out] out object pVoid);

    甚至於當你有裏面對應的接口類型的聲明的時候,完全可以把上面的object或IntPtr換成相應的接口類型,前提是你的接口類型的聲明一定要正確.讀者中用C++做過COM的一定對這種方式記憶猶新吧,只不過這裏不再需要什麼CoInitialize和CoUninitialize,.NET內部自己幫你搞定了.順便提一下,上面例子中的object與IntPtr聲明是相通的,我們可以用Marshal.GetObjectForIUnknown和Marshal.GetIUnknownForObject這兩個方法在object和IntPtr之間互轉,前題當然是這兩種方式所指向的都是C# COM對象才行.這種方式提供的傳入參數最多,創建C# COM對象也最靈活.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章