http://www.joycode.com/vsto/archive/2007/09/16/108546.joy
在“上篇”中,我们介绍了准备知识。本文要把它付诸实用:如何在你的 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"(具体的值根据你的情况自定义)。在“安装”、“卸载”或者“回滚”的时候调用这些方法,就可以啦。