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

2023-11-01 12:10

本文主要是介绍独乐乐不如众乐乐——将 VSTO 插件部署给所有用户(下篇),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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 的插件安装程序做一个自定义动作,结合上述机制,达到“部署到本机所有用户”的最终目的。

如何在你的 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

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

register

  好了,这样一来,我们就可以保证在卸载 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 插件部署给所有用户(下篇)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/322868

相关文章

闲置电脑也能活出第二春?鲁大师AiNAS让你动动手指就能轻松部署

对于大多数人而言,在这个“数据爆炸”的时代或多或少都遇到过存储告急的情况,这使得“存储焦虑”不再是个别现象,而将会是随着软件的不断臃肿而越来越普遍的情况。从不少手机厂商都开始将存储上限提升至1TB可以见得,我们似乎正处在互联网信息飞速增长的阶段,对于存储的需求也将会不断扩大。对于苹果用户而言,这一问题愈发严峻,毕竟512GB和1TB版本的iPhone可不是人人都消费得起的,因此成熟的外置存储方案开

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

在 Windows 上部署 gitblit

在 Windows 上部署 gitblit 在 Windows 上部署 gitblit 缘起gitblit 是什么安装JDK部署 gitblit 下载 gitblit 并解压配置登录注册为 windows 服务 修改 installService.cmd 文件运行 installService.cmd运行 gitblitw.exe查看 services.msc 缘起

【Kubernetes】K8s 的安全框架和用户认证

K8s 的安全框架和用户认证 1.Kubernetes 的安全框架1.1 认证:Authentication1.2 鉴权:Authorization1.3 准入控制:Admission Control 2.Kubernetes 的用户认证2.1 Kubernetes 的用户认证方式2.2 配置 Kubernetes 集群使用密码认证 Kubernetes 作为一个分布式的虚拟

Solr部署如何启动

Solr部署如何启动 Posted on 一月 10, 2013 in:  Solr入门 | 评论关闭 我刚接触solr,我要怎么启动,这是群里的朋友问得比较多的问题, solr最新版本下载地址: http://www.apache.org/dyn/closer.cgi/lucene/solr/ 1、准备环境 建立一个solr目录,把solr压缩包example目录下的内容复制

Maven(插件配置和生命周期的绑定)

1.这篇文章很好,介绍的maven插件的。 2.maven的source插件为例,可以把源代码打成包。 Goals Overview就可以查看该插件下面所有的目标。 这里我们要使用的是source:jar-no-fork。 3.查看source插件的example,然后配置到riil-collect.xml中。  <build>   <plugins>    <pl

jenkins 插件执行shell命令时,提示“Command not found”处理方法

首先提示找不到“Command not found,可能我们第一反应是查看目标机器是否已支持该命令,不过如果相信能找到这里来的朋友估计遇到的跟我一样,其实目标机器是没有问题的通过一些远程工具执行shell命令是可以执行。奇怪的就是通过jenkinsSSH插件无法执行,经一番折腾各种搜索发现是jenkins没有加载/etc/profile导致。 【解决办法】: 需要在jenkins调用shell脚

Jenkins 插件 地址证书报错问题解决思路

问题提示摘要: SunCertPathBuilderException: unable to find valid certification path to requested target...... 网上很多的解决方式是更新站点的地址,我这里修改了一个日本的地址(清华镜像也好),其实发现是解决不了上述的报错问题的,其实,最终拉去插件的时候,会提示证书的问题,几经周折找到了其中一遍博文

Spring Roo 实站( 一 )部署安装 第一个示例程序

转自:http://blog.csdn.net/jun55xiu/article/details/9380213 一:安装 注:可以参与官网spring-roo: static.springsource.org/spring-roo/reference/html/intro.html#intro-exploring-sampleROO_OPTS http://stati