主页 > 编程资料 > C# >
发布时间:2015-09-26 作者:网络 阅读:205次
中国最大的WEB开发资源网站及技术社区,
摘要:了解 Microsoft C# 和大型复杂的 COM 服务器之间的 COM 互操作性。本文介绍了如何准备 Office XP COM 对象以及如何在 C# 程序中使用这些对象,同时还提供了一些提示,帮助您理解为何某些操作必须通过特定方法才能实现。

  简介

  Microsoft(r) Office XP 最强大的功能之一,就是其组件(例如 Microsoft Excel 2002 和 Microsoft Word 2002)以组件对象模型 (COM) 接口的形式公开其功能。通过 Microsoft Visual Basic(r) 6.0 访问这些 COM 接口要相对容易些,但要通过 C 或 C++ 来使用这些接口和公共类,则会比较困难。然而,拥有托管扩展的 Microsoft .NET 和 Microsoft C#? 或 Microsoft Visual C++(r) 却能够象 Visual Basic 6.0 一样,轻松地使用 Office XP 公开的 COM 对象。

  本文假定您要进行 Office XP 编程。尽管本文通篇提供了 MSDN(r) 文档的超链接,但要掌握本文介绍的内容,您应该已经熟悉或者能够访问 Office XP 编程文档。

  该文档介绍了 Office XP 提供的接口和公共类及其使用方法。文档内容以 Visual Basic 编程语言形式表达,因此您需要在头脑中对其中的方法和事件签名进行转换。本文将介绍如何进行这种转换,如何准备 Office XP 的 COM 对象,以及如何在 C# 程序中使用这些 COM 对象。最后,本文还提供了一些提示,帮助您理解为何某些操作必须通过特定方法才能实现。通过此信息,您就应能够利用其他使用 C# 的 COM 服务器。

  系统要求
  要运行示例,计算机上需要安装以下软件:

Microsoft Windows(r) XP 或 Microsoft Windows 2000 及相关的 Service Pack (SP)
Microsoft Office XP 及相关的 SP
Microsoft .NET Framework(英文)及相关的 SP
Microsoft Office XP Primary Interop Assemblies (PIA)(英文)
Microsoft Visual Studio(r) .NET
   .NET 的一些快速入门知识
  .NET 技术引入了程序集的概念,并将其作为基本的可执行单元。程序集可以是可执行文件 (.exe) 或动态链接库 (.dll),并可以包含多个文件。程序集包含有关运行程序所需的代码、类型和资源的全部信息。

  要使用 Office XP 公开的 COM 对象,需要使用主互操作程序集 (PIA),这样 C# 编译器就能够找到 Office XP 公开的接口和公共类。

  有关互操作程序集或 PIA 方面的内容,本文将不做详细介绍。

  了解一下已公开的类型信息通常会给您一些启示。Microsoft Visual Studio(r) .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.FileNotFoundExceptionSystem.TypeInitializationException 类型的异常,告知哪个程序集无法加载。

  接下来在“如何编译和运行 example1.cs”一节中,将会介绍如何使用命令行生成 C# 程序和引用 PIA。

  本文包含的示例使用三个 Office XP PIA:

  • Microsoft.Office.Interop.Word.dll
  • Office.dll
  • Microsoft.Office.Interop.Excel.dll

  代码演练

  演示代码示例之前,首先应下载 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();

  Application 类的 Quit 方法接受三个参数:saveChanges、originalFormatrouteDocument。这些可选参数可在 Visual Basic 代码中省略,而 C# 中则没有可选参数;所有这三个参数都必须在调用时传递给 Quit。在 C# 中可通过将值 Missing.Value 赋给每个可选变量(用于通知 Quit 方法使用默认行为)可获得同样效果。在本示例中,即表示“不保存文档,保留文档的初始格式,并且不进行路由选择”。

object saveChanges = Missing.Value;
object originalFormat = Missing.Value;
object routeDocument = Missing.Value;
app.Quit(ref saveChanges, ref originalFormat, ref routeDocument);

  请注意,所有这三个参数都标有 ref 关键字。由于这些方法最初是用 Visual Basic 编写的,而默认情况下 Visual Basic 按引用来传递参数。因此,此处也必须按引用来传递参数。

   如何生成和运行 example1.cs

  要运行该示例,首先要生成 examle1.cs 示例。要在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中生成该示例:

  1. 转到 C:\CSOfficeSamples 目录或保存该示例的任何目录。如图 2 所示,在命令提示后键入 cd C:\CSOfficeSamples 即可。
  2. 然后,在图 2 所示的命令提示后键入 csc /r:"C:\Office XP PIAs\Microsoft.Office.Interop.Word.dll" example1.cs 生成 example1.cs。
    (如果 Office XP PIA 保存在其他位置,则需要使用相应值替换下面的“驱动器”和“安装路径”:csc /r:驱动器:\<安装路径>\Microsoft.Office.Interop.Word.dll example1.cs。)
    注意:命令行 csc 对 example1.c s 源文件进行编译,生成 example1.exe 可执行文件。在本示例中,所创建的可执行文件将自动保存在 example1.cs 所在的同一文件夹中。
    命令行选项 /r 将引用 Microsoft.Office.Interop.Word.dll。如果 Microsoft.Office.Interop.Word.dll PIA(或引用的任何 PIA)所在位置的路径出现错误,程序将在运行时失败,并生成一个 System.IO.FileNotFoundExceptionSystem.TypeInitializationException 类型的异常,告知哪个组件无法加载。

    图 2:使用命令行生成源文件

  3. 要运行 example1.exe(与 example1.cs 源文件位于同一文件夹中),双击该程序即可。

  该示例是一个非常简单的程序,并不具备任何让人感兴趣的功能,现在让我们看一看示例 2。

  示例 2:创建新 Word 文档

  example2.cs 与示例 1 一样,也是使用 Application 对象启动 Word 2002,然后在打开文档的集合(该集合封装在 Application.Documents 属性中)中添加一个新文档。第一个有意义的代码片断在创建新文档时出现的:

<FONT class=90v>object template=Missing.Value;
object newTemplate=Missing.Value;
object documentType=Missing.Value;
object visible=true;
_Document doc = app.Documents.Add( ref template,
ref newTemplate,
ref documentType,
ref visible);</FONT>

  Add 方法的所有参数都是可选的,因此必须给这些参数指定一个有意义的值或是指定 Missing.Value。在该示例中,由于我们不需要使用或创建模板,并且这只是一个纯文本文档,因此将前三个参数(templatenewTemplatedocumentType)设置为 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 将 Booleantrue 封装成 object,并将其传递给 Documents.Add() 函数。

  下一行重要代码是:

  doc.Words.First.InsertBefore

  使用从 app.Documents.Add() 函数调用返回的文档接口,在文档开始处添加了一些文本。此处没有特别之处。

  下面再来看下一段比较让人感兴趣的代码片断,其作用是保存文档:

<FONT class=90v>object fileName = Environment.CurrentDirectory+"\\example2_new";
#if OFFICEXP
doc.SaveAs2000( ref fileName,
#else
doc.SaveAs ( ref fileName,
#endif
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional);</FONT>

  首先要注意的一件事就是,保存文件名称的字符串被封装到 fileName 对象中。其次,此代码将在定义了 OFFICEXP 的情况下调用 SaveAs2000 方法,而在未定义 OFFICEXP 的情况下调用 SaveAs 方法。或许您已经猜到,SaveAs 方法签名在 Office 2000 和 Office XP 之间存在差别。

 如何生成和运行 example2.cs

  要生成 xample2.cs,可以在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中执行以下操作:

  1. 在 C:\CSOfficeSamples 目录或任何保存 example2.cs 的目录中,在如图 3 所示的命令提示后键入 csc /r:"C:\Office XP PIAs\Microsoft.Office.Interop.Word.dll" /d:OFFICEXP example2.cs
    (如果 Office XP PIA 保存在其他位置,则需要使用相应值替换下面的“驱动器”和“安装路径”:csc /r:驱动器:\<安装路径>\Microsoft.Office.Interop.Word.dll /d:OFFICEXP example2.cs。)

    图 3:使用命令行编译 example2.cs

  2. 要运行 example2.exe(与 example2.cs 源文件位于同一文件夹中),双击该程序即可。

  示例 3:打开现有的 Word 文档

  同 Documents.SaveAs 方法一样,Documents.Open 方法签名在 Office 2000 和 OfficeXP 之间也存在差别,因此新名称包装在 #if 声明中。Open 方法和 SaveAs 方法一样简单,如下所示:

<FONT class=90v> object fileName = Environment.CurrentDirectory+"\\example3";
object optional=Missing.Value;
#if OFFICEXP
_Document doc = app.Documents.Open2000( ref fileName,
#else
_Document doc = app.Documents.Open( ref fileName,
#endif
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional,
ref optional);</FONT>

  帮助中的 Word 2002 Visual Basic 参考以及 MSDN(英文)中有关 Documents.Open 方法的说明记录了这些可选参数。

  本示例中比较让人感兴趣的代码是,打开的文档中的文本先被突出显示,然后被剪切:

<FONT class=90v> object first=0;
object last=doc.Characters.Count;
Range r = doc.Range(ref first, ref last);
r.Select();
Thread.Sleep (2000);
r.Cut();</FONT>

  第一个字符和最后一个字符位置的整数值被封装到第一个和最后一个对象,然后传递给 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 命令提示)窗口中执行以下操作:

  1. 打开保存 example3.cs 源文件的目录(例如 C:\CSOfficeSamples),并在命令提示后键入 csc /r:"C:\Office XP PIAs\Microsoft.Office.Interop.Word.dll" /d:OFFICEXP example3.cs
    (如果 Office XP PIA 保存在其他位置,则需要使用相应值替换下面的“驱动器”和“安装路径”:csc /r:驱动器:\<安装路径>\Microsoft.Office.Interop.Word.dll /d:OFFICEXP example3.cs。)
  2. 要运行 example3.exe(与 example3.cs 源文件位于同一文件夹中),双击该程序即可。

  示例 4:使用 Word 公开的事件

  本示例涉及的内容要比其他几个多一些,但实际上并不复杂。看起来复杂的主要原因在于标识事件及其处理程序类型的名称长一些。看一看 Office XP 版本的 DocumentOpenDocumentChange 事件处理程序的设置代码:

... #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
.ApplicationEvents3_DocumentBeforeSaveEventHandler

  请注意,事件并未告诉我们任何有关事件处理程序签名的信息。因此,需要看一下事件处理程序声明。在 ILDASM 中,如果双击 ApplicationEvents3_DocumentBeforeSaveEventHandler 类型,就会看到类似图 6 显示的内容。

图 6:在 ILDASM 中查看事件处理程序声明

  让我们感兴趣的是 Invoke 方法。为事件处理程序编写的函数必须具有此签名。但是如何知道参数的含义及其使用的值呢?这就是 Word 2002 Visual Basic 文档的重要性所在。对于 DocumentBeforeSave 事件,文档(英文)叙述如下:

Private Sub object_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As
Boolean, Cancel As Boolean)

  该文档接下来描述了每个参数的含义。请记住,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) {
MessageBox.Show ("Saving document", "DocumentSave event",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}

  当通过调用 SaveAs 方法保存文档时,DocumentBeforeSave 事件将在保存文档前引发。

  在 SaveAs 方法调用的后面几行代码中,您将看到如下代码片段:

app.DocumentChange -= myChangeDoc;

  此代码行解除了 DocumentChange 事件的挂钩,这样该事件就不会在调用 Quit 期间引发了。

  如何生成和运行 example4.cs

  要生成 example4.cs,可以在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中执行以下操作:

  1. 打开保存 example4.cs 源文件的目录(例如 C:\CSOfficeSamples),并在命令提示后键入 csc /r:"C:\Office XP PIAs\Microsoft.Office.Interop.Word.dll" /d:OFFICEXP example4.cs
    (如果 Office XP PIA 保存在其他位置,则需要使用相应值替换下面的“驱动器”和“安装路径”:csc /r:驱动器:\<安装路径>\Microsoft.Office.Interop.Word.dll /d:OFFICEXP example4.cs。)
  2. 要运行 example4.exe(与 example4.cs 源文件位于同一文件夹中),双击该程序即可。

  示例 5:动画显示 Office 助手

  有些用户喜欢 Office 助手,有些人则讨厌它们。无论如何,example5.cs 在此处都仅仅是为了增添一点乐趣。本示例程序还使用位于 mso.dll 中的助手类型信息。该程序使用两个 PIA:

  • Microsoft.Office.Interop.Word.dll
  • Office.dll

  example5.cs 源文件中的每个重要步骤都作了详细注释。由于易于理解,此处不准备对此代码加以介绍。

  如何生成和运行 example5.cs

  要生成 example5.cs,可以在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中执行以下操作:

  1. 打开保存 example5.cs 源文件的目录(例如 C:\CSOfficeSamples 目录),并在命令提示后键入 csc /r:"C:\Office XP PIAs\Microsoft.Office.Interop.Word.dll" /r:"C:\Office XP PIAs\Office.dll" example5.cs
    (如果 Office XP PIA 保存在其他位置,则需要使用相应值替换下面的“驱动器”和“安装路径”:csc /r:驱动器:\<安装路径>\Microsoft.Office.Interop.Word.dll /r:Drive:\<安装路径>\Office.dll example5.cs。)
  2. 要运行 example5.exe(与 example5.cs 源文件位于同一文件夹中),双击该程序即可。

  示例 6:默认属性和索引属性

  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__Defaultset__Default 的访问函数。如果这两个访问函数存在,C# 就可以使用索引生成器语法而不是直接调用访问函数。本示例中的这两行代码如下所示:

_Worksheet wks2 = (_Worksheet)wkb.Worksheets["Market Share!"];
((_Worksheet)wkb.Worksheets["Market Share!"]).Name = "Fred";

  如何生成和运行 excel1.cs

  要生成 excel1.cs,可以在 Visual Studio .NET Command Prompt(Visual Studio .NET 命令提示)窗口中执行以下操作:

  1. 打开保存 excel1.cs 源文件的目录(例如 C:\CSOfficeSamples 目录),在命令提示后键入 csc /r:"C:\Office XP PIAs\Microsoft.Office.Interop.Excel.dll" /d:OFFICEXP excel1.cs
    (如果 Office XP PIA 保存在其他位置,则需要使用相应值替换下面的“驱动器”和“安装路径”:csc /r:驱动器:\<安装路径>\Microsoft.Office.Interop.Excel.dll /d:OFFICEXP excel1.cs。)
  2. 要运行 excel1.exe(与 excel1.cs 源文件位于同一文件夹中),双击该程序即可。

  小结

  C# 的 COM 互操作是一种非常有用的工具,因为利用它可以直接使用现有对象而无需为那些对象重写代码。本文可帮助您利用现有 COM 对象代码。


关键字词: