Office with .Net (二) ――― 使用.Net訪問(wèn)Office編程接口 在這篇文章里面,我將向大家介紹如何在.Net中訪問(wèn)Office所公開的編程接口。其實(shí),不管是使用哪種具體的技術(shù)來(lái)針對(duì)Office進(jìn)行開發(fā)(比如VSTO,或者用C#編寫一個(gè)Office Add-in,或者在一個(gè)WinForms程序中調(diào)用Office的功能,甚至在一個(gè)ASP.NET應(yīng)用的服務(wù)器端啟動(dòng)一個(gè)Excel進(jìn)程),只要是基于.Net平臺(tái),這篇文章所描述的內(nèi)容都是有價(jià)值的。 在這篇文章以及后續(xù)的文章中,所有的演示都將基于Office 2003 Professional和Visual Studio 2005。使用Visual Studio 2005并不代表我們不能在Visual Studio 2003中使用這些方法來(lái)訪問(wèn)Office編程接口,相反,這篇文章以及后續(xù)文章中的幾乎所有演示,都能在Visual Studio 2003中完成。筆者使用Visual Studio 2005的原因只是因?yàn)橄矚g它更好的IDE特性。:) 一、Office PIA 在第一篇文章中曾經(jīng)說(shuō)過(guò),Office的編程接口都是通過(guò)COM組件公開的,任何訪問(wèn)Office編程接口的程序,實(shí)際上都必須要最終調(diào)用Office COM組件。如果你是一個(gè)剛從事Windows平臺(tái)開發(fā)不太久,一開始就是學(xué)習(xí)和使用.Net的程序員,也許你又要感到郁悶了。所幸的是,.Net能夠讓開發(fā)人員非常方便的訪問(wèn)COM組件。我會(huì)用盡量簡(jiǎn)潔明了的描述,讓大家理解我們?cè)?Net中是如何訪問(wèn)Office COM組件的。 (一)Interop Assembly 在.Net Framework中,提供了一項(xiàng)叫做COM Interop的技術(shù),這項(xiàng)技術(shù)就是專門用于讓我們能夠在.Net代碼中直接訪問(wèn)COM組件的。它的基本原理是,.Net Framework能夠自動(dòng)針對(duì)某一個(gè)COM組件,幫助開發(fā)人員生成一個(gè)Interop Assembly(互調(diào)用程序集,后面簡(jiǎn)稱IA),IA是一個(gè)完全的托管程序集。IA中的名稱空間、類、方法等等,都是針對(duì)那個(gè)COM組件對(duì)應(yīng)的。在我們的.Net程序中,我們可以直接引用這個(gè)IA,并且調(diào)用里面相關(guān)的方法,這時(shí)IA就會(huì)幫我們?cè)偃ピL問(wèn)真正的COM組件里面的相應(yīng)方法。最后的效果就是,在我們自己的應(yīng)用程序中,只需要調(diào)用一個(gè)純粹.Net的IA即可。 下面的圖片說(shuō)明了IA是如何幫我們的程序訪問(wèn)到COM組件的。 生成一個(gè)IA的方法非常簡(jiǎn)單,當(dāng)我們?cè)赩isual Studio開發(fā)環(huán)境中添加一個(gè)新的COM組件引用時(shí),Visual Studio就會(huì)自動(dòng)幫我們生成一個(gè)相應(yīng)的IA。如下圖,我們示范在一個(gè)項(xiàng)目中引用Microsoft XML 5.0組件。 當(dāng)在上圖中點(diǎn)擊OK按鈕,Visual Studio就已經(jīng)自動(dòng)為我們生成了一個(gè)針對(duì)Microsoft XML 5.0組件的IA。打開項(xiàng)目目錄的obj\debug目錄,就能夠看到一個(gè)名稱為Interop.MSXML2.dll的程序集文件,這個(gè)文件就是自動(dòng)生成的IA,并且在項(xiàng)目中,Visual Studio也自動(dòng)引用了這個(gè)IA程序集。如下圖。 (二)Primary Interop Assembly 我們?cè)谧约旱膽?yīng)用程序中,訪問(wèn)Office COM組件的方法的基本原理,就如同下面所述,都是通過(guò)COM Interop,透過(guò)IA間接的訪問(wèn)到Office中的COM組件。但是針對(duì)Office這個(gè)軟件,則有一點(diǎn)點(diǎn)特殊的區(qū)別,那就是我們不應(yīng)該自己在Visual Studio中生成一個(gè)“自己的”訪問(wèn)Office COM的IA,而需要使用微軟提供的“官方的”PIA。 PIA的意思可以理解為“官方互操作程序集”,它和IA最主要的區(qū)別如下: 1、IA是由開發(fā)人員在開發(fā)機(jī)器上通過(guò)向?qū)ё詣?dòng)生成的,PIA是由軟件廠商(針對(duì)Office這個(gè)軟件而言,就是指微軟)提供的; 2、PIA經(jīng)過(guò)了廠商的優(yōu)化處理,使之更容易被.Net調(diào)用; (三)Office Primary Interop Assembly 所以,我們都應(yīng)該使用PIA來(lái)訪問(wèn)Office COM組件,而不應(yīng)該使用IA。那么如何把Office PIA安裝到我們的電腦上呢? 如果我們的機(jī)器上已經(jīng)安裝了.Net Framework,那么在安裝Office時(shí),在安裝向?qū)У母呒?jí)自定義選項(xiàng)中,我們?cè)诿總€(gè)組件(Word、Excel、PowperPoint等)的子選項(xiàng)中,都能看到一個(gè)“.NET可編程性支持”,選擇安裝它,Office2003的安裝程序就會(huì)自動(dòng)把PIA安裝到我們的計(jì)算機(jī)上。 另外,對(duì)于Office PIA的客戶端分發(fā)(就是說(shuō),給我們軟件的用戶都統(tǒng)一裝上PIA),微軟專門提供了一個(gè)安裝包??梢栽?a >http://www.microsoft.com/downloads/details.aspx?FamilyID=3c9a983a-ac14-4125-8ba0-d36d67e0f4ad&DisplayLang=en下載到這個(gè)分發(fā)安裝包。 Office PIA按照Office的各個(gè)組件(Word、Excel、PowerPoint、Outlook等),分成多個(gè)單獨(dú)的程序集。比如Word對(duì)應(yīng)的PIA程序集是Microsoft.Office.Interop.Word.dll(程序集里面的類都放在命名空間Microsoft.Office.Interop.Word中),Excel對(duì)應(yīng)的程序集是Microsoft.Office.Interop.Excel.dll(程序集里面的類都放在命名空間Microsoft.Office.Interop.Excel中)。另外,Office公用的一些組件(比如菜單欄)放在一個(gè)單獨(dú)的程序集中:Office.dll(對(duì)應(yīng)的命名空間是Microsoft.Office.Interop.Core)。 如果我們的開發(fā)機(jī)器上已經(jīng)安裝好了Office PIA,那么當(dāng)我們通過(guò)上面所述的方法,在Visual Studio中引用Office COM組件時(shí),Visual Studio會(huì)檢測(cè)到本機(jī)已經(jīng)安裝了Office PIA,然后,它會(huì)直接引用安裝好了的PIA,而不會(huì)再自動(dòng)生成一個(gè)新的IA。 如下圖,我們?cè)赩isual Studio中添加一個(gè)對(duì)Word COM組件的引用(Word在COM組件列表中是“Microsoft Word 11.0 Object Library”,相似的,Excel、Outlook、PowerPoint的COM組件名稱都遵循這個(gè)規(guī)律)。 在上圖中點(diǎn)擊OK按鈕后,在項(xiàng)目管理器中就可以看到,Visual Studio已經(jīng)幫我們引用了需要引用的組件。實(shí)際上,除了我們選擇要引用的Word組件外,其他額外但是必需的諸如Microsoft.Office.Core、stdole、VBIDE等組件也已經(jīng)被自動(dòng)引用進(jìn)來(lái)了。 在上圖的Word組件引用上點(diǎn)擊鼠標(biāo)右鍵,查看它的屬性,在它的路徑屬性中,我們可以看到這個(gè)PIA文件其實(shí)是在“C:\Windows\assembly\...”目錄中,這個(gè)目錄也就是我們機(jī)器上的全局程序集緩存(GAC,Global Assembly Cache)所在的目錄。這是因?yàn)镺ffice PIA是被安裝到機(jī)器上的GAC中,所以對(duì)Office PIA的引用會(huì)直接指向GAC中的相應(yīng)文件。 二、深入瀏覽Office PIA 如果讀者曾經(jīng)使用過(guò)VBA進(jìn)行過(guò)開發(fā)(或者使用其他的開發(fā)工具諸如VB/VC/Delphi直接調(diào)用過(guò)Office),那么其實(shí)你已經(jīng)對(duì)Office COM接口有了一定的了解,因?yàn)樵赩BA編輯器中所編寫的操作諸如Application、Document、Range的代碼,其實(shí)正是在操作Office COM組件中的Application、Document、Range這些類。 我們已經(jīng)知道,在Office PIA中,已經(jīng)把Office COM組件進(jìn)行了封裝,所以我們可以預(yù)見,對(duì)于每一個(gè)Office COM組件中的類或者接口,在Office PIA的程序集中,我們應(yīng)該都能找到一個(gè)對(duì)應(yīng)的類或者接口。接下來(lái),我們就用對(duì)象瀏覽器直接打開Office COM組件,然后再打開Office PIA,這樣我們就可以對(duì)照它們,更清楚的理解它們。 在Visual Studio中,打開視圖菜單中的對(duì)象瀏覽器,然后點(diǎn)擊對(duì)象瀏覽器中的添加其他組件按鈕,在出現(xiàn)的選擇窗口中,選擇COM組件中的“Microsoft Word 11 Object Library”,這時(shí)對(duì)象瀏覽器就直接打開了Word 2003的COM組件,如下圖。 在上圖所示的Word COM組件成員列表中,可以看到我選中了Word中的Application類的Quit()方法。Application類可以說(shuō)是各個(gè)Office組件的核心類,不管是Word、Excel、PowerPoint,都存在一個(gè)對(duì)應(yīng)的Application類,對(duì)應(yīng)Word、Excel、PowerPoint主程序。如果要在我們的程序中直接打開Word,就需要?jiǎng)?chuàng)建這個(gè)Application類的一個(gè)實(shí)例,如果要關(guān)閉掉這個(gè)新打開的Word程序,就調(diào)用這個(gè)新創(chuàng)建的Application對(duì)象的Quit()方法。 接下來(lái),我們?cè)儆脤?duì)象瀏覽器打開Office PIA中的Word所對(duì)應(yīng)的程序集。在前面的操作步驟中,我們已經(jīng)在項(xiàng)目中引用了Word的PIA,在項(xiàng)目管理器的引用列表中選中Word,點(diǎn)擊鼠標(biāo)右鍵,選擇在對(duì)象瀏覽器中查看,就可以在對(duì)象瀏覽器中打開Word的PIA了。如下圖。 如果在上圖所示的Microsoft.Office.Interop.Word命名空間所包含的類中做一些瀏覽,相信讀者會(huì)發(fā)現(xiàn)一個(gè)很有意思的事情。那就是其實(shí)Word的PIA中的類、接口,并不是和Word的COM組件中的類、接口一一對(duì)應(yīng)的。比如,我們?cè)赪ord COM組件中能夠看到一個(gè)叫做Application的類,但是在Word的PIA中,我們只能找到一個(gè)叫做Application的接口,和一個(gè)叫做ApplicationClass的類。 出現(xiàn)這個(gè)情況的原因,在于.Net的COM Interop(具體說(shuō)就是.Net SDK中的TlbImp.exe這個(gè)命令行工具)幫我們根據(jù)COM組件生成Interop Assembly時(shí),其實(shí)是不會(huì)一一對(duì)照COM組件來(lái)生成.Net類和接口的。相反,它會(huì)根據(jù)一定的規(guī)則,來(lái)生成對(duì)應(yīng)的.Net類和接口。 由于Application是Word編程接口中最重要的部分,所以我具體針對(duì)Word中的Application這個(gè)接口,把它的轉(zhuǎn)換規(guī)則簡(jiǎn)要的說(shuō)明一下(實(shí)際生成的接口和類比下面描述的要更多,相關(guān)的關(guān)系更復(fù)雜)。首先,Word PIA中會(huì)生成一個(gè)_Application接口,這個(gè)_Application接口基本描述了Word COM組件中的Application類中的所有操作和屬性,然后,Word PIA中還會(huì)生成多個(gè)ApplicationEvents_Event系列接口(ApplicationEvents2_Event、ApplicationEvents3_Event、ApplicationEvents4_Event接口…我們可以不用管這些具體的細(xì)節(jié)),這個(gè)接口基本描述了Word COM組件中的Application類中的所有事件。然后,Word PIA中會(huì)生成一個(gè)Applicatin接口,它實(shí)現(xiàn)了_Application接口和ApplicationEvents_Even接口,這樣,Application接口就基本描述了Word COM組件中的Application類中的所有操作、屬性、事件等等。最后,Word PIA中生成了一個(gè)具體的ApplicationClass類,這個(gè)類實(shí)現(xiàn)了Applicatin接口。 如果你已經(jīng)被上面那一段描述搞得頭昏腦脹,那么只需要記?。涸赪ord PIA中,我們有一個(gè)Application接口和一個(gè)ApplicationClass類,Application接口描述了對(duì)應(yīng)的Word COM組件中的Application類的所有成員,而ApplicationClass類是具體的實(shí)現(xiàn)類。 三、Code WalkThrough:一個(gè).Net WinForms程序 終于,在你忍受了N久,勉強(qiáng)看完了上面那些羅嗦的文字之后,總算可以看到一個(gè)具體的示范了。我們要用C#寫一個(gè)Windows應(yīng)用程序,在這個(gè)程序中,啟動(dòng)Word,用代碼操作它做一些操作,然后再關(guān)閉掉它。 首先,我們創(chuàng)建一個(gè)新的C# Windows應(yīng)用程序,然后通過(guò)上面介紹過(guò)的方法,在項(xiàng)目中引用Word的PIA(在添加引用的界面中,選擇COM組件列表中的Microsoft Word 11 Object Library)。 在自動(dòng)創(chuàng)建的啟動(dòng)窗體上,放兩個(gè)Button控件,一個(gè)叫做btnStartWord,另外一個(gè)叫做btnStopWord。我們希望當(dāng)用戶點(diǎn)擊btnStartWord時(shí),我們的程序自動(dòng)啟動(dòng)Word,然后創(chuàng)建一個(gè)新的Word文檔,然后將其自動(dòng)保存在磁盤上,當(dāng)用戶點(diǎn)擊btnStopWord時(shí),就關(guān)閉掉Word。窗體設(shè)計(jì)視圖如下。 在這個(gè)主窗體類的源代碼中,我們引用Word PIA的名稱空間,我們使用MSWord來(lái)替代Microsoft.Office.Interop.Word這個(gè)完整的命名空間名稱: using MSWord = Microsoft.Office.Interop.Word; 在主窗體類的源碼中,添加一個(gè)類級(jí)別的成員,_wordApp是一個(gè)Application類型的對(duì)象(記?。篗SWord.Application是一個(gè)接口!): private MSWord.Application _wordApp = null; 然后在btnStartWord按鈕的點(diǎn)擊事件代碼中,添加如下代碼,代碼創(chuàng)建一個(gè)新的Word實(shí)例,然后顯示它: _wordApp = new MSWord.Application(); _wordApp.Visible = true; 看到這里,很多人有一個(gè)非常大的疑惑,那就是MSWord.Application實(shí)際上是一個(gè)接口,那么我們?cè)趺纯赡芡ㄟ^(guò)“new MSWord.Application()”來(lái)創(chuàng)建一個(gè)Word實(shí)例呢?難道我們不應(yīng)該使用“new MSWord.ApplicationClass()”來(lái)做嗎?畢竟ApplicationClass才是實(shí)現(xiàn)Application接口的具體類啊。 在這里,Office PIA為我們提供了一個(gè)小小的“cookie”,我們實(shí)際上的確可以使用“new MSWord.Application()”來(lái)創(chuàng)建一個(gè)Word程序?qū)嵗模覀冎恍枰?,Office PIA會(huì)在底下自動(dòng)幫我們創(chuàng)建一個(gè)真正的Word程序?qū)嵗?/p> 接下來(lái),我們?cè)赽tnStartWord按鈕的事件代碼中,再添加如下的代碼。 Object missing = Type.Missing; Object sFileName = "C:\\Sample.doc"; MSWord.Documents docs = _wordApp.Documents; MSWord.Document doc = docs.Add(ref missing, ref missing, ref missing, ref missing); doc.SaveAs(ref sFileName, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing); doc.Close(ref missing, ref missing, ref missing); 在上面的代碼中,我們通過(guò)訪問(wèn)Application的Documents屬性,得到一個(gè)Documents類,然后通過(guò)Documents類的Add()方法,創(chuàng)建一個(gè)新文檔,并得到對(duì)應(yīng)這個(gè)新文檔的類型為Document的對(duì)象,然后通過(guò)Document類的SaveAs()方法將文檔保存在磁盤上,最后通過(guò)Document類的Close()方法關(guān)閉這個(gè)新文檔。 嗯,我知道我其實(shí)解釋得不夠徹底,上面代碼中的那些ref、missing之類的東東,到底是什么意思,為什么要用它們,相信不少人都非常迷惑(特別是曾經(jīng)用VBA或其他語(yǔ)言訪問(wèn)過(guò)Office COM組件接口的程序員而言)。這其實(shí)牽涉到用C#語(yǔ)言調(diào)用Office編程接口的一個(gè)“語(yǔ)法兼容”問(wèn)題。就是說(shuō),Office的產(chǎn)品開發(fā)組在對(duì)編程接口進(jìn)行設(shè)計(jì)時(shí),實(shí)際上是專門設(shè)計(jì)為被VBA調(diào)用的,所以接口都非常配合VBA的語(yǔ)法,使VBA程序員盡量感到方便。但是由于C#語(yǔ)法和VB語(yǔ)法有很多不同,所以在用C#訪問(wèn)Office編程接口時(shí),就會(huì)感到非常的“別扭”。 這這里,我只想對(duì)上面的代碼做如下簡(jiǎn)要的額外解釋: (1)很多的Office編程接口中的方法,都帶了非常多的參數(shù)(比如Document.SaveAs()方法有16個(gè)參數(shù)?。。。鴮?shí)際上我們調(diào)用它們的時(shí)候,并不是每一個(gè)參數(shù)都需要明確給一個(gè)特定的值的(比如Document.SaveAs()方法只需要明確給定第一個(gè)參數(shù),即保存到哪里),那么對(duì)于不需要給定明確值的參數(shù),我們可以直接傳一個(gè).Net類庫(kù)中自帶的靜態(tài)對(duì)象:Type.Missing就可以了。 (2)很多的Office編程接口中的方法,其參數(shù)都必需傳引用,而不能傳值,所以,調(diào)用這些方法的時(shí)候,對(duì)于參數(shù)都需要加上C#中的ref關(guān)鍵字。比如上面代碼中的Document.Add()、Document.SaveAs()、Document.Close()方法,它們的參數(shù)都必須傳引用,所以每個(gè)參數(shù)前面都加上了ref關(guān)鍵字。 對(duì)于使用C#語(yǔ)言調(diào)用Office編程接口時(shí),對(duì)“語(yǔ)法兼容”問(wèn)題的更全面的描述,請(qǐng)參看《Office with .Net(二)之外傳―――C#訪問(wèn)Office編程接口時(shí)的“語(yǔ)法兼容”問(wèn)題》。 繼續(xù)為我們的項(xiàng)目添加代碼。在btnStopWord按鈕的事件代碼中,填充下面的代碼: Object missing = Type.Missing; _wordApp.Quit(ref missing, ref missing, ref missing); _wordApp = null; GC.Collect(); GC.WaitForPendingFinalizers GC.Collect(); GC.WaitForPendingFinalizers 上面的代碼通過(guò)調(diào)用Application.Quit()方法,退出Word程序。更值得關(guān)注的部分是如何釋放掉Word程序?qū)嵗?。上面的代碼用了一個(gè)簡(jiǎn)單但是很有效的方法,讓W(xué)ord程序進(jìn)程被關(guān)閉回收,首先將對(duì)象_wordApp重新賦值為null,這樣底下的那個(gè)Word Application對(duì)象將不再被任何變量引用,最后通過(guò)強(qiáng)行調(diào)用.Net Framework中的垃圾收集方法,使Application對(duì)象能夠被垃圾回收器回收掉。(實(shí)際上,.Net Framework的垃圾回收器回收的只是Office PIA的一個(gè)包裝類對(duì)象而已,但是這個(gè)包裝類對(duì)象被回收后,對(duì)應(yīng)Word程序的COM組件對(duì)象會(huì)發(fā)現(xiàn)自己不再被其他任何對(duì)象引用,引用計(jì)數(shù)變?yōu)榱?,于是,那個(gè)COM組件就會(huì)被真正釋放掉了。) 如何保證在自己的應(yīng)用程序中關(guān)閉Office程序其實(shí)是一個(gè)不小的問(wèn)題,上面描述的方法并不是100%有效的,對(duì)這個(gè)話題更完整的描述請(qǐng)參考《Office with .Net(二)之外傳―――“徹底干凈的”關(guān)閉Office程序》一文。 我們的第一個(gè)示范程序到這里就已經(jīng)寫完了,現(xiàn)在我們可以運(yùn)行一下這個(gè)程序,然后先點(diǎn)擊第一個(gè)按鈕啟動(dòng)Word,并操作Word創(chuàng)建一個(gè)新文檔后再保存到磁盤上,接著點(diǎn)擊第二個(gè)按鈕關(guān)閉掉Word。 (四)總結(jié) 這篇文檔簡(jiǎn)要描述了如何在.Net中訪問(wèn)Office的編程接口,講解了Office PIA的概念和使用方法。從這篇文章可以看出,在.Net中操作Office是非常簡(jiǎn)單而直接的,微軟通過(guò)提供Office PIA,大大簡(jiǎn)化了.Net程序員的工作。 |
|
來(lái)自: 快樂(lè)至上365 > 《.net》