Skip to content

Fix pinned Store apps: duplicates in Recent and search, blank icon after an update - #2575

Open
emilioheredia-source wants to merge 5 commits into
Open-Shell:masterfrom
emilioheredia-source:fix-store-app-shortcut
Open

emilioheredia-source wants to merge 5 commits into
Open-Shell:masterfrom
emilioheredia-source:fix-store-app-shortcut

Conversation

@emilioheredia-source

Copy link
Copy Markdown

Pinning a Store app puts a shortcut to it in the Start Menu folder. With that shortcut, the app
showed up twice in Recent Programs (with a blank icon) and twice in search, and its icon became a
blank page after the app updated itself. Five commits, each with the details in its message:

  1. Resolve Apps folder items that stand for a shortcut to a Store app. With a shortcut to the
    app in the Start Menu, the Apps folder item for the appid is the shortcut's stand-in:
    PKEY_MetroAppLauncher=0, no package, the shortcut in System.AppUserModel.BestShortcut.
    RefreshItemInfo took it for a desktop program and dropped the appid, so the pinned-vs-recent
    duplicate check (which compares appids) missed it. Now such an item is resolved through its
    shortcut to the app, the same way a link to a Store app already is.
  2. Find a Start Menu shortcut to a Store app as a duplicate in search. Names from the folders
    are stored uppercase, but the Windows 10+ apps from GetMetroLinks kept their display name, so
    "MICROSOFT TO DO" never matched "Microsoft To Do". They are stored uppercase too. The name is
    only used for matching, so nothing visible changes except that the duplicate is gone.
  3. Take a Store app's package from its appid, not from a shortcut's saved copy. The shortcut
    keeps the full package name from when it was made, which includes the version. After an update
    InitializeForPackage fails with 0x80070490. GetPackageFullName now asks for the package of
    the appid's family installed now (GetPackagesByPackageFamily, main package only), and
    LoadMetroIcon and the package path use it. The package name is part of the icon cache key, so
    a new version's icon replaces the old one.
  4. Take the logo name from the installed manifest when the item has none. An item refreshed
    from the data cache gets its icon through the Apps folder, which gives the stand-in with no logo
    name. When the item has no logo name, or names another version, the name is read from the
    installed package's AppxManifest.xml (Square44x44Logo, or SmallLogo in Windows 8
    manifests).
  5. Split the package helpers into small functions. No change in behaviour; checked by compiling
    them on their own and comparing their results for four installed apps and three edge cases.

The package functions are loaded with GetProcAddress, since the DLL still loads on Windows 7.

Tested on Windows 11 26H2 (26300), with Microsoft Store (updated from 22608.1401.4.0 to
1401.5.0 while pinned), Microsoft To Do, Terminal, Teams and Outlook pinned:

  • This branch on its own (master plus these five commits): the Microsoft Store icons showed
    correctly, and searching "to do" listed Microsoft To Do once.
  • The Recent Programs duplicate was checked on my daily build, which contains these commits
    along with my other changes, not on this branch alone.
  • With temporary logging, all 11 Store app icon loads in a session came from the installed
    package, none from the fallback. Reading the Store's manifest took about 30 ms, once per session.
    (Measured on my daily build too.)
  • These tests were done on the branch's earlier base. Rebasing it onto current master (9dfa7fa)
    left all five commits unchanged, and since then I've used it every day for two weeks in my
    build on that master.

Not changed: CanUninstallMetroApp reads the package path through the Apps folder in the same
way, so Uninstall may be missing for a pinned Store app. I haven't seen that on screen yet.

With a shortcut to a Store app in the Start Menu (for example a program
pinned to the Classic menu, which lives in the Start Menu root), the Apps
folder lists that shortcut in place of the app. Creating the app's item
from its AppUserModelID then gives an item with the shortcut's properties:
PKEY_MetroAppLauncher=0, no package, and the shortcut named in
System.AppUserModel.BestShortcut. An app without a shortcut reports
PKEY_MetroAppLauncher=2 and its package, as expected.

RefreshItemInfo took such an item for a desktop program: not a link, not
a Metro app, so it dropped the appid. In the Recent Programs list this
showed as:
- a blank page instead of the app's icon, because the icon came from the
  shortcut, which has no icon of its own;
- the app listed in Recent although it is pinned, because for apps the
  duplicate check compares appids and this one was empty.

Seen with Microsoft To Do and Microsoft Store pinned; Quick Assist, not
pinned, was fine.

Now such an item is resolved through its shortcut to the app, the same
way RefreshItemInfo already resolves a link to a Store app: the item and
property store become the shortcut's target, if that target has the same
appid and is a Metro app. The appid, package and Metro icon then come out
right, and the existing duplicate check works.
Searching for a Store app that has a shortcut in the Start Menu (for
example one pinned to the Classic menu) listed it twice under Programs:
once from the Start Menu folder and once from the Apps folder.

GetSearchResults drops a program as a duplicate when its name and appid
match one already found, comparing names with wcscmp. Names from the
folders are stored uppercase (CollectSearchItems), and so are Metro link
names (AddSearchItem), but on Windows 10 and later the apps from
GetMetroLinks were added with their display name as it is. So "MICROSOFT
TO DO" from the shortcut never matched "Microsoft To Do" from the Apps
folder, although both have the same appid (checked with the search log).

Store these names uppercase too. The name is only used for matching,
which ignores case, and for finding duplicates; the results show the
item's own display name, so nothing visible changes except that the
duplicate is gone.
…d copy

A shortcut to a Store app stores a snapshot of the app's item: the full
package name, which includes the version, the install folder and the logo
name. Open-Shell resolves such a shortcut to that item and read the package
from it. After the app updates itself, that package no longer exists, so:
- LoadMetroIcon could not open the package's resources, and the item showed
  a blank tile. Seen with Microsoft Store pinned to the Classic menu the
  morning after it updated from 22608.1401.4.0 to 1401.5.0; the shortcut
  still named 1401.4.0.
- the item's package path pointed to a folder that is gone. The Recent
  Programs list drops an app whose package path does not exist.

GetPackageFullName had the same weakness from the other side: it looked the
appid up in the Apps folder, and with a shortcut to the app in the Start
Menu that item is the shortcut, which has no package at all.

The appid (package family name!app name) does not change with updates. Now
GetPackageFullName asks Windows for the package of that family installed now
(GetPackagesByPackageFamily), and takes the main package, since a family can
also list a bundle and resource packages. The old lookup stays as the
fallback. LoadMetroIcon and the package path use it, and the saved values
only when that finds nothing. The package name is part of the icon cache key
now, so a new version of an app shows its new icon.

The package functions are loaded with GetProcAddress, as the DLL still
loads on Windows 7.

CanUninstallMetroApp reads the package path through the Apps folder in the
same way; not changed here.
…m has none

The previous commit made LoadMetroIcon open the package installed now, but
the pinned Microsoft Store still showed a blank page. A diagnostic build
logged why: the item came from the data cache already marked as a Metro
app, so only its icon was refreshed, and for that RefreshItemInfo looks the
appid up in the Apps folder. With a shortcut to the app in the Start Menu,
that item is the Apps folder's stand-in for the shortcut, which has no
package and no logo name (PKEY_MetroIcon). LoadMetroIcon gave up, and the
fallback shell icon of the shortcut is the blank page the taskbar shows too.
To Do, Terminal and Teams hid the same failure, because the shell icon of
their shortcuts is still good.

Now the logo name comes from the item only when the item names the package
installed now. For a stand-in, or a snapshot of another version, it comes
from AppxManifest.xml of the installed package (Square44x44Logo, or
SmallLogo in Windows 8 manifests), read with the Appx manifest reader. That
is also right when an update renames the logo.

Checked on the real machine with the diagnostic build: all 5 pinned Store
apps (Store, To Do, Terminal, Teams, Outlook) loaded their icon from the
package, none left to the fallback. Reading the Store's manifest took about
30 ms, once per session.
GetInstalledPackageFullName and GetAppLogoName each did several things in
30-40 lines: splitting the appid, asking Windows for the family's
packages, picking the main one; opening the manifest, finding the app,
reading its logo. Each step is now its own function (SplitAppId,
IsMainPackage, ReadManifestApplications, GetManifestLogo), and the appid
is split in one place instead of two. The comments say what each returns
when it finds nothing.

No change in behaviour: the functions, compiled on their own from this
file, return the same packages, folders and logo names as before for
Microsoft Store, To Do, Terminal and Teams, and nothing for an unknown
package, a desktop program, or an unknown app in a known package.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant