mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 01:57:12 -06:00
Issue #305: an update keeps the File Explorer menu choices, and the image menu gets a switch (#1941)
INSTALLPDFCONTEXTMENU defaulted to 1 on every install, so an upgrade without INSTALLPDFCONTEXTMENU=0 brought "Edit with PdfCraft" back. It now works like the desktop shortcut since #1570: no static default, and a SetProperty after AppSearch turns it on for a first install and, for an update, only while the menu's key is still in HKLM. A value on the command line still wins. "Create PDF with PdfCraft..." for the ten image types moves out of PdfcraftApp into its own PdfcraftImageContextMenu component, gated by a new INSTALLIMAGECONTEXTMENU that defaults and is remembered the same way. Moving the rows is safe because MajorUpgrade removes the old version before installing (afterInstallValidate). The PDF menu first shipped in 0.7.1 and the image menu in 0.4.0, so a missing key can also mean the installed release never had it. OnlyDetect Upgrade rows (PDFCONTEXTMENURELEASE, IMAGECONTEXTMENURELEASE) tell those apart, so updating from 0.6.0 still adds the PDF menu. test-msi.ps1 evaluates the SetProperty and component conditions for the desktop shortcut and both menus (first install, =0, update with and without the item, =1, and an update from a release without the menu), and checks the registry searches, the Upgrade rows, that no image row is left in PdfcraftApp, and the RemoveExistingProducts scheduling. README documents the three properties, pinning the SHA-256 because same-version MSIs replace each other silently, and the signing identities (checked against the v0.7.1 files). docs/releasing.md and the misc.installers parity note follow. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ff6cabf047
commit
fe0060de54
@@ -452,17 +452,40 @@ logs and crash recovery in a `PdfCraftData` folder next to `pdfcraft.exe`, so no
|
||||
`%APPDATA%`; delete that file to use the normal per-user folders.
|
||||
|
||||
The MSI installs for all users and requires administrator privileges. It adds "Edit with PdfCraft"
|
||||
to the right-click menu of PDF files in File Explorer (on Windows 11, under "Show more options"),
|
||||
without changing which app opens PDFs by default. For unattended deployment without a desktop
|
||||
shortcut, run from an elevated terminal:
|
||||
to the right-click menu of PDF files and "Create PDF with PdfCraft…" to the right-click menu of
|
||||
images (.png .jpg .jpeg .tif .tiff .gif .bmp .jp2 .j2k .jpx) in File Explorer (on Windows 11, under
|
||||
"Show more options"), without changing which app opens them by default. For unattended deployment
|
||||
without a desktop shortcut, run from an elevated terminal:
|
||||
|
||||
```powershell
|
||||
msiexec /i "pdfcraft-<ver>-windows-x64.msi" /qn /norestart INSTALLDESKTOPSHORTCUT=0
|
||||
```
|
||||
|
||||
Add `INSTALLPDFCONTEXTMENU=0` to leave out the right-click menu entry. Pass both on upgrades too,
|
||||
since each install applies its own choices. Use the MSI for your architecture. Per-user
|
||||
installation overrides are not supported.
|
||||
| Property | `=0` leaves out |
|
||||
|---|---|
|
||||
| `INSTALLDESKTOPSHORTCUT` | the all-users desktop shortcut |
|
||||
| `INSTALLPDFCONTEXTMENU` | "Edit with PdfCraft" on PDF files |
|
||||
| `INSTALLIMAGECONTEXTMENU` | "Create PDF with PdfCraft…" on images |
|
||||
|
||||
All three are on for a first install. An upgrade keeps what the installed version has: a shortcut
|
||||
or menu entry that is there stays, and one that is missing (left out at install, or a desktop
|
||||
shortcut someone deleted) stays missing, so you don't need to pass them again. A value on the
|
||||
command line always wins: `=1` puts one back and `=0` removes it. Upgrading from a release that
|
||||
didn't have a menu yet (0.6.0 and earlier for the PDF menu, 0.2.1 and earlier for the image menu)
|
||||
adds it, as a first install would. Use the MSI for your architecture. Per-user installation
|
||||
overrides are not supported.
|
||||
|
||||
**Pin the checksum, not just the version.** Release candidates of a version share its MSI version
|
||||
(MSI versions are numbers only, so 1.2.3-rc.1 and 1.2.3 are both 1.2.3), and the installer lets an
|
||||
MSI of the same version replace the installed one without asking. Two different `0.7.1` MSIs can't
|
||||
be told apart by version, so if your deployment tool checks the file, pin its SHA-256 from
|
||||
`SHA256SUMS.txt` (`Get-FileHash <file> -Algorithm SHA256`).
|
||||
|
||||
**Signing identities.** The Windows MSIs and both executables are signed through Azure Trusted
|
||||
Signing with a certificate issued to `Learning Machines Inc` (Atlanta, Georgia, US). The macOS app
|
||||
and command-line tool are signed with the Developer ID `Learning Machines LLC (DJ6XS33FX8)` and
|
||||
notarized. The MSI's manufacturer, shown as the publisher in Installed apps, is
|
||||
`Learning Machines LLC`.
|
||||
|
||||
### macOS
|
||||
|
||||
|
||||
+13
-5
@@ -121,10 +121,14 @@ 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. 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.
|
||||
both unless `INSTALLDESKTOPSHORTCUT=0` or `INSTALLPDFCONTEXTMENU=0` is passed;
|
||||
`INSTALLIMAGECONTEXTMENU=0` (no checkbox) leaves out the image menu. 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), and keeps each menu only if
|
||||
its key path is still in HKLM (#305). Both are AppSearch results read before the old version is
|
||||
removed. An update from a release that didn't have a menu yet (before 0.7.1 for the PDF menu,
|
||||
before 0.4.0 for the image menu, found by `OnlyDetect` Upgrade rows) adds it like a first install.
|
||||
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
|
||||
@@ -132,8 +136,12 @@ 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.
|
||||
- The image right-click entry (#190) is the same kind of verb, `PdfCraft.CreatePdf`, for ten image
|
||||
types, in its own component (`PdfcraftImageContextMenu`) since it became optional. Its rows moved
|
||||
out of `PdfcraftApp`; that's safe because `MajorUpgrade` removes the old version before installing
|
||||
the new one (`afterInstallValidate`), which `test-msi.ps1` checks.
|
||||
- `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
|
||||
evaluates the first-install and update conditions of the desktop shortcut and both menus. 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.
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
|
||||
Per-machine install into Program Files, a Start Menu shortcut, a desktop shortcut (on by default,
|
||||
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.
|
||||
and in Default apps, never forced as the default), "Edit with PdfCraft" on PDF files'
|
||||
right-click menu and "Create PDF with PdfCraft…" on image files' right-click menu (both on by
|
||||
default, optional, and an update keeps the installed version's choice). Files opened with
|
||||
PdfCraft arrive on its command line.
|
||||
-->
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||
<Package
|
||||
@@ -51,9 +52,31 @@
|
||||
</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" />
|
||||
<!-- The File Explorer menus: "Edit with PdfCraft" for PDF files (#682) and "Create PDF with
|
||||
PdfCraft…" for images (#190). Each is on by default for a first install and has a property
|
||||
to leave it out of unattended installs: INSTALLPDFCONTEXTMENU=0 (also a welcome-dialog
|
||||
checkbox) and INSTALLIMAGECONTEXTMENU=0. Secure, like the desktop shortcut's.
|
||||
An update keeps the installed version's choice (#305) by looking for the menu's registry
|
||||
key, the way the desktop shortcut looks for its file: AppSearch runs before the old version
|
||||
is removed. Releases from before a menu existed (the PDF one came in 0.7.1, the image one in
|
||||
0.4.0) never had the key, so updating from one of them counts as a first install; the
|
||||
OnlyDetect rows below tell them apart. A value given on the command line wins. -->
|
||||
<Upgrade Id="EA00C367-7270-4C50-9D8B-A7A5C88AD27F">
|
||||
<UpgradeVersion Minimum="0.7.1" IncludeMinimum="yes" OnlyDetect="yes" Property="PDFCONTEXTMENURELEASE" />
|
||||
<UpgradeVersion Minimum="0.4.0" IncludeMinimum="yes" OnlyDetect="yes" Property="IMAGECONTEXTMENURELEASE" />
|
||||
</Upgrade>
|
||||
<Property Id="INSTALLPDFCONTEXTMENU" Secure="yes" />
|
||||
<Property Id="EXISTINGPDFCONTEXTMENU">
|
||||
<RegistrySearch Id="PdfContextMenuSearch" Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit" Type="raw" />
|
||||
</Property>
|
||||
<SetProperty Id="INSTALLPDFCONTEXTMENU" Value="1" After="AppSearch" Sequence="first"
|
||||
Condition="NOT INSTALLPDFCONTEXTMENU AND (NOT PDFCONTEXTMENURELEASE OR EXISTINGPDFCONTEXTMENU)" />
|
||||
<Property Id="INSTALLIMAGECONTEXTMENU" Secure="yes" />
|
||||
<Property Id="EXISTINGIMAGECONTEXTMENU">
|
||||
<RegistrySearch Id="ImageContextMenuSearch" Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf" Type="raw" />
|
||||
</Property>
|
||||
<SetProperty Id="INSTALLIMAGECONTEXTMENU" Value="1" After="AppSearch" Sequence="first"
|
||||
Condition="NOT INSTALLIMAGECONTEXTMENU AND (NOT IMAGECONTEXTMENURELEASE OR EXISTINGIMAGECONTEXTMENU)" />
|
||||
<UIRef Id="PdfcraftInstallerUI" />
|
||||
|
||||
<StandardDirectory Id="ProgramFiles6432Folder">
|
||||
@@ -98,38 +121,6 @@
|
||||
<RegistryValue Root="HKLM" Key="Software\PdfCraft\Capabilities" Name="ApplicationIcon" Value="[#PdfcraftExe],0" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\PdfCraft\Capabilities\FileAssociations" Name=".pdf" Value="PdfCraft.Document" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\RegisteredApplications" Name="PdfCraft" Value="Software\PdfCraft\Capabilities" Type="string" />
|
||||
|
||||
<!-- Image context menu (#190): convert without changing image file defaults. -->
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpg\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpg\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpg\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpeg\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpeg\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpeg\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tif\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tif\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tif\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tiff\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tiff\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tiff\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.gif\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.gif\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.gif\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.bmp\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.bmp\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.bmp\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jp2\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jp2\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jp2\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.j2k\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.j2k\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.j2k\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpx\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpx\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpx\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
</Component>
|
||||
|
||||
<!-- Shortcuts (#143) are plain links to pdfcraft.exe, not advertised MSI shortcuts: advertised
|
||||
@@ -165,6 +156,45 @@
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit\command" Value=""[#PdfcraftExe]" "%1"" Type="string" />
|
||||
</Component>
|
||||
|
||||
<!-- Image context menu (#190): "Create PDF with PdfCraft…" opens the DPI chooser, without
|
||||
changing image file defaults. Its own component with an HKLM key path, so
|
||||
INSTALLIMAGECONTEXTMENU=0 leaves it out and uninstall removes it. These rows were in
|
||||
PdfcraftApp up to 0.7.1; moving them is safe because MajorUpgrade removes the old version
|
||||
before installing (afterInstallValidate), so no old row is left behind. -->
|
||||
<Component Id="PdfcraftImageContextMenu" Guid="1B783BF3-12DF-4588-AF80-E2CEA8BC1E5A"
|
||||
Condition="INSTALLIMAGECONTEXTMENU = 1">
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" KeyPath="yes" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpg\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpg\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpg\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpeg\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpeg\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpeg\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tif\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tif\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tif\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tiff\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tiff\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tiff\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.gif\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.gif\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.gif\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.bmp\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.bmp\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.bmp\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jp2\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jp2\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jp2\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.j2k\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.j2k\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.j2k\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpx\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpx\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpx\shell\PdfCraft.CreatePdf\command" Value=""[#PdfcraftExe]" --create-images "%1"" Type="string" />
|
||||
</Component>
|
||||
|
||||
<Component Id="PdfcraftCli">
|
||||
<File Id="PdfcraftCliExe" Source="$(var.BinDir)\pdfcraft-cli.exe" KeyPath="yes" />
|
||||
</Component>
|
||||
|
||||
+100
-35
@@ -88,33 +88,50 @@ foreach ($entry in @(@('StartMenuShortcut', 'ProgramMenuFolder', 'PdfcraftStartM
|
||||
Assert-Equal $component[0] $entry[1] "$($entry[2]) directory"
|
||||
Assert-Equal $component[1] $entry[3] "$($entry[2]) condition"
|
||||
}
|
||||
# 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])"
|
||||
# Defaults an update keeps (#1058, #305). No static default: a SetProperty after AppSearch turns it on
|
||||
# for a first install and, for an update from a release that offered it ($Release set), only while what
|
||||
# it installed is still there ($Found, from AppSearch). A command-line value is left alone. Windows
|
||||
# Installer's evaluator runs the SetProperty condition and then $Component's own condition.
|
||||
function Assert-KeptOnUpdate([string] $Property, [string] $Release, [string] $Found, [string] $FoundValue, [string] $Component, [string] $What) {
|
||||
Assert-NoRow ('SELECT `Value` FROM `Property` WHERE `Property` = ''' + $Property + '''') "$What static default"
|
||||
$action = Read-Row ('SELECT `Action`, `Target` FROM `CustomAction` WHERE `Source` = ''' + $Property + '''') 2
|
||||
Assert-Equal $action[1] '1' "$What default value"
|
||||
$condition = $null
|
||||
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` = ''' + $action[0] + '''') 2
|
||||
if ([int] $set[0] -le [int] $search[0]) { throw "$sequence must set the $What default after AppSearch" }
|
||||
$condition = $set[1]
|
||||
}
|
||||
$installed = Read-Row ('SELECT `Condition` FROM `Component` WHERE `Component` = ''' + $Component + '''') 1
|
||||
Assert-Equal $installed[0] "$Property = 1" "$Component condition"
|
||||
$old = '{00000000-0000-0000-0000-000000000000}'
|
||||
# Name, command-line value, WIX_UPGRADE_DETECTED, $Release, $Found, installed (1) or not (0).
|
||||
$cases = @(
|
||||
@('first install', '', '', '', '', 1),
|
||||
@("first install, $Property=0", '0', '', '', '', 0),
|
||||
@('update, still there', '', $old, $old, $FoundValue, 1),
|
||||
@('update, removed', '', $old, $old, '', 0),
|
||||
@("update, $Property=0", '0', $old, $old, $FoundValue, 0),
|
||||
@("update, removed, $Property=1", '1', $old, $old, '', 1))
|
||||
# An update from a release from before it existed (WIX_UPGRADE_DETECTED alone) is a first install.
|
||||
if ($Release -ne 'WIX_UPGRADE_DETECTED') { $cases += , @('update from a release without it', '', $old, '', '', 1) }
|
||||
$session = $null
|
||||
try {
|
||||
$session = $Installer.OpenPackage((Resolve-Path -LiteralPath $Path).Path, 1)
|
||||
foreach ($case in $cases) {
|
||||
$session.Property($Property) = $case[1]
|
||||
$session.Property('WIX_UPGRADE_DETECTED') = $case[2]
|
||||
$session.Property($Release) = $case[3]
|
||||
$session.Property($Found) = $case[4]
|
||||
if ($session.EvaluateCondition($condition) -eq 1) { $session.Property($Property) = $action[1] }
|
||||
Assert-Equal ($session.EvaluateCondition($installed[0])) $case[5] "$What`: $($case[0])"
|
||||
}
|
||||
} finally {
|
||||
if ($session) { [void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($session) }
|
||||
}
|
||||
} finally {
|
||||
if ($session) { [void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($session) }
|
||||
}
|
||||
Assert-KeptOnUpdate 'INSTALLDESKTOPSHORTCUT' 'WIX_UPGRADE_DETECTED' 'EXISTINGDESKTOPSHORTCUT' 'C:\Users\Public\Desktop\PdfCraft.lnk' 'PdfcraftDesktopShortcut' 'Desktop shortcut'
|
||||
$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
|
||||
@@ -128,23 +145,71 @@ Assert-Equal $app[0] 'PdfcraftExe' 'Shortcut executable key path'
|
||||
$scope = Read-Row 'SELECT `Value` FROM `Property` WHERE `Property` = ''ALLUSERS''' 1
|
||||
Assert-Equal $scope[0] '1' 'Per-machine shortcut scope'
|
||||
|
||||
# Image context menu opens the DPI chooser; the app component owns every registry row so
|
||||
# uninstall removes it. No image default association is changed.
|
||||
# Each File Explorer menu remembers whether it was installed by finding its key path in HKLM, in the
|
||||
# package's own registry view (64-bit for x64 and arm64). Only a release that offered the menu can
|
||||
# leave it out, so the OnlyDetect Upgrade rows start at the first release with that menu.
|
||||
$upgradeCode = Read-Row 'SELECT `Value` FROM `Property` WHERE `Property` = ''UpgradeCode''' 1
|
||||
$view64 = $Database.SummaryInformation(0).Property(7) -notmatch '^Intel;'
|
||||
function Assert-MenuSearch([string] $Found, [string] $Key, [string] $Release, [string] $Since, [string] $What) {
|
||||
$search = Read-Row ('SELECT `Root`, `Key`, `Name`, `Type` FROM `RegLocator`, `AppSearch` WHERE `AppSearch`.`Signature_` = `RegLocator`.`Signature_` AND `AppSearch`.`Property` = ''' + $Found + '''') 4
|
||||
Assert-Equal $search[0] '2' "$What search HKLM root"
|
||||
Assert-Equal $search[1] $Key "$What search key"
|
||||
Assert-Equal $search[2] '' "$What search reads the default value"
|
||||
# msidbLocatorTypeRawValue (2), plus msidbLocatorType64bit (16) in a 64-bit package.
|
||||
Assert-Equal ([int] $search[3]) $(if ($view64) { 18 } else { 2 }) "$What search type"
|
||||
$upgrade = Read-Row ('SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Attributes` FROM `Upgrade` WHERE `ActionProperty` = ''' + $Release + '''') 4
|
||||
Assert-Equal $upgrade[0] $upgradeCode[0] "$What release detection upgrade code"
|
||||
Assert-Equal $upgrade[1] $Since "$What release detection minimum"
|
||||
Assert-Equal $upgrade[2] '' "$What release detection maximum"
|
||||
# msidbUpgradeAttributesOnlyDetect (2) + VersionMinInclusive (256): never removes anything itself.
|
||||
Assert-Equal ([int] $upgrade[3]) 258 "$What release detection attributes"
|
||||
if (($secure[0] -split ';') -notcontains $Release) { throw "$Release is not secure: '$($secure[0])'" }
|
||||
}
|
||||
# The old version is removed before the new one is installed, so rows that moved between components
|
||||
# (the image menu left PdfcraftApp after 0.7.1) are removed with it rather than orphaned.
|
||||
$validate = Read-Row 'SELECT `Sequence` FROM `InstallExecuteSequence` WHERE `Action` = ''InstallValidate''' 1
|
||||
$remove = Read-Row 'SELECT `Sequence` FROM `InstallExecuteSequence` WHERE `Action` = ''RemoveExistingProducts''' 1
|
||||
$initialize = Read-Row 'SELECT `Sequence` FROM `InstallExecuteSequence` WHERE `Action` = ''InstallInitialize''' 1
|
||||
if ([int] $remove[0] -le [int] $validate[0] -or [int] $remove[0] -ge [int] $initialize[0]) {
|
||||
throw "RemoveExistingProducts ($($remove[0])) must run between InstallValidate and InstallInitialize"
|
||||
}
|
||||
|
||||
# Image context menu (#190) opens the DPI chooser. Its own component, gated by INSTALLIMAGECONTEXTMENU
|
||||
# (on for a first install, kept on update only while present, secure), with the .png label as key
|
||||
# path so uninstall removes every row. No image default association is changed.
|
||||
$imageKey = 'Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf'
|
||||
foreach ($ext in @('png', 'jpg', 'jpeg', 'tif', 'tiff', 'gif', 'bmp', 'jp2', 'j2k', 'jpx')) {
|
||||
$key = 'Software\Classes\SystemFileAssociations\.' + $ext + '\shell\PdfCraft.CreatePdf'
|
||||
$menu = Read-Row ('SELECT `Value`, `Component_`, `Root` FROM `Registry` WHERE `Key` = ''' + $key + ''' AND `Name` IS NULL') 3
|
||||
Assert-Equal $menu[0] 'Create PDF with PdfCraft…' "$ext context menu label"
|
||||
Assert-Equal $menu[1] 'PdfcraftApp' "$ext context menu component"
|
||||
Assert-Equal $menu[1] 'PdfcraftImageContextMenu' "$ext context menu component"
|
||||
Assert-Equal $menu[2] '2' "$ext context menu HKLM root"
|
||||
$command = Read-Row ('SELECT `Value` FROM `Registry` WHERE `Key` = ''' + $key + '\command''') 1
|
||||
$command = Read-Row ('SELECT `Value`, `Component_` FROM `Registry` WHERE `Key` = ''' + $key + '\command''') 2
|
||||
Assert-Equal $command[0] '"[#PdfcraftExe]" --create-images "%1"' "$ext context menu command"
|
||||
$selection = Read-Row ('SELECT `Value` FROM `Registry` WHERE `Key` = ''' + $key + ''' AND `Name` = ''MultiSelectModel''') 1
|
||||
Assert-Equal $command[1] 'PdfcraftImageContextMenu' "$ext context menu command component"
|
||||
$selection = Read-Row ('SELECT `Value`, `Component_` FROM `Registry` WHERE `Key` = ''' + $key + ''' AND `Name` = ''MultiSelectModel''') 2
|
||||
Assert-Equal $selection[0] 'Single' "$ext context menu selection"
|
||||
Assert-Equal $selection[1] 'PdfcraftImageContextMenu' "$ext context menu selection component"
|
||||
}
|
||||
$view = $Database.OpenView('SELECT `Key` FROM `Registry` WHERE `Component_` = ''PdfcraftApp''')
|
||||
try {
|
||||
[void] $view.Execute()
|
||||
while ($record = $view.Fetch()) {
|
||||
if ($record.StringData(1) -match 'PdfCraft\.CreatePdf') { throw "image menu row left in PdfcraftApp: $($record.StringData(1))" }
|
||||
}
|
||||
} finally { [void] $view.Close() }
|
||||
$imageKeyPath = Read-Row ('SELECT `Registry` FROM `Registry` WHERE `Key` = ''' + $imageKey + ''' AND `Name` IS NULL') 1
|
||||
$imageComponent = Read-Row 'SELECT `KeyPath`, `Attributes` FROM `Component` WHERE `Component` = ''PdfcraftImageContextMenu''' 2
|
||||
Assert-Equal $imageComponent[0] $imageKeyPath[0] 'Image context menu key path'
|
||||
# msidbComponentAttributesRegistryKeyPath (4).
|
||||
if (([int] $imageComponent[1] -band 4) -eq 0) { throw "Image context menu key path is not a registry value: attributes $($imageComponent[1])" }
|
||||
Assert-KeptOnUpdate 'INSTALLIMAGECONTEXTMENU' 'IMAGECONTEXTMENURELEASE' 'EXISTINGIMAGECONTEXTMENU' 'Create PDF with PdfCraft…' 'PdfcraftImageContextMenu' 'Image context menu'
|
||||
Assert-MenuSearch 'EXISTINGIMAGECONTEXTMENU' $imageKey 'IMAGECONTEXTMENURELEASE' '0.4.0' 'Image context menu'
|
||||
if (($secure[0] -split ';') -notcontains 'INSTALLIMAGECONTEXTMENU') { throw "INSTALLIMAGECONTEXTMENU is not secure: '$($secure[0])'" }
|
||||
|
||||
# PDF context menu (#682): "Edit with PdfCraft" opens the file in PdfCraft. Its own component, gated
|
||||
# by INSTALLPDFCONTEXTMENU (default 1, secure, welcome checkbox), with its menu value as key path so
|
||||
# uninstall removes it. Several selected PDFs each start pdfcraft.exe "%1" (Document), which the
|
||||
# by INSTALLPDFCONTEXTMENU (on for a first install, kept on update only while present, secure, welcome
|
||||
# checkbox), with its menu value as key path so uninstall removes it. Several selected PDFs each start pdfcraft.exe "%1" (Document), which the
|
||||
# single-instance hand-off opens as tabs. The .pdf default is never set, so the user's default app
|
||||
# stays.
|
||||
$pdfKey = 'Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit'
|
||||
@@ -163,8 +228,8 @@ Assert-Equal $pdfCommand[1] 'PdfcraftPdfContextMenu' 'PDF context menu command c
|
||||
$pdfComponent = Read-Row 'SELECT `Condition`, `KeyPath` FROM `Component` WHERE `Component` = ''PdfcraftPdfContextMenu''' 2
|
||||
Assert-Equal $pdfComponent[0] 'INSTALLPDFCONTEXTMENU = 1' 'PDF context menu condition'
|
||||
Assert-Equal $pdfComponent[1] $pdfMenu[0] 'PDF context menu key path'
|
||||
$pdfDefault = Read-Row 'SELECT `Value` FROM `Property` WHERE `Property` = ''INSTALLPDFCONTEXTMENU''' 1
|
||||
Assert-Equal $pdfDefault[0] '1' 'PDF context menu default'
|
||||
Assert-KeptOnUpdate 'INSTALLPDFCONTEXTMENU' 'PDFCONTEXTMENURELEASE' 'EXISTINGPDFCONTEXTMENU' 'Edit with PdfCraft' 'PdfcraftPdfContextMenu' 'PDF context menu'
|
||||
Assert-MenuSearch 'EXISTINGPDFCONTEXTMENU' $pdfKey 'PDFCONTEXTMENURELEASE' '0.7.1' 'PDF context menu'
|
||||
if (($secure[0] -split ';') -notcontains 'INSTALLPDFCONTEXTMENU') { throw "INSTALLPDFCONTEXTMENU is not secure: '$($secure[0])'" }
|
||||
$pdfCheckbox = Read-Row 'SELECT `Type`, `Property`, `Text` FROM `Control` WHERE `Dialog_` = ''InstallWelcome'' AND `Control` = ''PdfContextMenu''' 3
|
||||
Assert-Equal $pdfCheckbox[0] 'CheckBox' 'Welcome PDF context-menu control'
|
||||
@@ -204,4 +269,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 (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'
|
||||
Write-Output 'ok MSI: per-machine scope guard, publisher, persistent progress text, Start Menu shortcut, optional desktop shortcut and PDF and image context menus (on for a first install, kept on update only while present; checkboxes for the first two), icon/key path, full-UI success/cancel/error and Finish controls, files-in-use dialog, OCR models'
|
||||
|
||||
@@ -10085,7 +10085,7 @@ status = "planned"
|
||||
commands = []
|
||||
tools = []
|
||||
evidence = []
|
||||
notes = "Windows MSI creates a Start menu shortcut, an optional (default on) desktop shortcut and an optional (default on, INSTALLPDFCONTEXTMENU) \"Edit with PdfCraft\" File Explorer menu entry for PDF files (#682), and confirms full-UI completion with Finish (#143); packaging/windows/test-msi.ps1 checks the compiled MSI and windows-arm64.yml checks installation/removal. PDF files use a dedicated embedded document icon when PdfCraft is chosen as the default reader (confirmed in Windows Explorer); first-page thumbnails remain unsupported. Signed installers across platforms remain planned."
|
||||
notes = "Windows MSI creates a Start menu shortcut, an optional (default on) desktop shortcut, an optional (default on, INSTALLPDFCONTEXTMENU) \"Edit with PdfCraft\" File Explorer menu entry for PDF files (#682) and an optional (default on, INSTALLIMAGECONTEXTMENU) \"Create PDF with PdfCraft…\" entry for images; an update keeps whichever of the three the installed version has (#1058, #305). It confirms full-UI completion with Finish (#143); packaging/windows/test-msi.ps1 checks the compiled MSI and windows-arm64.yml checks installation/removal. PDF files use a dedicated embedded document icon when PdfCraft is chosen as the default reader (confirmed in Windows Explorer); first-page thumbnails remain unsupported. Signed installers across platforms remain planned."
|
||||
|
||||
[[feature]]
|
||||
id = "misc.web-deploy"
|
||||
|
||||
Reference in New Issue
Block a user