Issue #18: Windows MSI: finish dialog with Launch LightCraft
Interactive MSI installs now walk Welcome -> Ready to install -> progress
-> "Completed the LightCraft Setup Wizard" (Finish) with "LightCraft has
been installed" text and a checked-by-default "Launch LightCraft"
checkbox. Failure and cancellation end on WiX's standard FatalError /
UserExit pages; silent (/qn) and basic-UI (/passive) installs show no
dialogs and launch nothing.
- lightcraft.wxs: custom UI set = WixUI_Minimal without the licence page
(MIT OR Apache-2.0 asks nothing of installers), maintenance dialogs for
repair/remove, ARPNOMODIFY. Launch uses the Util extension's
WixUnelevatedShellExec via Explorer (runs as the signed-in user, never
elevated), Return="ignore", only when NOT Installed and the box is ticked.
- package.ps1: add WixToolset.UI/Util.wixext 5.0.2 to the global cache,
pass -ext ... -culture en-us, and generate plain side/banner bitmaps
(brand blue / white) so the MSI carries no WiX sample artwork.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 1c5feb7ab3)
This commit is contained in:
parent
65ad290e89
commit
45621f5679
|
|
@ -147,6 +147,7 @@ jobs:
|
|||
if: inputs.version != ''
|
||||
shell: bash
|
||||
run: cargo xtask version set "$LIGHTCRAFT_VERSION"
|
||||
# package.ps1 adds the matching WixToolset.UI/Util.wixext (5.0.2) to the global extension cache.
|
||||
- name: Install WiX
|
||||
shell: pwsh
|
||||
run: dotnet tool install --global wix --version 5.0.2
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
LightCraft MSI (WiX Toolset v5: `dotnet tool install -g wix`). Built by package.ps1:
|
||||
LightCraft MSI (WiX Toolset v5: `dotnet tool install -g wix`, plus the UI and Util extensions:
|
||||
`wix extension add -g WixToolset.UI.wixext/5.0.2 WixToolset.Util.wixext/5.0.2`). Built by package.ps1:
|
||||
|
||||
wix build lightcraft.wxs -arch x64 -d Version=1.2.3 -d BinDir=... -d IconPath=... -o out.msi
|
||||
wix build lightcraft.wxs -arch x64 -ext WixToolset.UI.wixext/5.0.2 -ext WixToolset.Util.wixext/5.0.2
|
||||
-d Version=1.2.3 -d BinDir=... -d IconPath=... -d UiDialogBmp=... -d UiBannerBmp=... -o out.msi
|
||||
|
||||
Per-machine install into Program Files, a Start Menu shortcut, `.pcraft` owned by LightCraft,
|
||||
and LightCraft offered under "Open with" for PSD/PSB and the image formats it reads (without
|
||||
|
|
@ -38,8 +40,87 @@
|
|||
<Feature Id="Main" Title="LightCraft" Level="1">
|
||||
<ComponentGroupRef Id="LightcraftFiles" />
|
||||
</Feature>
|
||||
|
||||
<UIRef Id="LightcraftUI" />
|
||||
</Package>
|
||||
|
||||
<!--
|
||||
Interactive install UI: Welcome -> Ready to install -> progress -> "Completed" (Finish), with a
|
||||
"Launch LightCraft" checkbox (checked by default). Failure and cancellation end on WiX's standard
|
||||
FatalError / UserExit dialogs instead of the completion page. Maintenance (running the MSI again,
|
||||
Repair/Remove from Apps) uses the standard maintenance dialogs.
|
||||
|
||||
This is WixUI_Minimal without its licence-acceptance page: LightCraft is MIT OR Apache-2.0,
|
||||
which ask nothing of people who just install and run it, so a click-through EULA would only
|
||||
add a step. Silent (/qn) and basic-UI (/passive, /qb) installs never show these dialogs, so
|
||||
they are unaffected, and nothing is launched.
|
||||
|
||||
The dialogs, strings and error/cancel pages come from WixToolset.UI.wixext; the side and
|
||||
banner bitmaps are plain panels generated by package.ps1 (no third-party artwork).
|
||||
-->
|
||||
<Fragment>
|
||||
<WixVariable Id="WixUIDialogBmp" Value="$(var.UiDialogBmp)" />
|
||||
<WixVariable Id="WixUIBannerBmp" Value="$(var.UiBannerBmp)" />
|
||||
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT"
|
||||
Value="LightCraft has been installed. You can start it at any time from the Start menu." />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch LightCraft" />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
|
||||
|
||||
<!-- "Launch LightCraft" on Finish. WixUnelevatedShellExec (Util extension) asks the desktop
|
||||
shell (Explorer) to start the program, so it runs as the signed-in user at normal
|
||||
integrity even when the installer UI itself is elevated (e.g. msiexec started from an
|
||||
admin prompt); it never runs from the elevated/SYSTEM server side. It is a UI-only control
|
||||
event, so silent and passive installs never run it. Return="ignore": a failed launch must
|
||||
not turn a successful install into an error. -->
|
||||
<Property Id="WixUnelevatedShellExecTarget" Value="[#LightcraftExe]" />
|
||||
<CustomAction Id="LaunchLightcraft" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)"
|
||||
DllEntry="WixUnelevatedShellExec" Execute="immediate" Impersonate="yes" Return="ignore" />
|
||||
|
||||
<UI Id="LightcraftUI">
|
||||
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
|
||||
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
|
||||
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
|
||||
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
|
||||
|
||||
<DialogRef Id="ErrorDlg" />
|
||||
<DialogRef Id="FatalError" />
|
||||
<DialogRef Id="FilesInUse" />
|
||||
<DialogRef Id="MsiRMFilesInUse" />
|
||||
<DialogRef Id="PrepareDlg" />
|
||||
<DialogRef Id="ProgressDlg" />
|
||||
<DialogRef Id="ResumeDlg" />
|
||||
<DialogRef Id="UserExit" />
|
||||
<DialogRef Id="WelcomeDlg" />
|
||||
<DialogRef Id="VerifyReadyDlg" />
|
||||
<DialogRef Id="MaintenanceWelcomeDlg" />
|
||||
<DialogRef Id="MaintenanceTypeDlg" />
|
||||
|
||||
<!-- First install / upgrade (and patches): Welcome -> Ready to install. -->
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="1"
|
||||
Condition="NOT Installed OR PATCH" />
|
||||
|
||||
<!-- Maintenance: Welcome -> Repair / Remove -> Ready. -->
|
||||
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2"
|
||||
Condition="Installed AND NOT PATCH" />
|
||||
|
||||
<!-- Completion page: optionally launch, then close. -->
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchLightcraft" Order="1"
|
||||
Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 AND NOT Installed" />
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />
|
||||
|
||||
<!-- One feature, nothing to change: no "Change" in Apps & features. -->
|
||||
<Property Id="ARPNOMODIFY" Value="1" />
|
||||
</UI>
|
||||
|
||||
<UIRef Id="WixUI_Common" />
|
||||
</Fragment>
|
||||
|
||||
<Fragment>
|
||||
<ComponentGroup Id="LightcraftFiles" Directory="INSTALLFOLDER">
|
||||
<Component Id="LightcraftApp" Guid="8A1D3A39-F7C1-47B1-97AE-40C80BF82F4A">
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
Needs: Rust (MSVC toolchain + the target), the Windows SDK (rc.exe, signtool.exe),
|
||||
and WiX v5: dotnet tool install --global wix --version 5.0.2
|
||||
The WiX UI and Util extensions (installer dialogs, "Launch LightCraft" on Finish) are added to
|
||||
the global WiX extension cache by this script (wix extension add -g ..., needs network once).
|
||||
|
||||
.EXAMPLE
|
||||
pwsh packaging/windows/package.ps1 -Arch x64
|
||||
|
|
@ -32,6 +34,29 @@ function Invoke-Native([string] $What, [scriptblock] $Block) {
|
|||
if ($LASTEXITCODE -ne 0) { throw "$What failed with exit code $LASTEXITCODE" }
|
||||
}
|
||||
|
||||
# A 24-bit BMP of two flat colours: $SplitX pixels of $Left, then $Right (colours as R,G,B).
|
||||
# Used for the installer's side/banner bitmaps so the MSI ships no third-party artwork.
|
||||
function New-PanelBitmap([string] $Path, [int] $Width, [int] $Height, [int] $SplitX, [byte[]] $Left, [byte[]] $Right) {
|
||||
$stride = [int][Math]::Ceiling($Width * 3 / 4) * 4
|
||||
$row = New-Object byte[] $stride
|
||||
for ($x = 0; $x -lt $Width; $x++) {
|
||||
$c = if ($x -lt $SplitX) { $Left } else { $Right }
|
||||
$row[$x * 3] = $c[2]; $row[$x * 3 + 1] = $c[1]; $row[$x * 3 + 2] = $c[0] # BMP pixels are B,G,R
|
||||
}
|
||||
$pixels = $stride * $Height
|
||||
$ms = [IO.MemoryStream]::new()
|
||||
$w = [IO.BinaryWriter]::new($ms)
|
||||
$w.Write([byte]0x42); $w.Write([byte]0x4D) # 'BM'
|
||||
$w.Write([uint32](54 + $pixels)); $w.Write([uint32]0); $w.Write([uint32]54)
|
||||
$w.Write([uint32]40); $w.Write([int32]$Width); $w.Write([int32]$Height) # BITMAPINFOHEADER
|
||||
$w.Write([uint16]1); $w.Write([uint16]24); $w.Write([uint32]0); $w.Write([uint32]$pixels)
|
||||
$w.Write([int32]2835); $w.Write([int32]2835); $w.Write([uint32]0); $w.Write([uint32]0)
|
||||
for ($y = 0; $y -lt $Height; $y++) { $w.Write($row) }
|
||||
$w.Flush()
|
||||
[IO.File]::WriteAllBytes($Path, $ms.ToArray())
|
||||
$w.Dispose()
|
||||
}
|
||||
|
||||
# The version lives in one place: [workspace.package] version in the root Cargo.toml.
|
||||
$Version = $env:LIGHTCRAFT_VERSION
|
||||
if (-not $Version) {
|
||||
|
|
@ -91,10 +116,26 @@ Copy-Item (Join-Path $Bin 'lightcraft.exe'), (Join-Path $Bin 'lightcraft-cli.exe
|
|||
& (Join-Path $PSScriptRoot 'sign.ps1') (Join-Path $Stage 'lightcraft.exe') (Join-Path $Stage 'lightcraft-cli.exe')
|
||||
|
||||
# ---- MSI ---------------------------------------------------------------------------------------
|
||||
# Extensions must match the WiX tool version (5.0.2, see release.yml). Re-adding is harmless.
|
||||
$WixVersion = '5.0.2'
|
||||
$WixExtensions = 'WixToolset.UI.wixext', 'WixToolset.Util.wixext'
|
||||
foreach ($ext in $WixExtensions) {
|
||||
Invoke-Native "wix extension add $ext/$WixVersion" { wix extension add -g "$ext/$WixVersion" }
|
||||
}
|
||||
$ExtArgs = foreach ($ext in $WixExtensions) { '-ext'; "$ext/$WixVersion" }
|
||||
|
||||
# Installer dialog bitmaps (WixUI sizes): a brand-blue side panel on the welcome/finish pages and
|
||||
# a plain white banner on the inner pages. Generated here, never committed.
|
||||
$UiDialogBmp = Join-Path $Stage 'ui-dialog.bmp'
|
||||
$UiBannerBmp = Join-Path $Stage 'ui-banner.bmp'
|
||||
New-PanelBitmap $UiDialogBmp 493 312 164 ([byte[]](0x4E, 0x7B, 0xFB)) ([byte[]](0xFF, 0xFF, 0xFF))
|
||||
New-PanelBitmap $UiBannerBmp 493 58 0 ([byte[]](0xFF, 0xFF, 0xFF)) ([byte[]](0xFF, 0xFF, 0xFF))
|
||||
|
||||
$Msi = Join-Path $Dist "lightcraft-$Version-windows-$Arch.msi"
|
||||
Invoke-Native 'wix build' {
|
||||
wix build (Join-Path $PSScriptRoot 'lightcraft.wxs') -arch $Arch `
|
||||
wix build (Join-Path $PSScriptRoot 'lightcraft.wxs') -arch $Arch -culture en-us @ExtArgs `
|
||||
-d "Version=$MsiVersion" -d "BinDir=$Stage" -d "IconPath=$(Join-Path $Root 'assets\app-icon\lightcraft.ico')" `
|
||||
-d "UiDialogBmp=$UiDialogBmp" -d "UiBannerBmp=$UiBannerBmp" `
|
||||
-o $Msi
|
||||
}
|
||||
# wix writes its debug symbols (.wixpdb) next to the MSI; keep them out of the release assets.
|
||||
|
|
|
|||
Loading…
Reference in New Issue