Also, in comments below I've posted the console app that enumerates icons info at any time, without any hook. But there is a problem in it. Its because the Windows saves icon handle in ImageList control. And we cannot (at least, I don't know how to do it now) call ImageList_xxx() functions with HIMAGELIST
obtained from an another app.

In any case, I think I've earned grade A. I've lost on this about half of the day.

Thanks, that did give me an idea for a starting point. I've decided to try to tackle the problem from a different angle. I was able to force a DLL that I wrote into explorer's address space, and subclass the system tray. I can now see any WM_COPYDATA messages that are sent via Shell_NotifyIcon(). The problem is, when I go to examine the data as a NOTIFYICONDATA structure, all I can get is garbage that holds the same value every time - even in different Windows sessions. I disassembled Shell_NotifyIcon(), and it looks like it fudges with the NOTIFYICONDATA structure before sending the WM_COPYDATA message. Unfortunately, I am not fluent enough in assembly to be able to figure out what Shell_NotifyIcon() is doing. Does anybody know what is going on?

Enroll today in this bundle of courses to gain experience in the logistics of pen testing, Linux fundamentals, vulnerability assessments, detecting live systems, and more! This series, valued at $3,000, is free for Premium members, Team Accounts, and Qualified Experts.

eppsman, the cost of you Q is very interesting, so may be I can help you.
At first, what exactly do you want:
1) Hook to the explorer at boot time and intercept icon calls to save icon info. It seems that this is more easy than 2)
or,
2) Run your program at any time to enumerate icon (this may be impossible at all, at least, without 1)

Next, I'm fluent in asm, so you can post to me asm code along with information you discovered (WM_COPYDATA, and the source to sublassing tray window). My email is nick@rtzi.ru.
It can take about 2-3 day to see what can be done.

Here is the full answer to your Q (w/ icon handles). It works for NT and 95 and more simple.

//***************************************************************
// sh.cpp - Enumerate system tray icons - main module (console app).
// Run it in window, not in the full-screen mode.
//
// Creates the console, attached to the Explorer.
// The icons info will be printed on this console.
// Icons itself will be painted on the console window caption
// at 200-pixels offset from the left.
//
// Console automatically destroys after 10 sec.
//
// Link with shdll.dll.
//
//***************************************************************
#define STRICT

Here is the full answer to your Q. It works for NT and 95 and more simple.

//***************************************************************
// sh.cpp - Enumerate system tray icons - main module (console app).
// Run it in window, not in the full-screen mode.
//
// Creates the console, attached to the Explorer.
// The icons info will be printed on this console.
// Icons itself will be painted on the console window caption
// at 200-pixels offset from the left.
//
// Console automatically destroys after 10 sec.
//
// Link with shdll.dll.
//
//***************************************************************
#define STRICT

Here is the full answer to your Q. It works for NT and 95 and more simple.

//***************************************************************
// sh.cpp - Enumerate system tray icons - main module (console app).
// Run it in window, not in the full-screen mode.
//
// Creates the console, attached to the Explorer.
// The icons info will be printed on this console.
// Icons itself will be painted on the console window caption
// at 200-pixels offset from the left.
//
// Console automatically destroys after 10 sec.
//
// Link with shdll.dll.
//
//***************************************************************
#define STRICT