摘要:了解 Microsoft C# 和大型復雜的 COM 服務器之間的 COM 互操作性。本文介紹了如何准備 Office XP COM 對象以及如何在 C# 程序中使用這些對象,同時還提供了一些提示,幫助您理解為何某些操作必須通過特定方法才能實現。
簡介
Microsoft® Office XP 最強大的功能之一,就是其組件(例如 Microsoft Excel 2002 和 Microsoft Word 2002)以組件對象模型 (COM) 接口的形式公開其功能。通過 Microsoft Visual Basic® 6.0 訪問這些 COM 接口要相對容易些,但要通過 C 或 C++ 來使用這些接口和公共類,則會比較困難。然而,擁有托管擴展的 Microsoft .NET 和 Microsoft C#™ 或 Microsoft Visual C++® 卻能夠象 Visual Basic 6.0 一樣,輕松地使用 Office XP 公開的 COM 對象。
本文假定您要進行 Office XP 編程。盡管本文通篇提供了 MSDN® 文檔的超鏈接,但要掌握本文介紹的內容,您應該已經熟悉或者能夠訪問 Office XP 編程文檔。
該文檔介紹了 Office XP 提供的接口和公共類及其使用方法。文檔內容以 Visual Basic 編程語言形式表達,因此您需要在頭腦中對其中的方法和事件簽名進行轉換。本文將介紹如何進行這種轉換,如何准備 Office XP 的 COM 對象,以及如何在 C# 程序中使用這些 COM 對象。最後,本文還提供了一些提示,幫助您理解為何某些操作必須通過特定方法才能實現。通過此信息,您就應能夠利用其他使用 C# 的 COM 服務器。
系統要求要運行示例,計算機上需要安裝以下軟件:
.NET 技術引入了程序集的概念,並將其作為基本的可執行單元。程序集可以是可執行文件 (.exe) 或動態鏈接庫 (.dll),並可以包含多個文件。程序集包含有關運行程序所需的代碼、類型和資源的全部信息。
要使用 Office XP 公開的 COM 對象,需要使用主互操作程序集 (PIA),這樣 C# 編譯器就能夠找到 Office XP 公開的接口和公共類。
有關互操作程序集或 PIA 方面的內容,本文將不做詳細介紹。
了解一下已公開的類型信息通常會給您一些啟示。Microsoft Visual Studio® .NET 提供了一個稱作 ILDASM 的工具,用於列出封裝在程序集中的類型信息。圖 1 是 ILDASM 顯示 Word 2002 主互操作性程序集信息的部分屏幕快照。
注意:要打開 ILDASM 工具,請單擊“開始”,指向“程序”,指向“Microsoft Visual Studio .NET”,然後指向 Visual Studio .NET Tools(Visual Studio .NET 工具)並單擊 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)。在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中,鍵入 ildasm。ILDASM 窗口隨後打開。要查看某一特定互操作程序集或 PIA 的類型信息,在 File(文件)菜單中,單擊 Open(打開)。浏覽到互操作程序集或 PIA 的所在位置,選擇要查看的互操作程序集或 PIA 並單擊 Open(打開)。
圖 1:使用 ILDASM 工具查看互操作程序集的類型信息
如圖 1 所示,程序集位於 Microsoft.Office.Interop.Word.dll 中,而接口和公共類則封裝在 Microsoft.Office.Interop.Word 命名空間中。Application 公共類已經展開,這樣就可以看到,它擴展(按照 C++ 和 C# 用語,為派生)了 Application,並且在 Word 中實現了 ApplicationEvents2_Event 接口。所有這些內容都將在本文後續部分進行詳細討論。
使用 Office XP 主互操作程序集
在運行本文包含的示例之前,應該在計算機上安裝 Microsoft Office XP Primary Interop Assemblies (PIAs)(英文)。安裝完 PIA 後必須將其置於編譯器和已完成程序可以訪問的位置。有關詳細信息,請參閱 Office XP PIA 下載文檔中包含的自述文件和“.NET Framework Developer's Guide”(要閱讀它,請單擊“開始”,指向“程序”,然後指向 Microsoft .NET Framework SDK 並單擊 Documentation [文檔])中的“Assembly Location”一文。
本文出於演示目的,將 Office XP PIA 解壓縮到以下文件夾:C:\Office XP PIAs\。然後將其安裝到全局程序集緩存 (GAC) 並進行注冊。
可通過在命令行鍵入 C# 編譯器的可執行文件名稱 (csc.exe) 來調用此編譯器。安裝並注冊 PIA 後,便可使用 /r 選項,象引用其他任何程序集一樣在 csc 命令行上對其進行引用。如果 PIA 的位置無法訪問,程序將在運行時失敗,並生成一個 System.IO.FileNotFoundException 或 System.TypeInitializationException 類型的異常,告知哪個程序集無法加載。
接下來在“如何編譯和運行 example1.cs”一節中,將會介紹如何使用命令行生成 C# 程序和引用 PIA。
本文包含的示例使用三個 Office XP PIA:
演示代碼示例之前,首先應下載 odc_offcs.exe 文件並將示例程序解壓縮到 C:\CSOfficeSamples 或您選擇的目錄中。為便於引用,在下面的所有示例中,都假定示例程序位於 C:\CSOfficeSamples 目錄中。
下載文檔包含五個 Word 2002 示例程序(example1.cs、example2.cs、example3.cs、example4.cs 和 example5.cs)和一個 Excel 2002 示例程序 (excel1.cs)。示例源文件的相應示例生成文件(example1.exe、example2.exe 等)也一並包含在其中,供讀者使用。
所有代碼示例都作了詳細注釋。
示例 1:啟動 Word Application 對象第一個示例非常簡單,只顯示如何啟動 Word 2002,並使其在幾秒內保持打開狀態,然後再將其關閉。首先看一看 example1.cs 源文件中的主要代碼行。下面的代碼片段分配 Application 對象和它的基類對象,但實際上是進行 CoCreateInstance 調用。
Application app = new Application();saveChanges、originalFormat
和 routeDocument
。這些可選參數可在 Visual Basic 代碼中省略,而 C# 中則沒有可選參數;所有這三個參數都必須在調用時傳遞給 Quit。在 C# 中可通過將值 Missing.Value
賦給每個可選變量(用於通知 Quit 方法使用默認行為)可獲得同樣效果。在本示例中,即表示“不保存文檔,保留文檔的初始格式,並且不進行路由選擇”。 請注意,所有這三個參數都標有 ref 關鍵字。由於這些方法最初是用 Visual Basic 編寫的,而默認情況下 Visual Basic 按引用來傳遞參數。因此,此處也必須按引用來傳遞參數。
如何生成和運行 example1.cs要運行該示例,首先要生成 examle1.cs 示例。要在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中生成該示例:
注意:命令行 csc 對 example1.c s 源文件進行編譯,生成 example1.exe 可執行文件。在本示例中,所創建的可執行文件將自動保存在 example1.cs 所在的同一文件夾中。
命令行選項 /r 將引用 Microsoft.Office.Interop.Word.dll。如果 Microsoft.Office.Interop.Word.dll PIA(或引用的任何 PIA)所在位置的路徑出現錯誤,程序將在運行時失敗,並生成一個 System.IO.FileNotFoundException 或 System.TypeInitializationException 類型的異常,告知哪個組件無法加載。
圖 2:使用命令行生成源文件
該示例是一個非常簡單的程序,並不具備任何讓人感興趣的功能,現在讓我們看一看示例 2。
示例 2:創建新 Word 文檔example2.cs 與示例 1 一樣,也是使用 Application 對象啟動 Word 2002,然後在打開文檔的集合(該集合封裝在 Application.Documents 屬性中)中添加一個新文檔。第一個有意義的代碼片斷在創建新文檔時出現的:
<FONT class=90v>object template=Missing.Value; Add 方法的所有參數都是可選的,因此必須給這些參數指定一個有意義的值或是指定 Missing.Value。在該示例中,由於我們不需要使用或創建模板,並且這只是一個純文本文檔,因此將前三個參數(template
、newTemplate
和 documentType
)設置為 Missing.Value
。由於希望此文檔在本示例中可見,因此將參數 visible 設置為“true”。
您可能會對如何確定是否應將 Boolean 值賦給 visible
對象感到不解。這就是為什麼訪問 Word 2002 編程文檔很重要的原因所在了。如果看一看 Word 2002 對象模型文檔中有關 Documents.Add 方法的說明,您會看到以下內容:
Visible 可選的 Variant。設置為 True 將在可見窗口中打開文檔。如果該值為 False,Microsoft Word 將打開文檔,但將文檔窗口的 Visible 屬性設置為 False。默認值為 True。
注意:要查看 Word 2002 Visual Basic 文檔中的 Documents.Add 方法,可以在 Word 2002 的“工具”菜單中,選擇“宏”,然後單擊“Visual Basic 編輯器”。處於“Visual Basic 編輯器”的鍵盤狀態下時,按 F2 鍵激活“對象浏覽器”或按 F1 鍵查看“幫助”。然後搜索“Documents”或“Documents.Add”。在 MSDN 上也可以找到類似文檔。
這樣做回避了一個問題:為什麼 PIA 期望 Add 方法的參數類型為 object,而 Documents.Add 方法文檔卻顯示類型 Variant?這是因為 Variant 類型被自動封送處理為 .NET Object 對象類型,後者映射為 C# 的 object 類型。在本示例中,參數 visible 將 Boolean 值 true 封裝成 object,並將其傳遞給 Documents.Add()
函數。
下一行重要代碼是:
doc.Words.First.InsertBefore 使用從 app.Documents.Add()
函數調用返回的文檔接口,在文檔開始處添加了一些文本。此處沒有特別之處。
下面再來看下一段比較讓人感興趣的代碼片斷,其作用是保存文檔:
<FONT class=90v>object fileName = Environment.CurrentDirectory+"\\example2_new";首先要注意的一件事就是,保存文件名稱的字符串被封裝到 fileName 對象中。其次,此代碼將在定義了 OFFICEXP 的情況下調用 SaveAs2000 方法,而在未定義 OFFICEXP 的情況下調用 SaveAs 方法。或許您已經猜到,SaveAs 方法簽名在 Office 2000 和 Office XP 之間存在差別。
如何生成和運行 example2.cs
要生成 xample2.cs,可以在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中執行以下操作:
圖 3:使用命令行編譯 example2.cs
同 Documents.SaveAs 方法一樣,Documents.Open 方法簽名在 Office 2000 和 OfficeXP 之間也存在差別,因此新名稱包裝在 #if 聲明中。Open 方法和 SaveAs 方法一樣簡單,如下所示:
<FONT class=90v> object fileName = Environment.CurrentDirectory+"\\example3";幫助中的 Word 2002 Visual Basic 參考以及 MSDN(英文)中有關 Documents.Open 方法的說明記錄了這些可選參數。
本示例中比較讓人感興趣的代碼是,打開的文檔中的文本先被突出顯示,然後被剪切:
<FONT class=90v> object first=0; 第一個字符和最後一個字符位置的整數值被封裝到第一個和最後一個對象,然後傳遞給 Document.Range() 函數,該函數返回 Select() 函數調用的 Range 對象。這種顯式封裝是必需的,因為 Range 對象期待引用其參數,並且任何隱式或顯式的轉換都會將參數改為右值,而右值是不能按引用傳遞的。本示例使文本突出顯示持續兩秒鐘,而後對文本進行剪切。剪切操作也可以通過以下代碼實現:
object first=0; object units = WdUnits.wdCharacter; object last=doc.Characters.Count; doc.Range(ref first, ref last).Delete(ref units, ref last);
如何生成和運行 example3.cs
要生成 example3.cs,可以在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中執行以下操作:
本示例涉及的內容要比其他幾個多一些,但實際上並不復雜。看起來復雜的主要原因在於標識事件及其處理程序類型的名稱長一些。看一看 Office XP 版本的 DocumentOpen 和 DocumentChange 事件處理程序的設置代碼:
... #if OFFICEXP ApplicationEvents3_DocumentOpenEventHandler myOpenDoc = new ApplicationEvents3_DocumentOpenEventHandler (MyOpenEventHandler); ApplicationEvents3_DocumentChangeEventHandler myChangeDoc = new ApplicationEvents3_DocumentChangeEventHandler(DocChange); #else ... 這兩條語句僅僅是聲明事件的事件處理程序。隨後的幾行代碼中,這些處理程序將指定給 Application 對象 app
中的事件:
app.DocumentOpen += myOpenDoc; app.DocumentChange += myChangeDoc;
現在就可以使用這兩個事件了。調用 Open 方法時,這兩個事件將同時引發。依次打開超鏈接閱讀有關 DocumentOpen(英文)和 DocumentChange(英文)方法的文檔。
那麼,如何知道哪些事件可用及其處理程序的調用方法呢?如果使用 ILDASM 檢查 Word 2002 PIA (Microsoft.Office.Interop.Word.dll),會發現在有些類型前面標有綠色倒三角標志。該標志表示成員是一個事件。圖 4 顯示了 ILDASM 樹視圖圖標的幫助。
圖 4:ILDASM 的樹視圖圖標幫助
圖 5:使用 ILDASM 查看 Application 對象的事件
圖 5 顯示了 Application 對象的事件的部分屏幕快照。每一行最左邊的標識符是事件名稱。冒號右邊是事件處理程序的完整限定類型名。例如,DocumentBeforeSave 事件要求有如下類型的處理程序:
Microsoft.Office.Interop.Word請注意,事件並未告訴我們任何有關事件處理程序簽名的信息。因此,需要看一下事件處理程序聲明。在 ILDASM 中,如果雙擊 ApplicationEvents3_DocumentBeforeSaveEventHandler 類型,就會看到類似圖 6 顯示的內容。
圖 6:在 ILDASM 中查看事件處理程序聲明
讓我們感興趣的是 Invoke 方法。為事件處理程序編寫的函數必須具有此簽名。但是如何知道參數的含義及其使用的值呢?這就是 Word 2002 Visual Basic 文檔的重要性所在。對於 DocumentBeforeSave 事件,文檔(英文)敘述如下:
Private Sub object_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As該文檔接下來描述了每個參數的含義。請記住,C# 在默認情況下按值傳遞參數,而 Visual Basic 在默認情況下按引用傳遞參數。這就是為什麼兩個 Boolean 參數在用 ILDASM 顯示時後面要跟 & 符號,而在 C# 中使用時則用關鍵字 ref 標記的原因了。同樣,Visual Basic 中的 Subs 在 C# 中被看作返回 void 的方法。因此,DocumentSave 事件的處理程序應類似於如下所示:
public static void SaveHandler (Document doc, ref bool b1, ref bool b2) {當通過調用 SaveAs 方法保存文檔時,DocumentBeforeSave 事件將在保存文檔前引發。
在 SaveAs 方法調用的後面幾行代碼中,您將看到如下代碼片段:
app.DocumentChange -= myChangeDoc;
此代碼行解除了 DocumentChange 事件的掛鉤,這樣該事件就不會在調用 Quit 期間引發了。
如何生成和運行 example4.cs要生成 example4.cs,可以在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中執行以下操作:
有些用戶喜歡 Office 助手,有些人則討厭它們。無論如何,example5.cs 在此處都僅僅是為了增添一點樂趣。本示例程序還使用位於 mso.dll 中的助手類型信息。該程序使用兩個 PIA:
example5.cs 源文件中的每個重要步驟都作了詳細注釋。由於易於理解,此處不准備對此代碼加以介紹。
如何生成和運行 example5.cs要生成 example5.cs,可以在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中執行以下操作:
Word 2002 很少用到默認屬性和索引屬性,而 Excel 2002 卻經常用到它們,因此本示例 (excel1.cs) 利用了這一事實。
同所有 Office XP 互操作代碼一樣,本示例程序從實例化 Application 對象開始。創建工作簿和工作表後,創建了一個用於保存列標題的字符串數組。創建完該數組後,您將看到如下代碼片段:
wksRange = wks.get_Range("A2", "D2");此代碼獲取單元格 A2 到 D2 的 Range 對象。但既然工作表有一個 Range 屬性,為什麼還需要直接調用訪問函數呢?並且這樣做為何不象通常那樣會產生語法錯誤?
與 Visual Basic 和 Visual C++ 不同,C# 沒有適用於索引屬性的語法結構。要在 C# 中使用索引屬性,就必須直接調用訪問函數。_Worksheet.Range 屬性便是一個很好的例子。要在 Visual C++ 中獲取 Range 屬性的值,代碼應如下所示:
myRange = myWorksheet->Range["A2", "D2"];要在 C# 中執行相同的操作,代碼則應如下所示:
myRange = myWorksheet.get_Range("A2", "D2");設置 Range 屬性,而不是向其賦值,是對 set 訪問函數的調用:
myWorksheet.set_Range("A2", "D2", myRange);Microsoft Excel 2000 中的 Range.Value 屬性是一個常規屬性,但在 Excel 2002 中,則變成了一個索引屬性。這就是為什麼在本示例程序中使用該屬性時要將其括在 #if OFFICEXP 語句中的原因。
_Workbook.Worksheets 具有所謂的默認屬性。默認屬性在互操作程序集中被看作是名稱為 Item 的屬性。通常必須指定 Item 成員才能從 C# 使用默認屬性,但是在 Excel 庫中,TLBIMP 只需少量代碼就可以創建稱為 get__Default 或 set__Default 的訪問函數。如果這兩個訪問函數存在,C# 就可以使用索引生成器語法而不是直接調用訪問函數。本示例中的這兩行代碼如下所示:
_Worksheet wks2 = (_Worksheet)wkb.Worksheets["Market Share!"];要生成 excel1.cs,可以在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中執行以下操作:
C# 的 COM 互操作是一種非常有用的工具,因為利用它可以直接使用現有對象而無需為那些對象重寫代碼。本文可幫助您利用現有 COM 對象代碼。