How Windows 95 Handled System Tray Notifications
When Windows 95 introduced the Taskbar, it included a dedicated notification area—commonly known as the System Tray—to allow background processes to display status indicators and receive user input without cluttering the screen with full application windows. This article provides an overview of the Win32 architecture behind this mechanism, detailing how background programs registered icons with the Windows Shell, received user interaction events through custom window messages, and managed system resources while running silently.
The Notification Area Architecture
The System Tray was integrated directly into
Explorer.exe, the process responsible for the Windows 95
desktop and Taskbar. Because 32-bit Windows 95 applications ran in their
own preemptively multitasked address spaces, a background program could
not directly draw into the Taskbar's screen area. Instead, the operating
system relied on a centralized Win32 Shell API to bridge background
workers with the shell interface.
The Shell_NotifyIcon Function
Background programs interfaced with the notification area through a
single API function: Shell_NotifyIcon.
The function accepted two parameters:
- A message command flag: Specifying the desired
action, such as
NIM_ADD(to create an icon),NIM_MODIFY(to change the icon image or tooltip), orNIM_DELETE(to remove the icon when the task terminated). - A pointer to a
NOTIFYICONDATAstructure: Containing the configuration details for the entry.
The NOTIFYICONDATA structure held critical metadata,
including:
cbSize: The size of the structure.hWnd: The handle to the window that would receive notification events.uID: An application-defined identifier for the specific icon.uFlags: Flags determining which other members contained valid data (NIF_MESSAGE,NIF_ICON,NIF_TIP).uCallbackMessage: A custom message ID defined by the application.hIcon: The handle to the 16x16 icon to render.szTip: A string buffer for the mouse-hover tooltip text (limited to 64 characters in Windows 95).
The Asynchronous Message Loop
To manage tray notifications, a background task did not need an active, visible user interface, but it did require an internal message queue and a hidden window.
When a program initialized, it created an invisible window to secure
an hWnd and entered a standard message retrieval loop
(GetMessage/DispatchMessage). When calling
Shell_NotifyIcon with NIM_ADD, the application
provided this hWnd alongside an application-defined message
constant (typically within the WM_USER or
WM_APP range).
Once the icon was added, Explorer handled the drawing and hovered tooltips. When a user interacted with the icon—such as clicking, double-clicking, or right-clicking—Explorer identified the corresponding task and forwarded a notification back to the application's hidden window using the registered callback message:
wParam: Contained theuIDof the icon that triggered the event, allowing one window to manage multiple tray icons.lParam: Contained the specific mouse message that occurred (such asWM_LBUTTONDOWN,WM_LBUTTONDBLCLK, orWM_RBUTTONUP).
Processing User Interaction
When the background task's window procedure (WndProc)
intercepted its custom uCallbackMessage, it evaluated
lParam to determine how to respond:
- Left Click or Double-Click: Applications typically
brought their main interface to the foreground, unhiding their window
and calling
SetForegroundWindow. - Right Click: Programs typically generated a context
menu by calling
CreatePopupMenu, appending menu options, and displaying it withTrackPopupMenu. This enabled actions like "Open", "Properties", or "Exit" without exposing a full interface.
By decoupling the display rendering (handled by Explorer) from the event handling (processed by the background task's own message queue), Windows 95 ensured that background services could remain responsive and maintain a minimal memory footprint while giving users direct control over background execution.