.NET Rocks! 介绍 Visual Studio Tools for Applications

  在第295期《.NET Rocks!》中,VSTA 的 Program Manager——Naveen Yajaman 介绍了 Visual Studio Tools for Applications 的基本概念,包括这个产品和 VBA、VSTO 的关系,它的设计器工具(Design Time Tools)、运行时(Runtime);并谈论了有哪些软件开发商正在以什么样的方式使用 VSTA,怎么获得 VSTA 的使用授权,怎么把 VSTA 集成到现有的托管/非托管应用程序中。

  去年 VSTA 1.0 SDK 发布之前,我所在的团队担负了不少的测试工作,所以在此力挺 VSTA 和 Naveen Yajaman!

  收听地址:http://www.dotnetrocks.com/default.aspx?showNum=295

  下载 VSTA 1.0 SDK:http://www.microsoft.com/downloads/details.aspx?FamilyID=7C2EA8AE-E051-449A-9051-3A351BF27B7F&displaylang=en

探索 VSTO 2008,第一篇

前言:TechEd 2008 回来之后,就一直计划着写一些关于 VSTO 2008 的东西。在 TechEd,我接触了一些希望使用 VSTO 做 Office 平台开发的业界同仁,发现很多朋友对 VSTO 还不是那么了解,所以在这次的《探索 VSTO 2008》系列里面,我会从最简单的内容写起。第一篇就是关于开发环境的设置和熟悉。后续的会有各种控件、组件、对象的使用,数据绑定,发布,安全性等各方面的专题。我希望每一篇都保持简短,保证大家有耐心看完然后快速上手实验。如果有写得不详细的地方,可以留言给我,保证有问必答。

  Visual Studio 2008 宣布 RTM 也有那么几天了,相信不少微软技术发烧友早就迫不及待地从 MSDN Subscription 网站下载了火热出炉的光盘镜像。如果您没有 MSDN Subscription 也没关系,您可以到下列地址下载 90 天试用版本:http://msdn2.microsoft.com/en-us/vstudio/products/aa700831.aspx。注意,要使用 Visual Studio 2008 做 Office 平台开发,您必须安装 Visual Studio 2008 Professional 或者更高的 SKU,VSTO 目前还没有可以免费使用的 Express 版本(不过说不定哪天就有了哦)。

  接下来我们看看怎么构建一个学习 VSTO 的实验环境。

  首先如果条件允许,可以安装一个干净的系统。如果内存、硬盘空间足够,可以用虚拟机,至少分配 512MB 内存。操作系统可以用 Windows XP / Windows Vista 或者 Windows Server 2003。操作系统搞定之后,我推荐的安装顺序是:先安装 .NET Framework 2.0(如果是 Windows Vista 则跳过此步),接着用 Windows Update 把所有系统更新装好,然后再安装 2007 Office 系统套件,要确保安装其中的 .NET 编程支持。最后安装 Visual Studio 2008,确保 VSTO 安装组件。所有这些准备工作如果顺利的话,大约一个上午就可以完成。

确保.NET 编程支持的安装

确保VSTO组件的安装

  好了,迫不及待打开 Visual Studio 2008 的界面,创建新工程。VSTO 2008 项目模板在“新建工程”的对话框中有三种地方可以找到:

  • C# 或者 Visual Basic 语言节点,可以找到最常用的 Word、Excel、Outlook 模板
  • Office 节点,可以找到所有的 VSTO 模板,其中的 2003、2007 节点则根据 Office 版本进行了区分
  • Workflow 节点,可以找到 SharePoint 2007 工作流模板

语言节点的常用VSTO模板

工作流节点的SharePoint模板

  由于 Visual Studio 2008 兼容基于 .NET 2.0 至 3.5 的开发,所以在“新建工程”的对话框中我们可以看到有一个选择 .NET Framework 版本的下拉列表。这个列表对 VSTO 的影响在于对 Office 版本的支持上。如果我们需要开发支持 2007 Office 系统的项目,就必须选择 3.5 版本的 .NET Framework,因为这些模板用到了很多新版本框架的特性。如果只是开发支持 2003 Office 系统的项目,那么选择 2.0 版本的 .NET Framework 也可以。

.NET Framework 3.5 支持所有 VSTO 工程类型

.NET Framework 2.0 只支持 Office 2003 的 VSTO 工程

  选择一个 Word 2007 Document 的工程模板,第一次创建某个 Office 应用程序的工程,可能会出现一个信息框,要求启用 VBA 访问,这个时候必须选择确定,否则就不能继续了。这个选项也可以手工在 Office 应用程序的“信任中心”控制。

允许 VBA 访问

  完成创建工程向导之后,我们就看到了熟悉的开发界面。和 WinForm 应用程序开发不同的是,设计器里面是个实实在在的 Word 环境。在工具栏里面,除了标准的 Windows Forms 控件、组件,我们还可以看到 VSTO 为 Word 而定制的控件——Content Control(内容控件)。在后面的专题中,我会专门用一集的时间介绍这些内容控件的功能和使用方法。

  在结束之前,我们再一起看看一个典型的 Word 文档工程用到了哪些程序集——要知其所以然。打开项目属性页面,点击“引用”选项卡。图中用记号笔划出来的那些程序集就是一个典型 VSTO Word 文档项目所必须的程序集。对这个有所了解在今后开发当中解决问题可能有所帮助。

一个典型Word文档工程的程序集引用

  好了,今天的开篇就到这里了。大家在动手实验的过程中有什么问题,或者希望我接下来写 VSTO 哪方面的东西,请一定要留言告诉我!

DEV303: Visual Studio 2008 开发 Office 商业应用框架

我在北京 TechEd 2007 讲的课程,PowerPoint 幻灯片可以在此下载 <!–

–>

接下来的几个星期里,我将陆续把这次课程的内容以 Blog 的形式在博客堂发布(并且加以扩展),以帮助对 VSTO 感兴趣的朋友们尽快上手这个最酷的 Office 平台开发工具。

希望今后可以看到更多使用 VSTO 开发的解决方案!

关键词:北京,TechEd 2007,VSTO

  各位看官:走过路过不要错过啊!

  这次 TechEd 2007 北京会场,第二天有我的一个 Session 哦!内容当然是关于 VSTO 的,去 TechEd 的战斗在开发第一线的弟兄姐妹们一定要来捧场啊!有很精彩的 Demo 等待着大家……

独乐乐不如众乐乐——将 VSTO 插件部署给所有用户(下篇)

  注:本文原作者 Misha Shneerson 是 VSTO 团队的工程师。原文可以在下列地址找到:http://blogs.msdn.com/mshneer/archive/2007/09/05/deploying-your-vsto-add-in-to-all-users-part-ii.aspx

  在“上篇”中,我们介绍了准备知识。本文要把它付诸实用:如何在你的 Office 2007 插件安装程序中具体实现“部署到所有本机所有用户”,这包括任何安装程序都要处置的“安装”、“修复”和“删除”三大功能:

  • 在安装插件的时候,我们需要写入一些类似“上篇”中“testpropagation_create.reg”文件的注册表键值,其中包含的“Create”指令会让 Office 把注册表键值复制到用户的 HKCU 里面去。
  • 在卸载插件的时候,我们要把“Create”指令替换成“Delete”指令,让 Office 删除 HKCU 中对应的键值。同时,我们还需要把“Count”注册表值减少,这样可以保证 Office 要执行“Delete”指令。
  • 在执行修复的时候,我们需要增加“Count”的值,这样会让 Office 再把 HKLM 里面的键值复制到 HKCU。

  我们先来处理安装的时候需要用到的键值。最方便的方法莫过于修改一下现成的 VSTO 2005 SE 插件安装程序工程(创建插件工程时附带自动创建的安装程序工程)。就让我们来开一个新的 Excel 2007 插件工程好了,起个名字叫“MyAddIn”。你会发现 VSTO 自动帮你创建了一个配套的 MyAddInSetup 工程。打开这个工程,切换到注册表视图,然后把里面现成的注册表键值设置全部删除(比如那些在 HKEY_CURRENT_USER 里面的)。然后,把下面的注册表信息保存到一个 .reg 文件中,右键单击“Registry on Target Machine”节点,选择“导入(Import)……”,选中你刚才创建的 .reg 文件。这样就把我们需要的注册表信息导入安装程序工程了。如果有必要,你可以调整下面高亮显示的部分,以对应项目的实际情况。

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\MyCompany.MyAddIn\Create\Software\Microsoft\Office\Excel\Addins\MyCompany.MyAddIn]

“Description”=”MyAddIn — an addin created with VSTO technology”
“Manifest”=”[TARGETDIR]MyAddIn.dll.manifest”
“FriendlyName”=”MyAddIn
“LoadBehavior”=dword:00000003
“CommandLineSafe”=dword:00000001

  做完这一步,你的注册表视图看起来应该类似于下图:

  好了,这样一来,我们就可以保证在卸载 Add-In 的时候,相关的注册表键值会被正确删除。我还建议大家在属性窗口里面把“Create”节点的“DeleteOnUninstall”属性设置成 True,以此保证这个键值会在卸载的时候被删除。

  请注意,我们还需要在“MyCompany.MyAddIn”节点增加一个“Count”值。但是不能通过“注册表视图”来加。因为我们不但需要在卸载的时候保留这个值,还要将其减小。如果在设计器里面添加,那就会在卸载的时候被删除了。

  所以我们需要创建一个自定义安装动作(Custom Action),让它来改变 Count 的值,并且在卸载过程中创建一个“Delete”指令。

  下面我将向大家展示 Darryn Lavery(促成本文的首要功臣)为他的 MSDN 专栏文章编写的代码(这篇即将发布的文章更加详尽地阐述了本文讨论的问题和解决方案),他无私地把代码与我共享,而我就借花献佛,拿来和大家分享。不过还是要感谢 Darryn。

  现在我们就来仔细瞧瞧这个 Custom Action 的代码:先定义一个 RegisterOffice2007AddIn 类,添加一些私有方法。 

class RegisterOffice2007AddIn { 

    #region private methods

    private const string userSettingsLocation = @”Software\Microsoft\Office\12.0\User Settings”;

    public void IncrementCounter(RegistryKey instructionKey) {
        int count = 1;
        object value = instructionKey.GetValue(“Count”);

        if (value != null) {
            if ((int)value != Int32.MaxValue)
                count = (int)value + 1;
        } 

        instructionKey.SetValue(“Count”, count);
    }

    private string GetApplicationPath(string applicationName) {
        switch (applicationName.ToLower()) {
            case “excel”:
                return @”Software\Microsoft\Office\Excel\Addins\”;

            case “infopath”:
                return @”Software\Microsoft\Office\InfoPath\Addins\”;

            case “outlook”:
                return @”Software\Microsoft\Office\Outlook\Addins\”;

            case “powerpoint”:
                return @”Software\Microsoft\Office\PowerPoint\Addins\”;

            case “word”:
                return @”Software\Microsoft\Office\Word\Addins\”;

            case “visio”:
                return @”Software\Microsoft\Visio\Addins\”;

            case “project”:
                return @”Software\Microsoft\Office\MS Project\Addins\”;

            default:
                throw new Exception(applicationName + ” is not a supported application”, null);
        }
    } 

    #endregion

  上面的代码有两个方法:IncrementCounter 负责正确地更新“Count”键值;GetApplicationPath 返回针对某个 Office 应用程序的插件信息注册表键值路径。

  再看一个在“安装”和“修复”的时候直接被调用的方法:

public void RegisterAddIn(string addInName) {
    RegistryKey userSettingsKey = null;
    RegistryKey instructionKey = null;

    try {
        userSettingsKey = Registry.LocalMachine.OpenSubKey(userSettingsLocation, true);

        if (userSettingsKey == null) {
            throw new Exception(“Internal error: Office User Settings key does not exist”, null);
        }

        instructionKey = userSettingsKey.OpenSubKey(addInName, true);

        if (instructionKey == null) {
            instructionKey = userSettingsKey.CreateSubKey(addInName);
        } else {
            // 删除“Delete”指令
            try {
                instructionKey.DeleteSubKeyTree(“DELETE”);
            } catch (ArgumentException) { } // “Delete”指令不存在,忽略这种情况下的异常
        } 

        IncrementCounter(instructionKey);

    } finally {
        if (instructionKey != null)
            instructionKey.Close();

        if (userSettingsKey != null)
            userSettingsKey.Close();
    }
}

  在上面的代码中,我们首先保证“Delete”注册表指令不存在,然后增加计数器值(Count 键值)。要注意的是,我们没有用代码创建“Create”指令,因为这个指令已经在之前的“注册表视图”中定义好了,它会被自动写入注册表。

  最后就是在“卸载”的时候被调用的方法:

public void UnRegisterAddIn(string applicationName, string addInName) {
    RegistryKey userSettingsKey = null;
    RegistryKey instructionKey = null;
    RegistryKey deleteKey = null;

    try {
        userSettingsKey = Registry.LocalMachine.OpenSubKey(userSettingsLocation, true);

        if (userSettingsKey == null) {
            throw new Exception(“Internal error: Office User Settings key does not exist”, null);
        }

        instructionKey = userSettingsKey.OpenSubKey(addInName, true);

        if (instructionKey == null) {
            instructionKey = userSettingsKey.CreateSubKey(addInName);
        } else {
            // 确保“Create”指令被删除
            try {
                instructionKey.DeleteSubKeyTree(“CREATE”);
            } catch (ArgumentException) { } // “Create”指令不存在,忽略这种情况下的异常
        }

        string instructionString =
                        @”DELETE\” +
                        GetApplicationPath(applicationName) +
                        @”\” +
                        addInName;

        deleteKey = instructionKey.CreateSubKey(instructionString);

        IncrementCounter(instructionKey);
    } finally {
        if (deleteKey != null)
            deleteKey.Close();

        if (instructionKey != null)
            instructionKey.Close();

        if (userSettingsKey != null)
            userSettingsKey.Close();
    }
}

  好了,差不多大功告成!只要把这些代码包装进一个 Custom Action DLL(假设你已经有类似的经验),把 CustomActionData 属性设置成 /addinName=”MyCompany.MyAddIn” /application=”Excel”(具体的值根据你的情况自定义)。在“安装”、“卸载”或者“回滚”的时候调用这些方法,就可以啦。我会在稍后的日志中作一个总结。

独乐乐不如众乐乐——将 VSTO 插件部署给所有用户(上篇)

  注:本文原作者 Misha Shneerson 是 VSTO 团队的工程师。原文可以在下列地址找到:http://blogs.msdn.com/mshneer/archive/2007/09/05/deploying-your-vsto-add-in-to-all-users-part-i.aspx

  VSTO 插件(也叫“托管代码 Office 插件”)在部署方面有一个严重的不足,简单来说,微软只告诉了我们如何把这些插件部署到单个用户环境;而“如何一次性部署到一台机器的所有用户环境中”成为了我们的阿喀琉斯之踵。本文将给告诉你如何解决这个问题。

  首先让我们了解一些背景知识。

  Office 2007 已经内置了对托管代码插件的支持,通过一个叫“Manifest”的注册表值,Office 应用程序可以区分传统的 COM 插件和托管代码插件。这个键值可以在下列位置找到:HKCU\Software\Microsoft\Office\<App>\AddIns\<AddInName>。

  传统的 COM 插件可以通过在 HKLM\Software\Microsoft\Office\<App>\AddIns 注册自己的信息从而实现让本机所有的用户都可以使用。但是托管代码插件只能注册在 HKCU 里面,注册在 HKLM 里面的托管代码插件会被忽略。

  要创建一个安装包,做到能够往启动安装程序的那个用户的注册表里面写入插件信息并不困难。但是,要把同样的信息复制到本机所有用户的注册表键里面,就需要比较高级的 Win32 API 使用技巧了。如果还要再稍微深入一步,我们可以让安装程序把插件注册信息写入“模板”(存在于 C:\Documents and Settings\Default User\ntuser.dat),这样可以保证将来使用本机的新用户直接就有相应的注册表信息。关于这种技巧,请参见 Raymond Chen 的文章

  已经在抓脑袋了?其实有一种相对来说容易得多的解决方案。诀窍就在于使用 Office 的一个内部机制,在用户启动 Office 应用程序的时候,把 HKLM 里面注册的信息复制到当前用户的 HKCU 里面去。

  针对 Office 2007,这个神奇的键值就在这里:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings。如果你的电脑已经安装了 Office 2007,你可以打开注册表编辑器察看一下那里(如果是 x64 的操作系统,则应该在 Wow6432Node 的相应位置里面)。你会发现里面有不少看似随机的键,这些随机的键里面,又有“Create”以及“Delete”键;再进入一层看,就会发现里面似乎包含着某些完整路径的注册表信息!

  我们所见到的就是 Office 2007 的一种 HKLM 到 HKCU 的信息复制机制,而这种机制可以在注册表中进行完全的控制。让我们来进一步研究这种机制到底是怎么工作的。我们可以先做一个小练习——把下面的内容复制到记事本程序,存为“testpropagation_create.reg”文件。然后运行这个文件,把其中的信息导入 HKLM。

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\TestPropagation]
“Count”=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\TestPropagation\Create\Software\Microsoft\Office\TestKey]
“TestValue”=”Test”

  然后启动 Excel 2007,再检查一下注册表,你会发现你的 HKCU 中,有两个新的键值被创建出来了:

  • HKCU\Software\Microsoft\Office\TestKey 有了一个值:TestValue
  • HKCU\Software\Microsoft\Office\12.0\User Settings\TestPropagation 有一个 Count 值,并且等于 1

  下一步,我们看看如何用同样的机制来删除键值。把下面的内容复制并保存到“testpropagation_delete.reg”文件,并且运行:

Windows Registry Editor Version 5.00
[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\TestPropagation\Create]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\TestPropagation]
“Count”=dword:00000002
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\TestPropagation\Delete\Software\Microsoft\Office\TestKey]

  高亮显示的文字标明了和之前一个文件不同的地方。我们先移除之前创建的 TestPropagation\Create 键,注意前面的“-”符号,它表示删除键值。为了让指令正确执行,我们需要保证 HKLM 里面的 Count 值和 HKCU 里面的 Count 值不同,所以把 Count 的值设置成了 2。最后,Software\Microsoft\Office\TestKey 被放在了 TestPropagation\Delete 里面,这样 Office 就知道应该是删除那个键值,而不是创建。

  执行了 testpropagation_delete.reg 之后,你会发现:

  • HKCU\Software\Microsoft\Office\TestKey 键已经被删除
  • HKCU\Software\Microsoft\Office\12.0\User Settings\TestPropagation 键的 Count 值被设置成了 2

  好了,现在我们已经清楚了 Office 的注册表键值复制机制是怎么工作的。至于怎么利用这种机制来改进我们的插件安装程序,应该也能猜个八九不离十了吧?在本文的下篇中,我们将一起来看如何为一个 VSTO 2005 SE 的插件安装程序做一个自定义动作,结合上述机制,达到“部署到本机所有用户”的最终目的。

谁动了我的插件?

  之前提到了 VSTO 2005 SE (以下简称 Cypress) 开发的 Office 插件可能无法被装载,微软已经提供了修正此问题的更新程序。现在我们来探究一下引发此问题的根本原因——谁动了我的插件?

  先了解一下 Cypress 针对 Office 2003 插件的基本装载原理。用 Cypress 开发的 Office 插件并不是直接被 Office 应用程序加载的,编译得到的 .NET 程序集没有被注册为 COM 组件,而是由一个 Cypress 提供的“Loader”加载。向 Office 应用程序注册插件信息的时候,COM 注册信息中“InprocServer32”实际上指向 Cypress Add-in Loader,并且提供一个清单文件(Manifest)告诉 Loader 真正应该加载的 .NET 程序集信息。这样做的好处有很多:各个 VSTO 开发的插件不会互相干扰(Loader 会为它们创建各自的应用程序域);Loader 还可以帮助检查插件的新版本、提供插件自动升级功能;还可以对插件程序集的安全权限进行控制等等。

  至于 COM 注册信息,我们知道在 Windows 中有三个地方可以存放(不考虑 Wow64 的情况):
  HKEY_CURRENT_USER\Software\Classes
  HKEY_LOCAL_MACHINE\Software\Classes
  HKEY_CLASS_ROOT (上面两个的合并版本)

  在 Cypress 的 RTM 版本中,插件的 COM 注册信息被写入 HKEY_CURRENT_USER\Software\Classes。因为这样做可以保证开发人员不需要管理员权限就可以调试、部署。这种方法在 Windows 2000 / XP / 2003 的平台上没有任何问题。而在 Windows Vista 中,新的安全特性“Mandatory Integrity Control ”使包括 Office 2003 在内的 COM 应用程序行为发生了变化。如果应用程序是以“中等”或者更低的 Integrity Level 运行,COM 信息会从 HKEY_CLASS_ROOT 读取,包含了 Cypress 写入 HKEY_CURRENT_USER\Software\Classes 中的信息;但是如果应用程序以高于“中等”的 Integrity Level 运行(即“以管理员身份运行”),或者 UAC 关闭,COM 信息只从 HKEY_LOCAL_MACHINE 中读取。在这种情况下,Office 2003 无法找到 Cypress 插件写入的信息,所以就导致插件装载失败!

  如果开发者在 Windows Vista 中“以管理员身份”启动 Visual Studio 2005,就会因为上述的原因无法在 IDE 中调试插件。因为此时的 Visual Studio 2005 进入调试模式启动的 Office 2003 程序默认也是“以管理员身份”运行的。

  为了修正这个问题,Cypress 的修正程序将改变插件信息的写入行为:先尝试写入 HKLM,如果因为没有权限而失败,再写入 HKCU。这样,即便 Visual Studio 和 Office 2003 被提升为“管理员身份”运行,也能够保证插件的 COM 信息被找到。

  值得开发人员注意的是,这次发布的 Cypress 修正程序并没有对插件工程配套的安装程序工程作任何修改(见图)。我们必须要自己在 HKLM 中添加和 HKCU 中同样的信息,才能保证插件安装在 Windows Vista 的机器上也可以完全正常工作。

  最后顺带提一下,Office 2007 “天生”就认识 VSTO 开发的插件,所以就没有 Office 2003 那样的麻烦事儿了。

无法调试 Outlook 插件?

  刚才想在自己的邮件机器上做一个 Outlook 插件的小实验,却发现 Visual Studio 2005 不能正确启动 Outlook 进入调试模式。感觉奇怪之余,马上想到“不能启动 Outlook”的真正原因可能是 Outlook 已经在内存中了。随后打开任务管理器证实了这一点。原来是 Windows Mobile Device Center 已经通过 COM 调用启动了 Outlook(同步数据用,无界面显示),所以 Visual Studio 2005 就无法再启动一个 Outlook 进程开始调试了。

  解决方法就是断开 Windows Mobile 设备连接,WMDC 会释放对 Outlook 的 COM 引用;如果没有别的程序也保持着对 Outlook 的 COM 引用,那么 Outlook.exe 就会从内存中卸载。

  平时一般都在专门的 Lab 机器上做实验,没有其它软件的干扰。如果在相对杂乱的环境里面做开发,务必要先排除其它软件可能造成的影响。特此记录。

Cypress 设计器更新

  由于 Visual Studio Tools 2005 第二版(代号 Cypress)早于 Windows Vista 发布,在后来发现了一些针对 Windows Vista 环境中用 Cypress 开发 Office 2003 插件时产生的问题。微软已经分别发布了一个设计时更新(Design Time,针对 Visual Studio)和一个运行时更新(Runtime,针对发布后的环境)。

  下载地址分别如下:

  建议开发环境/部署环境是 Windows Vista / Windows Server 2008 的 Cypress 用户安装这两个更新,可以避免一些麻烦哦!

新的开始

  本来打算在另外一个挺大的中文社区写写关于 VSTO / VSTA 的东西,但是那个社区的文章编辑器实在是太不好用,最终弃暗投明来了这里。

  在这个Blog中,我会主要写一些关于 Visual Studio Tools for Office / Application 的技术文章,也会翻译一些资料和大家共享。希望我们可以通过 VSTO 这座桥梁,联结起强大的开发平台和办公平台,构建出最棒的应用!