WiX自定义动作无法同步路径值至文本框及DISM调用问题
我来帮你搞定这两个WiX自定义动作的问题,都是实际开发中很容易踩的坑:
问题1:BrowseForFolder返回值无法同步到SXS_PATH属性和文本框
原因分析
你的代码没法同步路径,主要是两个关键问题:
- 未处理用户取消浏览的场景:当用户点击BrowseForFolder对话框的「取消」按钮时,
objFolder会变成空值(Nothing),这时候执行Set objFolderItem = objFolder.Self会直接抛出错误,导致Err.Number不为0,跳过了设置Session.Property("SXS_PATH")的逻辑。 - SXS_PATH属性未标记为Secure:在UAC启用的Windows环境下,immediate模式的自定义动作要把属性传递给后续UI或动作,必须给属性加上
Secure="yes",否则属性值会被UAC机制过滤,无法正常传递。
修复方案
1. 修改VBScript代码,增加空值判断
给脚本加上错误捕获,并且只在用户确实选择了文件夹时才设置属性:
Option Explicit Public Sub GetSxsPath() Dim objShell, objFolder, objFolderItem, strPath Const WINDOW_HANDLE = 0 Const NO_OPTIONS = 0 On Error Resume Next ' 开启错误捕获,避免取消时直接崩溃 Set objShell = CreateObject("Shell.Application") Set objFolder = objShell.BrowseForFolder _ (WINDOW_HANDLE, "Choose SxS Folder for IIS role and .NET features", NO_OPTIONS) If Not objFolder Is Nothing Then ' 仅当用户选择了文件夹才继续 Set objFolderItem = objFolder.Self strPath = objFolderItem.Path Session.Property("SXS_PATH") = strPath End If ' 清理对象,避免内存泄漏 Set objFolderItem = Nothing Set objFolder = Nothing Set objShell = Nothing On Error GoTo 0 ' 关闭错误捕获 End Sub
2. 修改WiX中的SXS_PATH属性,添加Secure标记
<Property Id="SXS_PATH" Value="C:\" Secure="yes" />
这样修改后,用户选择文件夹时会正确同步路径到SXS_PATH,取消操作也不会报错,属性值也能在UAC环境下正常传递。
问题2:调用DISM命令并传入SXS_PATH的值
DISM是修改系统组件的命令,需要管理员权限,所以不能用普通的immediate自定义动作,得用**延迟执行(deferred)**的模式,并且禁用用户模拟(Impersonate="no"),同时要把SXS_PATH属性传递到延迟上下文里,具体实现方案如下:
方案1:用WiX自带的CAQuietExec执行命令(推荐)
这种方式不用额外写脚本,直接用WiX内置的静默执行组件:
- 定义传递属性的CustomAction:把SXS_PATH打包成CustomActionData,传递给延迟动作
<CustomAction Id="SetDismActionData" Property="RunDismCommand" Value="SXS_PATH=[SXS_PATH]" Execute="immediate" />
- 定义执行DISM的CustomAction:配置延迟执行和权限
<CustomAction Id="RunDismCommand" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Impersonate="no" Return="check" />
- 配置DISM命令参数:用转义后的引号和连接符整合命令,通过
[SXS_PATH]引用传递的路径
<Property Id="RunDismCommand" Value=""C:\Windows\System32\DISM.exe" /Online /Enable-Feature /FeatureName:NetFx3 /All /Source:[SXS_PATH] && "C:\Windows\System32\DISM.exe" /Online /Enable-Feature /FeatureName:IIS-WebServerRole /FeatureName:IIS-RequestFiltering /FeatureName:IIS-Security /FeatureName:IIS-HttpLogging /FeatureName:IIS-HealthAndDiagnostics /FeatureName:IIS-WebServerManagementTools /FeatureName:IIS-ManagementConsole /FeatureName:IIS-IIS6ManagementCompatibility /FeatureName:IIS-Metabase /FeatureName:IIS-WebServer /FeatureName:IIS-Performance /FeatureName:IIS-HttpCompressionStatic /FeatureName:IIS-CommonHttpFeatures /FeatureName:IIS-StaticContent /FeatureName:IIS-DefaultDocument /FeatureName:IIS-DirectoryBrowsing /FeatureName:IIS-HttpErrors /FeatureName:IIS-ApplicationDevelopment /FeatureName:IIS-ASPNET /FeatureName:IIS-NetFxExtensibility /FeatureName:IIS-ISAPIExtensions /FeatureName:IIS-ISAPIFilter /all /Source:[SXS_PATH]" />
注意:XML里引号要用
"转义,多个命令用&&连接
- 添加到安装序列:确保动作在正确的时机执行,且只在用户选择离线安装时触发
<InstallExecuteSequence> <Custom Action="SetDismActionData" Before="RunDismCommand">ONOFF_PROPERTY = "1" AND NOT Installed</Custom> <Custom Action="RunDismCommand" Before="InstallFinalize">ONOFF_PROPERTY = "1" AND NOT Installed</Custom> </InstallExecuteSequence>
方案2:用VBScript执行DISM(适合需要更多自定义逻辑的场景)
如果你更习惯用VBScript,也可以写一个延迟执行的脚本:
- VBScript代码:通过
Session.CustomActionData获取传递的路径
Option Explicit Public Sub RunDism() Dim strSxsPath, objShell, strCmd strSxsPath = Session.CustomActionData("SXS_PATH") ' 拼接DISM命令,用Chr(34)表示引号 strCmd = """C:\Windows\System32\DISM.exe"" /Online /Enable-Feature /FeatureName:NetFx3 /All /Source:" & Chr(34) & strSxsPath & Chr(34) strCmd = strCmd & " && ""C:\Windows\System32\DISM.exe"" /Online /Enable-Feature /FeatureName:IIS-WebServerRole /FeatureName:IIS-RequestFiltering /FeatureName:IIS-Security /FeatureName:IIS-HttpLogging /FeatureName:IIS-HealthAndDiagnostics /FeatureName:IIS-WebServerManagementTools /FeatureName:IIS-ManagementConsole /FeatureName:IIS-IIS6ManagementCompatibility /FeatureName:IIS-Metabase /FeatureName:IIS-WebServer /FeatureName:IIS-Performance /FeatureName:IIS-HttpCompressionStatic /FeatureName:IIS-CommonHttpFeatures /FeatureName:IIS-StaticContent /FeatureName:IIS-DefaultDocument /FeatureName:IIS-DirectoryBrowsing /FeatureName:IIS-HttpErrors /FeatureName:IIS-ApplicationDevelopment /FeatureName:IIS-ASPNET /FeatureName:IIS-NetFxExtensibility /FeatureName:IIS-ISAPIExtensions /FeatureName:IIS-ISAPIFilter /all /Source:" & Chr(34) & strSxsPath & Chr(34) Set objShell = CreateObject("WScript.Shell") objShell.Run strCmd, 0, True ' 0表示隐藏命令窗口,True表示等待命令执行完成 Set objShell = Nothing End Sub
- WiX配置:定义脚本二进制文件和对应的CustomAction
<CustomAction Id="SetDismVbsActionData" Property="RunDismVbs" Value="SXS_PATH=[SXS_PATH]" Execute="immediate" /> <CustomAction Id="RunDismVbs" BinaryKey="DismScript.vb" Impersonate="no" Return="check" Execute="deferred" VBScriptCall="RunDism" /> <Binary Id="DismScript.vb" SourceFile="RunDism.vbs"/> <InstallExecuteSequence> <Custom Action="SetDismVbsActionData" Before="RunDismVbs">ONOFF_PROPERTY = "1" AND NOT Installed</Custom> <Custom Action="RunDismVbs" Before="InstallFinalize">ONOFF_PROPERTY = "1" AND NOT Installed</Custom> </InstallExecuteSequence>
内容的提问来源于stack exchange,提问作者illuminate33
相关产品推荐
相关产品推荐

