創(chuàng)建C# COM對(duì)象的方法
用C#做WinForm程序,時(shí)間長(zhǎng)了難免會(huì)遇到和COM組件打交道的地方,用什么方式創(chuàng)建C# COM對(duì)象也成了我們必須面對(duì)的一個(gè)問(wèn)題.據(jù)我所知道的創(chuàng)建C# COM對(duì)象的方法一共有以下幾種:
1.使用.NET包裝COM組件
這是最簡(jiǎn)單的就是導(dǎo)入COM組件所在的DLL,讓IDE生成。NET一個(gè)IL包裝加到項(xiàng)目中,這樣原來(lái)COM里面所有實(shí)現(xiàn)了IDispatch,Dual的COM類(lèi)型及其相關(guān)類(lèi)型就可以直接在。NET程序里面使用,比如以前在2003時(shí)代,想要寫(xiě)自己的基于IE的瀏覽器,就得手動(dòng)加入與IWebBrowser2接口相關(guān)的DLL,這種方式是大家最常用的,也是最傻瓜化的,因此也沒(méi)什么可解釋的。
但是這種方式有個(gè)至命的缺點(diǎn)——不是所有的C# COM對(duì)象都能用這種方式導(dǎo)出。正如前面所說(shuō)的,只有實(shí)現(xiàn)了IDispatch,Dual類(lèi)型的接口才支持被導(dǎo)出,而且面對(duì)不同版本的COM或許會(huì)生成不一樣的導(dǎo)出DLL,比如說(shuō)A機(jī)器上寫(xiě)代碼時(shí)導(dǎo)入了一個(gè)Jet2.6版本的包裝DLL,代碼編譯了拿到B機(jī)器上去運(yùn)行,但是B機(jī)器上的Jet版本是2.8的,就可能會(huì)出現(xiàn)運(yùn)行時(shí)錯(cuò)誤。
2.用反射動(dòng)態(tài)創(chuàng)建
包括使用Type.GetTypeFromCLSID和Type.GetFromProgID兩種方法獲取COM對(duì)象的Type再創(chuàng)建.這種方式也好理解,就是說(shuō)使用這兩個(gè)方法之前,必須得知道COM對(duì)象的GUID或ProgID,好在這也不是什么難事,一般我們要使一個(gè)COM對(duì)象,多多少少都了解一些這個(gè)COM對(duì)象的GUID或ProgID信息.用這種方獲取到了一個(gè)Type對(duì)象后,就可以用.NET里面通用的反射創(chuàng)建對(duì)象的方法來(lái)做了.
這里給出一個(gè)創(chuàng)建JetEngine 的COM對(duì)象的代碼實(shí)例:
- publicobjectGetActiveXObject(Guidclsid)
- {
- TypeTypet=Type.GetTypeFromCLSID(clsid);
- if(t==null)returnnull;
- returnActivator.CreateInstance(t);
- }
- Guidg=newGuid("DE88C160-FF2C-11D1-BB6F-00C04FAE22DA");//JetEngine
- objectjet=GetActiveXObject(g);
是不是覺(jué)得***調(diào)用GetActiveXObject(g)的地方和IE里面Javascript里面用new ActiveXOjbect創(chuàng)建COM對(duì)象的方法很相像?
3.聲明CoCreateInstance外部函數(shù),用這個(gè)函數(shù)去創(chuàng)建相應(yīng)的COM實(shí)例
M$在2005里面包裝的WebBrowser控件內(nèi)部就是用這個(gè)函數(shù)去創(chuàng)建的, 使用這種方式創(chuàng)建COM,就跟在C++里面不什么兩樣了.有一點(diǎn)需要說(shuō)明的是,一般我們?cè)诖a中引入外部方法的時(shí)候,方法的參數(shù)和返回值的類(lèi)型不一定是唯一的一種,只要在邏輯上相互能轉(zhuǎn)化,一般都可以使用.
比如說(shuō)如下幾種聲明都是正確的:
- [return:MarshalAs(UnmanagedType.Interface)]
- [DllImport("ole32.dll",ExactSpelling=true,PreserveSig=false)]
- publicstaticexternobjectCoCreateInstance([In]refGuidclsid,
- [MarshalAs(UnmanagedType.Interface)]objectpunkOuter,intcontext,[In]refGuidiid);
- [DllImport("ole32.dll",ExactSpelling=true,PreserveSig=false)]
- publicstaticexternIntPtrCoCreateInstance([In]refGuidclsid,
- IntPtrpunkOuter,intcontext,[In]refGuidiid);
- [DllImport("ole32.dll",ExactSpelling=true)]
- publicstaticexternintCoCreateInstance([In]refGuidclsid,
- IntPtrpunkOuter,intcontext,[In]refGuidiid,[Out]outIntPtrpVoid);
- [DllImport("ole32.dll",ExactSpelling=true)]
- publicstaticexternintCoCreateInstance([In]refGuidclsid,
- [MarshalAs(UnmanagedType.Interface)]objectpunkOuter,intcontext,
- [In]refGuidiid,[MarshalAs(UnmanagedType.Interface),Out]outobjectpVoid);
甚至于當(dāng)你有里面對(duì)應(yīng)的接口類(lèi)型的聲明的時(shí)候,完全可以把上面的object或IntPtr換成相應(yīng)的接口類(lèi)型,前提是你的接口類(lèi)型的聲明一定要正確.讀者中用C++做過(guò)COM的一定對(duì)這種方式記憶猶新吧,只不過(guò)這里不再需要什么CoInitialize和CoUninitialize,.NET內(nèi)部自己幫你搞定了.順便提一下,上面例子中的object與IntPtr聲明是相通的,我們可以用Marshal.GetObjectForIUnknown和Marshal.GetIUnknownForObject這兩個(gè)方法在object和IntPtr之間互轉(zhuǎn),前題當(dāng)然是這兩種方式所指向的都是C# COM對(duì)象才行.這種方式提供的傳入?yún)?shù)最多,創(chuàng)建C# COM對(duì)象也最靈活.
【編輯推薦】