版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://aishengwu03.52blog.net/logs/2990007.html
在这篇文章里面,我将向大家介绍如何在.Net中访问Office所公开的编程接口。其实,不管是使用哪种具体的技术来针对Office进行开发(比如VSTO,或者用C#编写一个Office Add-in,或者在一个WinForms程序中调用Office的功能,甚至在一个ASP.NET应用的服务器端启动一个Excel进程),只要是基于.Net平台,这篇文章所描述的内容都是有价值的。
在这篇文章以及后续的文章中,所有的演示都将基于Office 2003 Professional和Visual Studio 2005。使用Visual Studio 2005并不代表我们不能在Visual Studio 2003中使用这些方法来访问Office编程接口,相反,这篇文章以及后续文章中的几乎所有演示,都能在Visual Studio 2003中完成。笔者使用Visual Studio 2005的原因只是因为喜欢它更好的IDE特性。:)
一、Office PIA
在第一篇文章中曾经说过,Office的编程接口都是通过COM组件公开的,任何访问Office编程接口的程序,实际上都必须要最终调用Office COM组件。如果你是一个刚从事Windows平台开发不太久,一开始就是学习和使用.Net的程序员,也许你又要感到郁闷了。所幸的是,.Net能够让开发人员非常方便的访问COM组件。我会用尽量简洁明了的描述,让大家理解我们在.Net中是如何访问Office COM组件的。
(一)Interop Assembly
在.Net work中,提供了一项叫做COM Interop的技术,这项技术就是专门用于让我们能够在.Net代码中直接访问COM组件的。它的基本原理是,.Net work能够自动针对某一个COM组件,帮助开发人员生成一个Interop Assembly(互调用程序集,后面简称IA),IA是一个完全的托管程序集。IA中的名称空间、类、方法等等,都是针对那个COM组件对应的。在我们的.Net程序中,我们可以直接引用这个IA,并且调用里面相关的方法,这时IA就会帮我们再去访问真正的COM组件里面的相应方法。最后的效果就是,在我们自己的应用程序中,只需要调用一个纯粹.Net的IA即可。
下面的图片说明了IA是如何帮我们的程序访问到COM组件的。
生成一个IA的方法非常简单,当我们在Visual Studio开发环境中添加一个新的COM组件引用时,Visual Studio就会自动帮我们生成一个相应的IA。如下图,我们示范在一个项目中引用Microsoft XML 5.0组件。
当在上图中点击OK按钮,Visual Studio就已经自动为我们生成了一个针对Microsoft XML 5.0组件的IA。打开项目目录的obj/debug目录,就能够看到一个名称为Interop.MSXML2.dll的程序集文件,这个文件就是自动生成的IA,并且在项目中,Visual Studio也自动引用了这个IA程序集。如下图。
(二)Primary Interop Assembly
我们在自己的应用程序中,访问Office COM组件的方法的基本原理,就如同下面所述,都是通过COM Interop,透过IA间接的访问到Office中的COM组件。但是针对Office这个软件,则有一点点特殊的区别,那就是我们不应该自己在Visual Studio中生成一个“自己的”访问Office COM的IA,而需要使用微软提供的“官方的”PIA。
PIA的意思可以理解为“官方互操作程序集”,它和IA最主要的区别如下:
1、IA是由开发人员在开发机器上通过向导自动生成的,PIA是由软件厂商(针对Office这个软件而言,就是指微软)提供的;
2、PIA经过了厂商的优化处理,使之更容易被.Net调用;
(三)Office Primary Interop Assembly
所以,我们都应该使用PIA来访问Office COM组件,而不应该使用IA。那么如何把Office PIA安装到我们的电脑上呢?
如果我们的机器上已经安装了.Net work,那么在安装Office时,在安装向导的高级自定义选项中,我们在每个组件(Word、Excel、PowperPoint等)的子选项中,都能看到一个“.NET可编程性支持”,选择安装它,Office2003的安装程序就会自动把PIA安装到我们的计算机上。
另外,对于Office PIA的客户端分发(就是说,给我们软件的用户都统一装上PIA),微软专门提供了一个安装包。可以在http://www.microsoft.com/downloads/details.aspx?FamilyID=3c9a983a-ac14-4125-8ba0-d36d67e0f4ad&DisplayLang=en下载到这个分发安装包。
Office PIA按照Office的各个组件(Word、Excel、PowerPoint、Outlook等),分成多个单独的程序集。比如Word对应的PIA程序集是Microsoft.Office.Interop.Word.dll(程序集里面的类都放在命名空间Microsoft.Office.Interop.Word中),Excel对应的程序集是Microsoft.Office.Interop.Excel.dll(程序集里面的类都放在命名空间Microsoft.Office.Interop.Excel中)。另外,Office公用的一些组件(比如菜单栏)放在一个单独的程序集中:Office.dll(对应的命名空间是Microsoft.Office.Interop.Core)。
如果我们的开发机器上已经安装好了Office PIA,那么当我们通过上面所述的方法,在Visual Studio中引用Office COM组件时,Visual Studio会检测到本机已经安装了Office PIA,然后,它会直接引用安装好了的PIA,而不会再自动生成一个新的IA。
如下图,我们在Visual Studio中添加一个对Word COM组件的引用(Word在COM组件列表中是“Microsoft Word 11.0 Object Library”,相似的,Excel、Outlook、PowerPoint的COM组件名称都遵循这个规律)。
在上图中点击OK按钮后,在项目管理器中就可以看到,Visual Studio已经帮我们引用了需要引用的组件。实际上,除了我们选择要引用的Word组件外,其他额外但是必需的诸如Microsoft.Office.Core、stdole、VBIDE等组件也已经被自动引用进来了。
在上图的Word组件引用上点击鼠标右键,查看它的属性,在它的路径属性中,我们可以看到这个PIA文件其实是在“C:/Windows/assembly/...”目录中,这个目录也就是我们机器上的全局程序集缓存(GAC,Global Assembly Cache)所在的目录。这是因为Office PIA是被安装到机器上的GAC中,所以对Office PIA的引用会直接指向GAC中的相应文件。
二、深入浏览Office PIA
如果读者曾经使用过VBA进行过开发(或者使用其他的开发工具诸如VB/VC/Delphi直接调用过Office),那么其实你已经对Office COM接口有了一定的了解,因为在VBA编辑器中所编写的操作诸如Application、Document、Range的代码,其实正是在操作Office COM组件中的Application、Document、Range这些类。
我们已经知道,在Office PIA中,已经把Office COM组件进行了封装,所以我们可以预见,对于每一个Office COM组件中的类或者接口,在Office PIA的程序集中,我们应该都能找到一个对应的类或者接口。接下来,我们就用对象浏览器直接打开Office COM组件,然后再打开Office PIA,这样我们就可以对照它们,更清楚的理解它们。
在Visual Studio中,打开视图菜单中的对象浏览器,然后点击对象浏览器中的添加其他组件按钮,在出现的选择窗口中,选择COM组件中的“Microsoft Word 11 Object Library”,这时对象浏览器就直接打开了Word 2003的COM组件,如下图。
在上图所示的Word COM组件成员列表中,可以看到我选中了Word中的Application类的Quit()方法。Application类可以说是各个Office组件的核心类,不管是Word、Excel、PowerPoint,都存在一个对应的Application类,对应Word、Excel、PowerPoint主程序。如果要在我们的程序中直接打开Word,就需要创建这个Application类的一个实例,如果要关闭掉这个新打开的Word程序,就调用这个新创建的Application对象的Quit()方法。
接下来,我们再用对象浏览器打开Office PIA中的Word所对应的程序集。在前面的操作步骤中,我们已经在项目中引用了Word的PIA,在项目管理器的引用列表中选中Word,点击鼠标右键,选择在对象浏览器中查看,就可以在对象浏览器中打开Word的PIA了。如下图。
如果在上图所示的Microsoft.Office.Interop.Word命名空间所包含的类中做一些浏览,相信读者会发现一个很有意思的事情。那就是其实Word的PIA中的类、接口,并不是和Word的COM组件中的类、接口一一对应的。比如,我们在Word COM组件中能够看到一个叫做Application的类,但是在Word的PIA中,我们只能找到一个叫做Application的接口,和一个叫做ApplicationClass的类。
出现这个情况的原因,在于.Net的COM Interop(具体说就是.Net SDK中的TlbImp.exe这个命令行工具)帮我们根据COM组件生成Interop Assembly时,其实是不会一一对照COM组件来生成.Net类和接口的。相反,它会根据一定的规则,来生成对应的.Net类和接口。
由于Application是Word编程接口中最重要的部分,所以我具体针对Word中的Application这个接口,把它的转换规则简要的说明一下(实际生成的接口和类比下面描述的要更多,相关的关系更复杂)。首先,Word PIA中会生成一个_Application接口,这个_Application接口基本描述了Word COM组件中的Application类中的所有操作和属性,然后,Word PIA中还会生成多个ApplicationEvents_Event系列接口(ApplicationEvents2_Event、ApplicationEvents3_Event、ApplicationEvents4_Event接口…我们可以不用管这些具体的细节),这个接口基本描述了Word COM组件中的Application类中的所有事件。然后,Word PIA中会生成一个Applicatin接口,它实现了_Application接口和ApplicationEvents_Even接口,这样,Application接口就基本描述了Word COM组件中的Application类中的所有操作、属性、事件等等。最后,Word PIA中生成了一个具体的ApplicationClass类,这个类实现了Applicatin接口。
如果你已经被上面那一段描述搞得头昏脑胀,那么只需要记住:在Word PIA中,我们有一个Application接口和一个ApplicationClass类,Application接口描述了对应的Word COM组件中的Application类的所有成员,而ApplicationClass类是具体的实现类。
三、Code WalkThrough:一个.Net WinForms程序
终于,在你忍受了N久,勉强看完了上面那些罗嗦的文字之后,总算可以看到一个具体的示范了。我们要用C#写一个Windows应用程序,在这个程序中,启动Word,用代码操作它做一些操作,然后再关闭掉它。
首先,我们创建一个新的C# Windows应用程序,然后通过上面介绍过的方法,在项目中引用Word的PIA(在添加引用的界面中,选择COM组件列表中的Microsoft Word 11 Object Library)。
在自动创建的启动窗体上,放两个Button控件,一个叫做btnStartWord,另外一个叫做btnStopWord。我们希望当用户点击btnStartWord时,我们的程序自动启动Word,然后创建一个新的Word文档,然后将其自动保存在磁盘上,当用户点击btnStopWord时,就关闭掉Word。窗体设计视图如下。
在这个主窗体类的源代码中,我们引用Word PIA的名称空间,我们使用MSWord来替代Microsoft.Office.Interop.Word这个完整的命名空间名称:
using MSWord = Microsoft.Office.Interop.Word;
在主窗体类的源码中,添加一个类级别的成员,_wordApp是一个Application类型的对象(记住:MSWord.Application是一个接口!):
private MSWord.Application _wordApp = null;
然后在btnStartWord按钮的点击事件代码中,添加如下代码,代码创建一个新的Word实例,然后显示它:
_wordApp = new MSWord.Application();
_wordApp.Visible = true;
看到这里,很多人有一个非常大的疑惑,那就是MSWord.Application实际上是一个接口,那么我们怎么可能通过“new MSWord.Application()”来创建一个Word实例呢?难道我们不应该使用“new MSWord.ApplicationClass()”来做吗?毕竟ApplicationClass才是实现Application