Issue #1058: an update keeps the desktop shortcut only if it is still there (#1570)

The launcher installs and updates PdfCraft with `msiexec /passive` and no properties, so
INSTALLDESKTOPSHORTCUT always took its default of 1 and every update put the shortcut back,
even for users who had deleted it. The property no longer has a static default: a SetProperty
after AppSearch turns it on for a first install, and for an update only while PdfCraft.lnk is
still on the all-users desktop. A value on the command line still wins, and the full-UI
checkbox still starts ticked on a first install.

test-msi.ps1 checks the action runs after AppSearch in both sequences and evaluates its
condition for first installs, updates with and without the shortcut, and
INSTALLDESKTOPSHORTCUT=0.
This commit is contained in:
pauliusiv123-rgb authored and GitHub committed 2026-10-13 00:09:56 -04:00
1 parent e9b9d98bb4
commit 91c0b93c7f
3 files changed
+50 -11

No files matched your search

+6 -2
View File
@@ -121,7 +121,10 @@ neither the MSI nor the portable zip needs the Visual C++ redistributable.
dialogs. Full UI confirms success with Finish; failures and cancellations have distinct messages.
The welcome dialog has a "Create a desktop shortcut" checkbox and an "Edit with PdfCraft"
right-click menu checkbox, both ticked by default. `/qn` and `/qb` stay unattended and create
both unless `INSTALLDESKTOPSHORTCUT=0` or `INSTALLPDFCONTEXTMENU=0` is passed.
both unless `INSTALLDESKTOPSHORTCUT=0` or `INSTALLPDFCONTEXTMENU=0` is passed. An update (the
launcher runs `msiexec /passive` with no properties) keeps the desktop shortcut only if
`PdfCraft.lnk` is still on the all-users desktop, so deleting it once keeps it away (#1058); a
value given on the command line wins.
- The PDF right-click entry (#682) is a classic verb under
`HKLM\Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit`, so it shows whichever app
is the default for `.pdf` and never changes that default. Several selected PDFs (up to 15, the
@@ -129,7 +132,8 @@ neither the MSI nor the portable zip needs the Visual C++ redistributable.
hand-off opens them as tabs. On Windows 11 it is under "Show more options"; a first-level entry
needs an `IExplorerCommand` handler and package identity (a sparse MSIX package), which the MSI
doesn't have.
- `test-msi.ps1 <file.msi>` checks the compiled shortcut, context-menu and dialog tables. Packaging
- `test-msi.ps1 <file.msi>` checks the compiled shortcut, context-menu and dialog tables, and
evaluates the desktop shortcut's first-install and update conditions. Packaging
runs it in a child process before signing, so the MSI isn't held open when signtool runs. The ARM64 install
smoke test checks that both all-users shortcuts point at the installed `pdfcraft.exe` and are
removed on uninstall, and that `INSTALLDESKTOPSHORTCUT=0` skips the desktop one.
+15 -5
View File
@@ -5,7 +5,7 @@
wix build pdfcraft.wxs installer-ui.wxs -arch x64 -d Version=1.2.3 -d BinDir=... -d ModelsDir=... -d IconPath=... -o out.msi
Per-machine install into Program Files, a Start Menu shortcut, a desktop shortcut (on by default,
optional), an App Paths entry, PdfCraft registered as a PDF handler (offered under "Open with"
optional, and an update keeps it only if it is still there), an App Paths entry, PdfCraft registered as a PDF handler (offered under "Open with"
and in Default apps, never forced as the default), and "Edit with PdfCraft" on PDF files'
right-click menu (on by default, optional). Files opened with PdfCraft arrive on its command
line.
@@ -37,10 +37,20 @@
<Property Id="ARPURLINFOABOUT" Value="https://getartcraft.com/apps/pdfcraft" />
<Property Id="ARPURLUPDATEINFO" Value="https://getartcraft.com/apps/pdfcraft" />
<Property Id="ARPHELPLINK" Value="https://discord.gg/artcraft" />
<!-- Desktop shortcut (#143): on by default, a checkbox in the full-UI welcome dialog, and
INSTALLDESKTOPSHORTCUT=0 on the msiexec command line for unattended installs. Secure, so the
choice made in the (unelevated) UI reaches the elevated install. -->
<Property Id="INSTALLDESKTOPSHORTCUT" Value="1" Secure="yes" />
<!-- Desktop shortcut (#143): on by default for a first install, a checkbox in the full-UI
welcome dialog, and INSTALLDESKTOPSHORTCUT=0 on the msiexec command line for unattended
installs. Secure, so the choice made in the (unelevated) UI reaches the elevated install.
An update keeps the shortcut only if it is still on the desktop (#1058): the launcher
updates with /passive and no properties, so deleting the shortcut is the only way its
users can say no. A value given on the command line wins. -->
<Property Id="INSTALLDESKTOPSHORTCUT" Secure="yes" />
<Property Id="EXISTINGDESKTOPSHORTCUT">
<DirectorySearch Id="DesktopSearch" Path="[DesktopFolder]" Depth="0">
<FileSearch Name="PdfCraft.lnk" />
</DirectorySearch>
</Property>
<SetProperty Id="INSTALLDESKTOPSHORTCUT" Value="1" After="AppSearch" Sequence="first"
Condition="NOT INSTALLDESKTOPSHORTCUT AND (NOT WIX_UPGRADE_DETECTED OR EXISTINGDESKTOPSHORTCUT)" />
<!-- "Edit with PdfCraft" on the right-click menu of PDF files in File Explorer (#682): on by
default, the same way, and INSTALLPDFCONTEXTMENU=0 for unattended installs without it. -->
<Property Id="INSTALLPDFCONTEXTMENU" Value="1" Secure="yes" />
+29 -4
View File
@@ -74,7 +74,7 @@ Assert-Equal $status[0] 'Please wait while setup completes.' 'Persistent progres
Assert-NoRow 'SELECT `Event` FROM `EventMapping` WHERE `Dialog_` = ''InstallProgress'' AND `Control_` = ''Status''' 'progress text subscription'
# Plain (non-advertised) shortcuts to pdfcraft.exe, each in its own component; the desktop one is
# gated by INSTALLDESKTOPSHORTCUT, which defaults to 1 and is secure so the UI choice reaches the
# gated by INSTALLDESKTOPSHORTCUT, which is secure so the UI choice reaches the
# elevated install.
foreach ($entry in @(@('StartMenuShortcut', 'ProgramMenuFolder', 'PdfcraftStartMenuShortcut', ''),
@('DesktopShortcut', 'DesktopFolder', 'PdfcraftDesktopShortcut', 'INSTALLDESKTOPSHORTCUT = 1'))) {
@@ -88,8 +88,33 @@ foreach ($entry in @(@('StartMenuShortcut', 'ProgramMenuFolder', 'PdfcraftStartM
Assert-Equal $component[0] $entry[1] "$($entry[2]) directory"
Assert-Equal $component[1] $entry[3] "$($entry[2]) condition"
}
$default = Read-Row 'SELECT `Value` FROM `Property` WHERE `Property` = ''INSTALLDESKTOPSHORTCUT''' 1
Assert-Equal $default[0] '1' 'Desktop shortcut default'
# No static default (#1058): a SetProperty after AppSearch turns it on for a first install, and for an
# update only while the old shortcut is still on the desktop; a command-line value is left alone.
Assert-NoRow 'SELECT `Value` FROM `Property` WHERE `Property` = ''INSTALLDESKTOPSHORTCUT''' 'desktop shortcut static default'
$setDesktop = Read-Row 'SELECT `Action`, `Target` FROM `CustomAction` WHERE `Source` = ''INSTALLDESKTOPSHORTCUT''' 2
Assert-Equal $setDesktop[1] '1' 'Desktop shortcut default value'
foreach ($sequence in @('InstallUISequence', 'InstallExecuteSequence')) {
$search = Read-Row ('SELECT `Sequence` FROM `' + $sequence + '` WHERE `Action` = ''AppSearch''') 1
$set = Read-Row ('SELECT `Sequence`, `Condition` FROM `' + $sequence + '` WHERE `Action` = ''' + $setDesktop[0] + '''') 2
if ([int] $set[0] -le [int] $search[0]) { throw "$sequence must set the desktop shortcut default after AppSearch" }
}
$session = $null
try {
$session = $Installer.OpenPackage((Resolve-Path -LiteralPath $Path).Path, 1)
foreach ($case in @(
@('first install', '', '', '', 1),
@('first install, INSTALLDESKTOPSHORTCUT=0', '0', '', '', 0),
@('update, shortcut still there', '', '{00000000-0000-0000-0000-000000000000}', 'C:\Users\Public\Desktop\PdfCraft.lnk', 1),
@('update, shortcut deleted', '', '{00000000-0000-0000-0000-000000000000}', '', 0),
@('update, INSTALLDESKTOPSHORTCUT=0', '0', '{00000000-0000-0000-0000-000000000000}', 'C:\Users\Public\Desktop\PdfCraft.lnk', 0))) {
$session.Property('INSTALLDESKTOPSHORTCUT') = $case[1]
$session.Property('WIX_UPGRADE_DETECTED') = $case[2]
$session.Property('EXISTINGDESKTOPSHORTCUT') = $case[3]
Assert-Equal ($session.EvaluateCondition($set[1])) $case[4] "Desktop shortcut default: $($case[0])"
}
} finally {
if ($session) { [void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($session) }
}
$secure = Read-Row 'SELECT `Value` FROM `Property` WHERE `Property` = ''SecureCustomProperties''' 1
if (($secure[0] -split ';') -notcontains 'INSTALLDESKTOPSHORTCUT') { throw "INSTALLDESKTOPSHORTCUT is not secure: '$($secure[0])'" }
$checkbox = Read-Row 'SELECT `Type`, `Property`, `Text` FROM `Control` WHERE `Dialog_` = ''InstallWelcome'' AND `Control` = ''DesktopShortcut''' 3
@@ -179,4 +204,4 @@ $rm = Read-Row 'SELECT `Dialog` FROM `Dialog` WHERE `Dialog` = ''MsiRMFilesInUse
Assert-Equal $rm[0] 'MsiRMFilesInUse' 'Files-in-use dialog'
[void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($Database)
[void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($Installer)
Write-Output 'ok MSI: per-machine scope guard, publisher, persistent progress text, Start Menu shortcut, optional desktop shortcut (default on, checkbox), optional PDF context menu (default on, checkbox), icon/key path, full-UI success/cancel/error and Finish controls, files-in-use dialog, OCR models'
Write-Output 'ok MSI: per-machine scope guard, publisher, persistent progress text, Start Menu shortcut, optional desktop shortcut (on for a first install, kept on update only while present, checkbox), optional PDF context menu (default on, checkbox), icon/key path, full-UI success/cancel/error and Finish controls, files-in-use dialog, OCR models'