65.9K
CodeProject 正在变化。 阅读更多。
Home

桌面和快速启动快捷方式的条件安装

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (43投票s)

2005年9月25日

20分钟阅读

viewsIcon

345594

downloadIcon

4110

创建一个安装程序,允许用户选择是否在桌面或快速启动栏中创建快捷方式。

引言

使用 Visual Studio .NET 2003 创建的安装项目可以轻松地为您的应用程序添加桌面快捷方式。但不支持根据条件创建快捷方式。此外,也不支持在快速启动栏中添加快捷方式。本文将向您展示如何让用户选择是否添加这些快捷方式。

在开发此解决方案时,我还需要克服 System.Environment.GetFolderPath 方法和 System.Environment.SpecialFolder 枚举的一个限制。它们只提供当前用户桌面的位置,而没有提供“所有用户”桌面的位置,后者是“为所有人”安装所必需的。

背景

最近,我被要求修改一个应用程序的安装程序,以便用户可以选择是否在桌面上添加快捷方式。我以为这会很容易,但很快发现比我想象的要难。我在安装程序中添加了一个带有创建桌面快捷方式选项的复选框对话框。然后,我将“用户桌面”文件夹的 Condition 属性设置为相应的复选框(此文件夹中的实际快捷方式没有 Condition 属性)。但这并不起作用。Visual Studio .NET IDE 给你的印象是,你可以在部署文件的文件夹上设置条件,但这具有误导性。Windows Installer 中没有这样的条件。因此,尽管 IDE 允许定义条件,但并不会对它们执行任何操作。在我最初发表这篇文章后,djm181 发表了一篇题为“没必要那么复杂”的评论,声称这些条件确实有效。然而,经过进一步调查,发现他所做的事情只是由于安装程序创建两个同名快捷方式的顺序而造成了有效的假象。

经过一番搜索,发现许多其他人也发现这种技术行不通,并寻求解决方案。大多数答案是使用 Orca 来编辑 MSI 文件。虽然这可行,但我更喜欢一个每次在 Visual Studio 中重新生成解决方案时都会包含的解决方案。

这里提供的解决方案使用一个添加到被部署应用程序中的 Installer 类。Installer 类中的代码使用 Windows Script Host 来创建快捷方式。

使用代码

本文的源代码提供了一个简单的 Win Forms 应用程序,其中包含一个用于创建快捷方式的 Installer 类,以及一个包含询问用户是否要创建快捷方式对话框的安装项目。请按照以下步骤将此功能添加到您自己的项目中。首先,在 Visual Studio 中打开您将要部署的应用程序的项目。这些步骤分为两个主要部分:

向您的应用程序添加代码

引用 Windows Script Host

Windows Script Host 是一个 COM 组件,因此您需要在应用程序主程序集的项目中添加对它的引用。要在 Visual Studio .NET IDE 中执行此操作,请按以下步骤操作:在解决方案资源管理器中,右键单击项目的“引用”部分并选择“添加引用”,选择“COM”选项卡,在 ListBox 中找到并选择“Windows Script Host Object Model”,单击“选择”,然后单击“确定”。这将添加对 IWshRuntimeLibrary 的引用。您可以在此处找到有关 Windows Script Host 的更多信息。

添加 ShortcutsInstaller 类

向您的应用程序的主程序集项目添加一个新类。将该类命名为“ShortcutsInstaller”。该类的所有代码都已在演示项目中提供,因此我在这里只回顾要点。

您需要添加对我们将在此类中使用的几个命名空间的引用。在您的代码中包含以下引用:

using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using IWshRuntimeLibrary;

System.Configuration.Install 命名空间需要引用 System.Configuration.Install.dll 程序集。要在 Visual Studio .NET IDE 中添加此引用,请执行以下操作:在解决方案资源管理器中,右键单击项目的“引用”部分并选择“添加引用”,在 ListBox 中找到并选择“System.Configuration.Install.dll”,单击“选择”,然后单击“确定”。

ShortcutsInstaller 类必须继承自基类 System.Configuration.Install.Installer 并包含 RunInstaller 特性:

[RunInstaller(true)]            
public class ShortcutsInstaller : Installer
{
   ...
}

重写 Install 方法

当您实现自己的 Installer 类时,您可以重写基类 Installer 的一个或多个方法:InstallCommitRollbackUninstall。这些方法对应于安装过程的不同阶段。我们将重写其中一些方法。要重写基类的 Install 方法,请在您的类中添加一个新方法,如下所示:

public override void Install(IDictionary savedState)
{
   base.Install(savedState);
      ...
}

此方法的第一行必须是调用我们正在重写的基类方法:base.Install(savedState)

Installer 类的参数

我们的 installer 类需要一种方法来了解用户为当前安装做出了哪些选择。我们的安装程序需要三条信息:

  • 用户是选择为“所有人”还是“仅为我”安装。
  • 用户是否选择创建桌面快捷方式。
  • 用户是否选择创建快速启动快捷方式。

为了向我们的 installer 类提供这些信息,.NET 允许将参数传递给安装程序。稍后当我们查看安装项目时,我们将看到如何将这些参数传递给我们的 installer 类。这些参数通过基类 InstallerContext 属性提供给安装程序。这使我们可以访问包含参数的 StringDictionary 对象。我们可以使用 ContainsKey 方法检查参数是否已提供给我们的 installer 类:

const string ALLUSERS_PARAM = "ALLUSERS";
if (!Context.Parameters.ContainsKey(ALLUSERS_PARAM))
  throw new Exception(string.Format(
    "The {0} parameter has not been provided for the {1} class.", 
    ALLUSERS_PARAM, this.GetType()));

Visual Studio .NET 安装项目提供的默认安装文件夹对话框包含单选按钮,供用户选择是为使用计算机的每个人还是仅为自己安装应用程序。“所有人”选项的参数值为“1”,而“仅为我”选项的参数值为空字符串。

对于我们将添加到安装项目中以允许用户选择安装快捷方式的复选框,如果复选框被选中,参数值为“1”,如果未选中,则为空字符串。

检查参数值的代码如下所示:

bool allusers = 
   Context.Parameters[ALLUSERS_PARAM] != string.Empty;
bool installDesktopShortcut = 
   Context.Parameters[DESKTOP_SHORTCUT_PARAM] != string.Empty;
bool installQuickLaunchShortcut = 
   Context.Parameters[QUICKLAUNCH_SHORTCUT_PARAM] != string.Empty;

桌面文件夹的位置

如果用户选择添加桌面快捷方式,我们需要确定将要创建快捷方式的桌面文件夹的位置。如果用户选择为“所有人”安装,则该位置是“所有用户”桌面。对于“仅为我”的安装,它是当前用户的桌面位置。

.NET Framework 为我们提供了一种获取当前用户桌面位置的方法,即使用 System.Environment.GetFolderPath 方法:

desktopFolder = 
  Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);

但是,System.Environment.SpecialFolder 枚举不包含“所有用户”桌面文件夹的成员。要找到“所有用户”桌面文件夹的位置,我们需要使用 Windows Script Host:

object allUsersDesktop = "AllUsersDesktop";
WshShell shell = new WshShellClass();
desktopFolder = 
   shell.SpecialFolders.Item(ref allUsersDesktop).ToString();

请注意传递给 SpecialFolders.Item 方法的 ref 对象参数。这是使用 COM Interop 调用它的方式。如果我们只是编写 VBScript,它会像下面这样简单:

set shell = WScript.CreateObject("WScript.Shell")
desktopFolder = shell.SpecialFolders("AllUsersDesktop")

但是,要通过 COM Interop 访问 SpecialFolders 集合,需要将对象的 Item 属性作为索引通过引用传递给集合。使用 COM Interop 时,文件夹作为对象返回,因此我们需要对返回的值使用 ToString 方法。

在演示项目中,我将获取“AllUsersDesktop”文件夹的代码放在一个 try-catch 块中,因为在某些旧版本的 Windows 上不支持此文件夹。如果不支持“AllUsersDesktop”文件夹,则快捷方式将创建在当前用户的桌面上。

快速启动文件夹的位置

如果用户选择添加快速启动快捷方式,我们需要确定创建快捷方式的文件夹位置。快速启动栏的功能是 Internet Explorer 的一部分,快速启动快捷方式的文件夹位置也是 Internet Explorer 应用程序数据的一部分。没有“所有用户”的快速启动文件夹,因此快速启动快捷方式总是添加到当前用户的快速启动文件夹中,即使用户选择为“所有人”安装也是如此。

我们用来查找当前用户桌面的 System.Environment.GetFolderPath 方法也可以为我们提供当前用户“Application Data”文件夹的位置。我们需要硬编码应用程序数据文件夹中快速启动文件夹的位置。在演示项目中,我将快速启动文件夹的位置设为 ShortcutsInstaller 类的一个属性,这样我就不必在多个地方重复位置代码。快速启动文件夹位置的代码是:

private string QuickLaunchFolder
{
  get
  {
    return
      Environment.GetFolderPath(
         Environment.SpecialFolder.ApplicationData)
         + "\\Microsoft\\Internet Explorer\\Quick Launch";
  }
}

创建快捷方式

在演示项目中,我在 ShortcutsInstaller 类中有一个单独的 CreateShortcut 方法来创建快捷方式。这个方法在重写的 Install 方法中被调用,用于创建桌面和快速启动快捷方式。CreateShortcut 方法接受四个用于快捷方式的参数:

  • 将创建快捷方式的文件夹。
  • 快捷方式的名称 - 这是为快捷方式显示的标题。
  • 快捷方式的目标 - 这是由快捷方式启动的应用程序可执行文件。
  • 快捷方式的描述 - 当鼠标悬停在快捷方式上时,这会显示在工具提示中。

CreateShortcut 方法使用 Windows Script Host 来创建快捷方式。实际的快捷方式是一种特殊类型的文件,扩展名为 .lnk(当您在资源管理器中查看文件时,Windows 总是隐藏此扩展名)。快捷方式的名称用作文件名。以下是 CreateShortcut 方法的代码:

private void CreateShortcut(string folder, 
     string name, string target, string description)
{
   string shortcutFullName = 
            Path.Combine(folder, name + ".lnk");

   try
   {
      WshShell shell = new WshShellClass();
      IWshShortcut link = 
        (IWshShortcut)shell.CreateShortcut(shortcutFullName);
      link.TargetPath = target;
      link.Description = description;
      link.Save();
   }
   catch (Exception ex)
   {
     MessageBox.Show(
       string.Format(
         "The shortcut \"{0}\" could not be created.\n\n{1}",
         shortcutFullName, ex.ToString()), "Create Shortcut", 
         MessageBoxButtons.OK, MessageBoxIcon.Information);
   }
}

如果由于任何原因无法创建快捷方式,try-catch 块意味着将向用户显示一条消息,但安装不会失败。

删除快捷方式

在安装您的应用程序时创建的任何快捷方式都应该在卸载应用程序时被删除。要做到这一点,您需要重写基类 InstallerUninstall 方法:

public override void Uninstall(IDictionary savedState)
{
   base.Uninstall(savedState);
      ...
}

在演示项目中,我在 ShortcutsInstaller 类中有单独的 DeleteShortcutDeleteShortcuts 方法。DeleteShortcuts 方法从重写的 Uninstall 方法中调用(我们将在讨论 Rollback 方法时再次使用此方法)。然后 DeleteShortcuts 方法调用 DeleteShortcut 方法两次:一次用于桌面快捷方式,一次用于快速启动快捷方式。我们不关心用户在安装应用程序时是否选择创建快捷方式。如果它们存在,我们只需从“所有用户”桌面、当前用户桌面和快速启动文件夹中删除快捷方式。以下是 DeleteShortcut 方法的代码:

private void DeleteShortcut(string folder, string name)
{
   string shortcutFullName = Path.Combine(folder, name + ".lnk");
   FileInfo shortcut = new FileInfo(shortcutFullName);
   if (shortcut.Exists)
   {
      try
      {
        shortcut.Delete();
      }
      catch (Exception ex)
      {
         MessageBox.Show(
            string.Format(
              "The shortcut \"{0}\" could not be deleted.\n\n{1}",
              shortcutFullName, ex.ToString()), "Delete Shortcut", 
              MessageBoxButtons.OK, MessageBoxIcon.Information);
      }
   }
}

我们使用 FileInfo 类来查看快捷方式是否存在,如果存在则删除该文件。如果由于任何原因无法删除文件,将向用户显示一条消息。

回滚

如果安装因任何原因失败且快捷方式已经创建,则需要删除这些快捷方式。如果出现问题导致安装无法完成,将调用 ShortcutsInstaller 类的 Rollback 方法。我们需要重写基类 InstallerRollback 方法并调用 DeleteShortcuts 方法。

程序集特性

我利用了正在安装的应用程序的程序集特性值(在 AssemblyInfo.cs 文件中设置)。程序集特性用于设置快捷方式的名称和描述。如果设置了 AssemblyTitle 特性,则将其用于快捷方式的名称。如果未设置 AssemblyTitle 特性,则使用应用程序的文件名。如果设置了 AssemblyDescription 特性,则将其用于快捷方式的描述。如果未设置 AssemblyDescription 特性,则快捷方式的描述将设置为“启动 xxx”,其中 xxx 是快捷方式的名称。我已向 ShortcutsInstaller 类添加了使用反射获取程序集特性的属性。获取 AssemblyTitle 特性的代码是:

object titleAttribute = 
  myAssembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), 
                                                       false)[0];
_name = ((AssemblyTitleAttribute)titleAttribute).Title;

您可以使用的另一个选项是,从安装项目的 "ProductName" 和 "Description" 属性中传递这些值来作为快捷方式的名称和描述值。

Setup 项目

创建安装项目

如果您的应用程序解决方案中已经有一个安装项目,那么您可以跳过此部分。要在 Visual Studio .NET IDE 中向您的解决方案添加一个安装项目,请执行以下操作:

在解决方案资源管理器中,右键单击解决方案并选择“添加”,然后选择“新建项目”。这将显示“添加新项目”对话框。在“项目类型”中选择“安装和部署项目”,在“模板”中选择“安装项目”,提供一个合适的“名称”和“位置”,然后单击“确定”。

在解决方案资源管理器中,右键单击新的安装项目,选择“视图”,然后选择“文件系统”。这将显示文件系统编辑器,您可以在其中指定将要安装的文件及其在目标计算机上的位置。文件系统编辑器分为两部分:左侧的导航窗格和右侧的详细信息窗格。导航窗格包含一个表示目标计算机上文件系统的文件夹层次列表。文件夹名称对应于标准的 Windows 文件夹;例如,“应用程序文件夹”对应于“Program Files”文件夹下的一个文件夹,应用程序将安装在该文件夹中。在导航窗格中选择一个文件夹时,将要安装在该文件夹中的任何文件和快捷方式都会显示在详细信息窗格中。

右键单击“应用程序文件夹”,选择“添加”,然后选择“项目输出”。这将显示“添加项目输出组”对话框。项目下拉列表框包含您解决方案中的其他项目。选择包含 ShortcutsInstaller 类的项目。项目下方的列表框包含可以部署的项目输出列表。在此列表中选择“主输出”,然后单击“确定”。您可能会收到一个关于“wshom.ocx”依赖项的消息框。如果收到,只需单击“确定”——我将在下面讨论“wshom.ocx”。三个文件将被添加到文件系统编辑器的详细信息窗格中:

  • 您的应用程序的主输出 - 这是由应用程序项目构建的 EXE 文件。
  • Interop.IWshRuntimeLibrary.dll - 这是 Visual Studio .NET 为 Windows Script Host 创建的 COM Interop 程序集。此文件必须部署到与您的 EXE 相同的文件夹中。
  • wshom.ocx - 这是 Windows Script Host ActiveX 控件 - 见下文

您可能希望为您的应用程序添加一个快捷方式,以便可以从“开始”菜单访问它。要在 Visual Studio .NET IDE 中执行此操作,请按以下步骤操作。在文件系统编辑器的详细信息窗格中,右键单击“主输出...”并选择“创建主输出的快捷方式...”。这会将快捷方式添加到“应用程序文件夹”中。将快捷方式重命名为您的应用程序的标题。然后您可以将快捷方式剪切粘贴或拖放到导航窗格中的“用户程序菜单”中。

wshom.ocx

当您向安装项目添加要部署的程序集时,Visual Studio .NET 会尝试确定该程序集所依赖的所有其他组件。这将包括您在项目中添加为引用的所有程序集。每个依赖的程序集都将检查其依赖项,依此类推。这包括您引用的任何 COM 组件。任何不属于标准 .NET Framework 程序集的依赖项都将被添加到您的安装项目的应用程序文件夹中。这就是为什么 Interop.IWshRuntimeLibrary.dllwshom.ocx 被添加到您的安装项目中的原因。

虽然 Interop.IWshRuntimeLibrary.dll 必须部署,但通常不需要部署 wshom.ocx,因为它是标准 Windows 安装的一部分。实际上,当 Visual Studio .NET 将 wshom.ocx 添加到您的项目中时,它的方式是只会将其复制到目标计算机,但不会实际使用。如果您查看此文件的属性窗口,您会看到 Register 属性设置为“vsdrfDoNotRegister”。这意味着如果 wshom.ocx 在目标计算机上尚未作为正确注册的 COM 组件存在,您的桌面和快速启动快捷方式将不会被创建。如果您要安装到的计算机没有预先注册 wshom.ocx,您需要将 Register 属性更改为“vsdrpCOM”。您可能还希望将目标文件夹更改为 Windows 系统文件夹。

在演示项目中,我完全避免了部署 wshom.ocx 文件。要防止此文件被复制到目标计算机,请将该文件的 Exclude 属性更改为“True”。

移除任何现有的桌面快捷方式

如果您已经在安装项目的文件系统编辑器中定义了应用程序的桌面快捷方式,那么您需要删除此快捷方式。取而代之的是,快捷方式将由 ShortcutsInstaller 类添加。

添加一个复选框对话框

您需要向安装项目添加一个对话框,其中包含复选框,供用户选择是否创建桌面和快速启动快捷方式。要在 Visual Studio .NET IDE 中执行此操作,请执行以下操作:

在解决方案资源管理器中,右键单击安装项目,选择“视图”,然后选择“用户界面”。这将显示用户界面编辑器,您可以在其中指定和编辑在安装过程中显示的对话框。默认情况下已经包含了一些对话框。用户界面编辑器包含一个单一窗格,其中有一个用户界面对话框的层次列表。该列表分为两个部分,分别用于标准安装和管理安装,每个部分都包含“开始”、“进度”和“结束”节点,以表示安装的各个阶段。我将只描述为标准安装(在“安装”节点下)添加对话框所需的操作。

右键单击“开始”节点并选择“添加对话框”,选择其中一个“复选框”项并单击“确定”——我假设您选择“复选框 (A)”。“复选框 (A)”对话框被添加到“确认安装”对话框之后。您需要将其向上移动,使其位于“安装文件夹”对话框之后和“确认安装”对话框之前。您可以右键单击“复选框 (A)”对话框并选择“上移”,或使用拖放来移动对话框。

选中“复选框 (A)”对话框后,按“F4”键查看“属性”窗口。将“Banner Text”属性更改为“快捷方式”,并将“Body Text”属性更改为“安装程序可以在您的桌面和快速启动栏中创建 [ProductName] 的快捷方式。您希望安装程序创建快捷方式吗?”。不要更改“[ProductName]”——Visual Studio 将自动将其替换为您的安装项目的“ProductName”属性的值——只需确保您设置了此属性!

将“Checkbox1Label”属性更改为“是的,我希望在桌面上创建 [ProductName] 快捷方式”。将“Checkbox1Property”属性更改为“DESKTOP_SHORTCUT”,并将“CheckBox1Value”属性更改为“Checked”。

对 CheckBox2 属性进行类似的更改,但使用值“是的,我希望在快速启动栏中创建 [ProductName] 快捷方式。”和“QUICKLAUNCH_SHORTCUT”。

将“Checkbox3Visible”和“Checkbox4Visible”属性更改为“False”,因为这些将不会被使用。

自定义操作

要让您的安装项目调用应用程序项目中的 Installer 方法,您需要为您的安装项目定义“自定义操作”。要在 Visual Studio .NET IDE 中执行此操作,请按以下步骤操作:

在解决方案资源管理器中,右键单击安装项目,选择“视图”,然后选择“自定义操作”。这将显示自定义操作编辑器,您可以在其中指定在安装过程中在目标计算机上执行的其他操作。自定义操作编辑器包含一个单一窗格,其中有一个自定义操作的层次列表。该列表分为四个部分,代表安装的各个阶段:安装、提交、回滚和卸载。这些对应于基类 InstallerInstallCommitRollbackUninstall 方法。在 ShortcutsInstaller 类中,我们为 InstallRollbackUninstall 方法提供了重写方法,因此我们需要添加将调用这些方法的自定义操作。

可以一次一个地将自定义操作添加到每个单独的节点,但有一种简单的方法可以一次添加多个。在自定义操作编辑器中右键单击“自定义操作”根节点,然后选择“添加自定义操作”。将显示“在项目中选择项”对话框。在列表中选择“应用程序文件夹”并单击“确定”。为包含 ShortcutsInstaller 类的项目选择“主输出...”,然后单击“确定”。一个“主输出...”条目将被添加到安装、提交、回滚和卸载的每个节点中。我们只对 InstallRollbackUninstall 方法进行了重写,因此我们需要从提交节点中删除“主输出...”条目。右键单击此条目并选择“删除”。

我们需要向 ShortcutsInstaller 类的 Install 方法传递一些参数。这些参数将告知 Install 方法用户为要创建的快捷方式所做的选择。选中“安装”节点下的“主输出...”条目后,按“F4”键查看“属性”窗口。将“CustomActionData”属性更改为“/ALLUSERS=[ALLUSERS] /DESKTOP_SHORTCUT=[DESKTOP_SHORTCUT] /QUICKLAUNCH_SHORTCUT=[QUICKLAUNCH_SHORTCUT]”。(注意:不要更改“Arguments”属性。)CustomActionData 值将向 ShortcutsInstaller 类提供三个参数:

  • /ALLUSERS=[ALLUSERS] - 此参数的左侧(“/ALLUSERS”)对应于 ShortcutsInstallerInstall 方法所期望的参数名称。此参数的右侧(“[ALLUSERS]”)对应于安装项目用于用户选择安装是为“所有人”还是“仅为我”的变量名称。不要求左侧的文本“ALLUSERS”与右侧的文本相同。左侧的文本可以是您喜欢的任何内容,只要它与您的安装程序类所期望的相同即可。
  • /DESKTOP_SHORTCUT=[DESKTOP_SHORTCUT] - 此参数的左侧(“/DESKTOP_SHORTCUT”)对应于 ShortcutsInstallerInstall 方法所期望的参数名称。此参数的右侧(“[DESKTOP_SHORTCUT]”)对应于我们在用户界面编辑器中为“Checkbox1Property”属性所赋的值。同样,不要求左侧的文本“DESKTOP_SHORTCUT”与右侧的文本相同。左侧的文本可以是您喜欢的任何内容,只要它与您的安装程序类所期望的相同即可。
  • /QUICKLAUNCH_SHORTCUT=[QUICKLAUNCH_SHORTCUT] - 与 DESKTOP_SHORTCUT 参数类似,这是用于用户界面编辑器中的“Checkbox2Property”属性。

生成并测试解决方案

现在您可以测试您的安装程序了。当您在 Visual Studio .NET 中向解决方案添加安装项目时,默认情况下它不包含在解决方案的生成中。要生成包括安装项目的整个解决方案,您可以在解决方案资源管理器中右键单击安装项目,然后选择“生成”。确保输出显示没有项目失败或被跳过。

要测试安装程序,请在解决方案资源管理器中右键单击安装项目并选择“安装”。您可以多次安装和卸载,以测试是否创建快捷方式以及是为“所有人”还是“仅为我”安装的各种组合。确保在卸载应用程序时快捷方式被删除。

摘要

如果您想在自己的安装项目中添加在桌面或快速启动栏上可选创建快捷方式的功能,那么我希望这篇文章能为您节省一些时间。

© . All rights reserved.