为什么无法从外部访问VSTO对象?
2012-04-19 11:41 slmk 阅读(1444) 评论(3) 编辑 收藏 举报为什么要从外部获取VSTO对象?
通常的场景是我们用VSTO创建了一个office应用程序,里面用托管代码定义了一些方法,我们想通过外部程序打开该VSTO程序,获取VSTO对象引用,然后调用这些方法,以达到控制Office文档的目的。
直接获取失败
以下代码试图从外部程序获取VSTO对象引用,然而失败了!
using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Threading; using Microsoft.Office.Tools.Excel.Extensions; using IExcel = Microsoft.Office.Interop.Excel; using TExcel = Microsoft.Office.Tools.Excel; namespace ClientApplication { class Program { static int Main(string[] args) { /* To avoid any problem (exception of type "Invalid format") when manipulating the * workbook, we'll specify here the culture en-US to avoid forcing the client to * have a language pack for excel */ Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); //Get the application and set it visible (ie, seeing the excel front end) IExcel.Application application = new IExcel.Application(); application.Visible = true; //Let's create the path to the file and then : open it string basePath = @"D:\Projects\PDA - Blog\Blog Research\VSTO - WCF\ServerApplication"; string fileName = "ServerApplication.xlsx"; IExcel.Workbook book = application.Workbooks.Open(Path.Combine(basePath, fileName), Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); /* Let's now use the .NET 3.5 SP1 functionality to convert an interop * object to a VSTO object * As it is an extension method, we could also use * TExcel.Workbook workbook = book.GetVstoObject(); */ TExcel.Workbook workbook = WorkbookExtensions.GetVstoObject(book); //Let's now check that the conversion did work Debug.Assert(workbook != null, "The conversion to VSTO did not work"); //It will fail ! return 0; } } }
也就是说我们从VSTO程序外部,试图获取的VSTO对象总是为空!
原因其实很简单:
我们在试图获取另一个进程中的对象引用,而一个进程是不可以直接访问另一个进程的内存空间的。这个道理我们都知道,我们对进程间通讯的方法也耳熟能详,什么消息发送、命名管道、匿名管道、共享内存和Socket等。令我们不解的往往是:既然能取得Interop的Appliction、WorkBook等对象,为什么不能取得托管的VSTO对象呢?如果我们了解COM就不难理解了,.net与office的Com的互操作其实也是进程间的一种通信机制,只不过是通过COM组件掩盖了底层的通信机制,看来进程间通信的最高境界就是通过COM组件的方式。也就是说Interop的Appliction等对象是由COM接口公开出来的,本例中后台其实启动了一个Excel进程作为COM服务器,我们的外部程序,本例中的命令行程序充当了一个COM客户端。而我们的VSTO对象没有公开为COM可见的形式。
MSDN的解释:
其实本文所说的VSTO对象,在MSDN的官方名称是宿主控件和宿主项。通过编程方式在运行时创建宿主项是有限制的,参加MSDN文章:Programmatic Limitations of Host Items and Host Controls
大体意思是很多宿主项只能在设计时创建,运行时不能创建。
如何实现从外部调用VSTO程序的托管方法呢?
答案就是使用进程间通信,有兴趣的可以参考这篇文章:http://www.pedautreppe.com/post/How-can-we-manipulate-the-VSTO-objects-when-opening-a-VSTO-document-from-command-line-.aspx ,是通过.net remoting实现的。
参考资料
可以通过OLE-COM Object Viewer查看COM组件公开的接口