Repository navigation
How Tray Visibility Works
Windows decides whether a notification-area icon sits on the taskbar or inside the overflow
flyout by reading one registry value per icon. This page documents that store, why naive
tweaks decay, which legacy switches still matter, which registry areas are dangerous, and
how TrayIconGuard v8 reconciles everything. Every statement is traced to the studied source
tree (124e849) or to the repository's own mechanics canon, docs/HOW-IT-WORKS.md, which
was introduced with the v7 rewrite and carried into v8.
- The store Windows actually uses
- The decay problem
- Applying changes live
- The legacy switch: EnableAutoTray
- System icon policies: HideSCA values
- Pre-1703 icon cache: TrayNotify
- Danger zone: StuckRects3
- What a reconcile pass does
- See also
Since Windows 10 1703 (build 15063), per-icon notification-area visibility lives in:
HKEY_CURRENT_USER\Control Panel\NotifyIconSettings\{<icon-id>}
Each tray registration gets one subkey — usually a GUID, occasionally a shell name.
TrayIconGuard reads and writes exactly this location (src/core/trayicons.cpp:14):
| Value | Type | Meaning |
|---|---|---|
IsPromoted |
REG_DWORD |
1 = icon on the taskbar, 0 = icon in the overflow flyout. The Windows Settings per-icon toggle reads and writes this same value. |
ExecutablePath |
REG_SZ |
Owning executable, e.g. C:\Program Files\App\app.exe. |
IconResource |
REG_SZ |
Icon resource, e.g. C:\…\app.exe,-101. |
IconSnapshot |
REG_BINARY |
Last rendered bitmap of the icon. |
LastNotificationAddedTime |
REG_QWORD |
FILETIME (UTC) of the last notification. |
The app derives the friendly name it displays from the executable's FileDescription
version-resource field, falling back to the file name (src/core/trayicons.cpp:40-68), and
expands environment variables in ExecutablePath before doing so.
Note
There is no documented public API for this store: it is observed shell state. That is why the repository documents it explicitly and why the watcher reconciles rather than assumes.
Explorer (re)creates these subkeys every time an application registers its tray icon —
at application start, after updates, after crashes, at logon. On (re)registration Windows
writes IsPromoted = 0 unless it considers the icon recently interactive. Consequences:
- A one-shot registry tweak (or a
.regfile, or a script run once) stops working within days: newly updated applications reappear hidden. - User reports match: applications whose updaters restart frequently are the classic offenders.
- The durable fix is a watcher: observe the subtree, debounce burst writes, re-promote.
sequenceDiagram
participant App as Application
participant Ex as Explorer
participant Reg as NotifyIconSettings
participant G as TrayIconGuard watcher
App->>Ex: Shell_NotifyIcon (register)
Ex->>Reg: recreate subkey, IsPromoted=0
Reg-->>G: registry change notification
G->>G: debounce (default 400 ms)
G->>Reg: IsPromoted=1 (unless excluded/user-hidden)
Ex->>Ex: tray refresh shows the icon
Explorer normally picks up external IsPromoted writes without a restart — icons move on
the next tray refresh. When it does not (observed on some Windows 11 24H2+ builds, after
fast user switching, or when Explorer cached the layout), a shell restart applies
everything immediately. TrayIconGuard offers a guarded restart
(src/core/explorer.cpp:30-69): terminate every explorer.exe, wait up to 8 s for the
shell's own auto-restart, start explorer.exe manually if it did not come back, then wait
up to 4 s for the shell window. The optional auto-restart in the guard adds a cooldown
(default 60 s, floor 10 s) and only fires after two consecutive passes that had to fix
something, to avoid restart flapping (src/core/guard.cpp).
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer → EnableAutoTray (DWORD)
0 means “show all icons” for the Windows XP–7 era Hide inactive icons checkbox. Modern
Windows largely ignores it, but writing 0 is harmless and still helps a few legacy shell
components. TrayIconGuard therefore keeps it strictly as a belt-and-suspenders measure
(settings key guard.legacyBelt, default on; src/core/legacy.cpp:22-27), never as the
primary mechanism. Removing the value hands control back to Windows
(--restore-defaults does exactly that).
The volume, network and power system icons can be force-hidden by policy values
HideSCAVolume, HideSCANetwork, HideSCAPower equal to 1. Their correct home is:
HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer
Pre-7.0 artifacts of this repository wrote them one path segment higher (without
Policies), where they have no effect at all; the project's own audit notice documents
this (see Legacy Script Editions). TrayIconGuard counts stale =1 values for the
Dashboard and removes them on demand or during --promote-all
(src/core/legacy.cpp:34-50).
HKCU\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify
→ IconStreams, PastIconsStream (binary caches)
Older Windows 10 builds cached per-icon preferences here. Deleting the two values forces a
rebuild and clears phantom or stale icons; a shell restart afterwards is recommended. On
Windows 11 the key is frequently absent. TrayIconGuard exposes this only as an explicit
repair (Guard page → Reset legacy tray cache, src/core/legacy.cpp:57-64), never
automatically.
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects3\Settings
This binary blob stores resolution- and monitor-specific taskbar layout state. Overwriting
it with a canned value — which the v6-era .reg file did — corrupts the taskbar layout on
other displays. TrayIconGuard v8 never touches it; the repository's pitfall list states the
rule explicitly. If a guide tells you to paste a StuckRects3 hex blob, close the guide.
One pass of the watcher (src/core/guard.cpp, RunPass) works as follows:
- Rescan the store subtree into a snapshot (subkeys → entries with
IsPromoted, executable, friendly name, exclusion match). - For each entry compute the desired state from the current visibility mode:
- full — every icon promoted;
- compact — only TrayIconGuard's own icon promoted;
- custom (default) — promote unless the executable matches an exclusion wildcard or the icon id is in the user-hidden list.
- Write
IsPromotedonly where the current value differs; count changes; log an event line and emit a UI/tray notification (N|name1, name2, truncated at 160 characters). - If anything changed and auto-restart is enabled with the cooldown elapsed and two consecutive fixing passes, restart Explorer.
- Re-arm the registry notification (the wait is one-shot per signal).
Warning
In the published 8.3.0/8.4.0 source the pass is reached only through the poll timer or a
stop signal: the armed registry event is never included in the wait set, so with the
default pollMs: 0 the loop blocks forever after arming. The defect, its fix and a
verification recipe are documented in The Guard Watcher; the pass logic itself is
exactly as described above and is what the patched build executes on every notification.
- The Guard Watcher — timing knobs, modes, statistics, defect and fix
- Legacy Script Editions — the wrong paths of the v6 era, with the audit that froze them
- Configuration Reference — every key behind the behaviors above
- Troubleshooting — “store not supported”, decay symptoms, Explorer restarts
TrayIconGuard · feed 8.6.3 / source tree 8.6.3 (see Changelog and Versioning) · PolyForm Noncommercial 1.0.0 · Repository · Report an issue · Home · Glossary · FAQ · Troubleshooting
Every claim in this wiki is traced to a source line at commit 124e849 or reproduced by
executing the program (build + Wine battery, 2026-10-16); measured discrepancies between
documentation, source and shipped binaries are documented, not smoothed over.
Home Getting Started Features Overview Comparison with Alternatives
Installation and Deployment Configuration Reference Command-Line Interface
How Tray Visibility Works Legacy Script Editions The Guard Watcher
GUI Pages Guide Backups and Snapshots Localization and Themes Self-Updater Guide Security and Privacy Troubleshooting
Architecture Roadmap and Ideas Independent-Audits Testing and QA Building from Source Changelog and Versioning FAQ Glossary
8.6.3 feed / 8.6.3 source · PolyForm Noncommercial 1.0.0