fix(msi): correct CustomAction pattern for elements\ read-only

- Use SetProperty + WixQuietExec two-step pattern to pass runtime
  INSTALLDIR to a deferred CustomAction (fixes WIX1077 and WIX0400)
- Add WixToolset.Util.wixext/7.0.0 extension (required for WixQuietExec)
- Fix condition syntax: collapse multi-line conditions to single line
- Add -ext WixToolset.Util.wixext to wix build command in windows-msi.yml
This commit is contained in:
Laurent Trinques
2026-05-21 02:55:28 +02:00
parent eeaa059a77
commit 9760288db6
2 changed files with 23 additions and 10 deletions

View File

@@ -92,6 +92,7 @@ jobs:
echo $toolsPath >> $env:GITHUB_PATH
wix eula accept wix7
wix extension add WixToolset.UI.wixext/7.0.0
wix extension add WixToolset.Util.wixext/7.0.0
Write-Host "WiX v7 installed, EULA accepted, UI extension added."
# ----------------------------------------------------------------
@@ -208,6 +209,7 @@ jobs:
-d "FilesDir=$filesDir" `
-d "LicenseRtf=$licRtf" `
-ext WixToolset.UI.wixext `
-ext WixToolset.Util.wixext `
-o "dist\$outputName"
if (-not (Test-Path "dist\$outputName")) {

View File

@@ -104,23 +104,34 @@
<!-- ============================================================
CustomAction: set elements\ subtree read-only after install
Runs an inline PowerShell script via WiX's QtExec pattern.
Executes after all files are committed to disk (afterInstallFinalize).
============================================================ -->
<Property Id="SetElementsReadOnly"
Value="powershell.exe -NonInteractive -NoProfile -WindowStyle Hidden -Command &quot;Get-ChildItem -Path '[INSTALLDIR]elements' -Recurse -File | ForEach-Object { $_.IsReadOnly = $true }&quot;" />
Pattern used: two-step SetProperty + Exec
1. CA_ResolveElementsPath (immediate, SetProperty):
copies the runtime value of INSTALLDIR into the property
ELEMENTS_READONLY_CMD, building the full powershell command.
2. CA_SetElementsReadOnly (deferred, Execute="deferred"):
runs the command stored in ELEMENTS_READONLY_CMD.
This is the correct WiX v4/v7 pattern for passing a
directory path (resolved at runtime) to a deferred CA.
============================================================ -->
<!-- Step 1: build the powershell command with the resolved INSTALLDIR -->
<CustomAction Id="CA_ResolveElementsPath"
Property="CA_SetElementsReadOnly"
Value="powershell.exe -NonInteractive -NoProfile -WindowStyle Hidden -Command &quot;Get-ChildItem -LiteralPath '[INSTALLDIR]elements' -Recurse -File | ForEach-Object { `$_.IsReadOnly = `$true }&quot;" />
<!-- Step 2: deferred execution of the powershell command -->
<CustomAction Id="CA_SetElementsReadOnly"
Property="SetElementsReadOnly"
ExeCommand=""
BinaryRef="Wix4UtilCA_X86"
DllEntry="WixQuietExec"
Execute="deferred"
Impersonate="no"
Return="ignore" />
<InstallExecuteSequence>
<Custom Action="CA_SetElementsReadOnly" After="InstallFiles">
NOT Installed AND NOT REMOVE
</Custom>
<Custom Action="CA_ResolveElementsPath" After="InstallFiles">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="CA_SetElementsReadOnly" After="CA_ResolveElementsPath">NOT Installed AND NOT REMOVE</Custom>
</InstallExecuteSequence>
<!-- ============================================================