如何让MSI安装至每次唯一的目录?若用自定义动作应置于何处?
我希望实现如下等效的WiX目录配置:
<Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder"> <Directory Id="INSTALLDIR" Name="$(var.MyProductName)"> <Directory Id="SXSBASEDIR" Name="SxS"> <Directory Id="SXSDIR" Name="<<Unique-ID>>"> <Directory Id="BINDIR" Name="bin" /> </Directory> </Directory> </Directory> </Directory> </Directory>
其中<<Unique-ID>>必须在每次安装时都不同:首次安装后文件部署到\Program Files\MyProductName\SxS\<<Unique-ID-1>>\bin;卸载后重新安装同一MSI时,文件要部署到\Program Files\MyProductName\SxS\<<Unique-ID-2>>\bin。
我曾尝试用DATE和TIME变量生成<<Unique-ID>>,但它们包含文件名非法字符,且格式受区域设置影响。恳请提供实现建议;若只能使用自定义动作,请问应将其放置在InstallExecuteSequence的哪个位置?
需求背景
我开发了一款内核模式文件系统驱动(FSD),受Windows文件系统设计限制,FSD加载后难以卸载,导致卸载和升级操作必须重启系统。
为此我提出一种解决方案:将FSD安装到每次安装都唯一的目录,以此支持同时加载多个FSD实例(包括当前安装的实例和已卸载的旧实例,FSD已修改为支持并行运行)。
场景示例:
- 用户安装
MyProductName.msi- FSD被安装到
\Program Files\MyProductName\SxS\1\bin
- FSD被安装到
- FSD被加载,且可能无法卸载
- 用户卸载
MyProductName.msi- Windows安装程序将
\Program Files\MyProductName\SxS\1\bin标记为待删除,但目录仍保留
- Windows安装程序将
- 用户重新安装同一
MyProductName.msi- FSD被安装到
\Program Files\MyProductName\SxS\2\bin
- FSD被安装到
- FSD再次被加载
- 此时存在两个相同的FSD实例:
- 原安装的FSD会在重启后消失,其所在目录
\Program Files\MyProductName\SxS\1\bin已标记为待删除,重启后将被移除 - 当前安装的FSD会在重启后保留,其所在目录为
\Program Files\MyProductName\SxS\2\bin
- 原安装的FSD会在重启后消失,其所在目录
实现方案
1. 生成合法的唯一目录名
推荐两种生成方式,都能避免非法字符和区域设置问题:
- GUID截取法:在自定义动作中生成一个全新的GUID,去掉其中的
-符号(比如把{12345678-1234-1234-1234-1234567890AB}转为123456781234123412341234567890AB),将这个字符串作为SXSDIR的Name属性值。GUID本身全球唯一,完全满足每次安装生成不同ID的需求,且不含任何非法字符。 - 序号递增法:在自定义动作中遍历
SxS目录下的所有子目录,提取其中的数字序号,找到最大值后加1作为新的ID(首次安装时目录为空,直接设为1)。这种方式生成的ID更直观,适合需要清晰序号的场景。
2. 自定义动作的位置要求
如果使用自定义动作来设置唯一目录,必须将其放置在InstallExecuteSequence的**CostInitialize之后、CostFinalize之前**,且设为**即时执行(immediate)**类型。
原因是:Windows Installer在CostInitialize阶段完成目录结构的初始化,此时可以修改目录属性;而CostFinalize阶段会计算磁盘空间、确定最终安装路径,因此必须在这之前完成唯一目录的设置,确保后续文件部署使用正确路径。
WiX配置示例:
<!-- 先定义自定义动作的DLL --> <Binary Id="CustomActionsDLL" SourceFile="path/to/your/CustomActions.dll" /> <!-- 定义自定义动作 --> <CustomAction Id="SetUniqueSXSDir" BinaryKey="CustomActionsDLL" DllEntry="SetUniqueSXSDir" Execute="immediate" /> <!-- 在执行序列中指定位置 --> <InstallExecuteSequence> <Custom Action="SetUniqueSXSDir" After="CostInitialize" /> </InstallExecuteSequence>
内容的提问来源于stack exchange,提问作者Bill Zissimopoulos

