设置项目 - 安装/升级序列






4.60/5 (4投票s)
在安装/升级设置项目时,显示不同的对话框序列和文本。
引言
在分发 .NET 应用程序时,设置项目是一个非常有用的工具,因为它易于使用,并且可以跟踪应用程序正常安装和工作所需的所有库依赖项。
我们有一个简单的任务想要实现。我们的对话框安装序列是:欢迎界面、文件夹界面、确认安装界面、完成安装界面。我们想要的是,当安装新版本的产品时,安装程序需要检测是否已安装前一版本,如果是,则跳过文件夹界面,因为用户在首次安装时已经选择了安装位置,并且选择了“所有用户”还是“仅我”。我们还希望更改对话框中的所有文本,以表明安装程序正在升级而不是安装。
如何使用它
您必须在“逐步”部分的开头手动完成“设置项目更改”的第 1 至第 7 步,并将文章附带的 JavaScript 文件添加为设置项目的后期生成事件,该文件将自动执行 ORCA 演示的 MSI 更改。PostBuildEvent="$(ProjectDir)PBE.js" "$(BuiltOuputPath)"
并设置 RemovePreviousVersions=True
。

如何实现
为了实现我们的目标,我们使用了设置项目本身提供的功能,以及后期生成 JavaScript,该 JavaScript 自动化了必要的 MSI 更改。
挑战之一是检测我们是正在安装还是升级 - 没有简单的参数可以告诉我们。有 [PREVIOUSVERSIONSINSTALLED]
,但正如某处所说,它在前一版本的卸载过程中才有意义(具有正确的值),而在安装或升级时则不然。所以我们所做的是添加一个注册表值,记住最后一个安装/升级的 [ProductCode]
,然后在升级运行时(升级是用相同的 [UpgradeCode]
但不同的 [ProductCode]
进行安装),将该注册表值读取到参数 [OLDPRODUCTCODE]
中,并检查 SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[OLDPRODUCTCODE]
下是否存在具有该值的注册表项,这意味着之前安装过同一产品,换句话说,我们正在升级。
跳过 FolderForm
意味着我们必须记住用户的 TargetFolder
和 AllUsers
/JustMe
选择。这是通过在 HKLM\Software\[Manufacturer]\[UpgradeCode]
下创建注册表值来完成的(我们使用 [UpgradeCode]
,因为它对于安装是唯一且一致的,但您也可以使用简单的 string
)来存储这些选择。
AU - 存储 AllUsers
/JustMe
的选择(如果 [ALLUSERS]
= "" 则为 JustMe
,否则为 AllUsers
)。
最初,我们尝试仅存储参数 [FolderForm_AllUsers]
,该参数在 FolderForm
中根据单选按钮的选择保存 ALL 或 ME 的值,但出于某种奇怪的原因,在写入注册表值 TF - 保存 [TARGETDIR]
的阶段之前,它丢失了其值。要读取这些值用于连续升级,创建了注册表搜索 - 一个将注册表值 TF 检索到 [TARGETDIR]
,另一个将注册表值 AU 检索到 [REMALLUSERS]
。
我们不直接设置 [ALLUSERS]
或 [FolderForm_AllUsers]
,因为它们会被 MSI 以某种方式操作,所以即使一开始就用记住的值设置,它们也会在之后被重置并丢失其值。当使用 ORCA 查看 MSI 时,在 Parameters
表中,参数 [FolderForm_AllUsers]
被设置为 ME
,并且有一个 VSDCA_FolderForm_AllUsers action
在 InstallUISequence
表中被调用来将其值更改为 ALL
。因此,现在在升级时,我们添加了条件 [REMALLUSERS]<>"ME"
,这样如果用户之前选择了 ME
,这个操作将被跳过,确保 [FolderForm_AllUsers]
保持设置为 ME
。
跳过 FormFolder
对话框意味着我们需要一个不同于导致该对话框的表单的 NextButton
行为 - 在我们的例子中是 WelcomeForm
- 以及 FormFolder
之后表单的 PreviousButton
行为 - 在我们的例子中是 ConfirmInstall
表单。
WelcomeForm
NextButton
的行为由参数 [WelcomeForm_NextArgs]
决定,ConfirmInstall
PreviousButton
的行为由参数 [ConfirmInstallForm_PrevArgs]
决定。定义了自定义操作,使我们能够将其更改为升级值 [WelcomeForm_NextArgs]=ConfirmInstallForm [ConfirmInstallForm_PrevArgs]=""
,并在 InstallUISequence
中以升级为条件的( ISOLDPRODUCTCODEINSTALLED<>""
)调用这些自定义操作。
此外,我们希望更改所有控件的文本,以说明我们是正在安装还是升级,这意味着我们想要替换的单词应该是参数,并且参数的值会根据安装或升级进行相应设置。所以我们在 Parameter
表中为我们希望在安装/升级时不同的每个单词创建了一个参数,并将其值设置为指示“安装”。创建过程允许在 CustomAction
表中更改这些参数为升级值。在升级时调用这些自定义操作以实际更改 InstallUISequence
表中的参数值,条件是 ISOLDPRODUCTCODEINSTALLED<>
""。在 Control
表的 Text
字段中,将所有单词替换为参数名称,这样根据安装/升级,我们将看到相应的文本。
逐步操作
设置项目更改
- 在本地计算机上创建注册表值 TF 以记住上次安装的
[TARGETDIR]
- 在本地计算机上创建注册表值 AU 以记住“所有用户”/“仅我”
- 在本地计算机上创建注册表值 PC 以记住上次安装/升级的
[ProductCode]
- 创建注册表搜索,将 TF 值从注册表读取到变量
[TARGETDIR]
- 创建注册表搜索,将 AU 值从注册表读取到变量
[REMALLUSERS]
非常重要的是,第 6 步必须在第 7 步之前执行,因为第 7 步取决于第 6 步检索到的值。
- 创建注册表搜索,将 PC 值从注册表读取到变量
[OLDPRODUCTCODE]
- 创建注册表搜索,以检查注册表项(安装)
SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[OLDPRODUCTCODE]
是否存在,并将其写入变量[ISOLDPRODUCTCODEINSTALLED]
。这将决定我们是正在安装还是升级。后期生成事件对 MSI 的更改 - (如前所述,当设置为后期生成事件时,由文章附带的 JavaScript 自动完成。)
- 使操作
VSDCA_FolderForm_AllUsers
(将[FolderForm_AllUsers]
设置为其默认值)仅在安装时发生,在升级时,值将根据从注册表[REMALLUSERS]
中检索的值进行设置。根据是安装还是升级更改所有控件的文本 {对于每个我们想要更改的
string
- 替换对} - 在
Property
表中插入一个具有默认(安装)值的参数 - 在
CustomAction
表中插入一个可以将其参数设置为升级值操作 - 在
InstallUISequence
表中插入对自定义操作的调用,该调用在升级时(条件ISOLDPRODUCTCODEINSTALLED<>""
)实际将参数更改为升级值 - 对于
Control
表中的每一行,在Text
列的值中,用参数名称替换我们希望依赖于安装/升级的string
{结束}
升级时删除
FolderForm
。 - 在
CustomAction
表中插入一个操作,该操作可以设置[WelcomeForm_NextArgs]
的值(该值决定了单击WelcomeForm
中“下一步”按钮时显示的表单(我们想跳过 Folder form)),使其成为FolderForm
之后的表单名称,在本例中是ConfirmInstall
表单。 - 在
InstallUISequence
表中插入对该自定义操作的调用,该调用在升级时(条件ISOLDPRODUCTCODEINSTALLED<>""
)实际上将[WelcomeForm_NextArgs]
设置为ConfirmInstall
表单。 - 在
CustomAction
表中插入一个操作,该操作可以设置[ConfirmInstallForm_PrevArgs]
的值(该值决定了单击ConfirmInstall
表单中“上一步”按钮时显示的表单(我们想跳过FolderForm
)),使其成为FolderForm
之前的表单名称,在本例中是WelcomeForm
。 - 在
InstallUISequence
表中插入对该自定义操作的调用,该调用在升级时(条件ISOLDPRODUCTCODEINSTALLED<>""
)实际上将[ConfirmInstallForm_PrevArgs]
设置为WelcomeForm
。由于我们在升级时删除了文件夹表单,但我们仍然希望当
FolderForm
的“下一步”被点击时发生的所有操作都被转移到升级时的WelcomeForm
的“下一步”,因为其余安装过程依赖于它们,其中一个就是设置记住的“所有用户”/“仅我”的选择 {对于ControlEvent
表中的每一行,如果Dialog='FolderForm'
且Control='NextButton'
}。 - 复制相同的值,但用于 Dialog=
WelcomeForm
Control=NextButton
和 Condition=ISOLDPRODUCTCODEINSTALLED=""
- 现在WelcomeForm
在升级时将执行FolderForm
在安装时执行的所有操作。
注意
JavaScript 更改仅适用于描述的安装序列 **欢迎界面、文件夹界面、确认安装界面**,但可以更改为适用于其他安装序列。另外,假设 [FolderForm_AllUsers]
参数在 MSI 的 Property
表中默认设置为 ME
,事实似乎也是如此(可以添加一个检查来设置此参数,如果不存在,以保持一致性)。