Introduction
The code provided with this article should be useful to manage the visualization of an icon on the Windows task bar. The code encapsulates the stuff required for handling mouse events, offering to the user a clean and simple interface.
We will use C++ without MFC and STL. We directly use SDK APIs.
Background
In order to show an icon on the Windows taskbar, we can use the Windows API Shell_NotifyIcon
. This API allows the programmer to add, delete and modify a certain icon in a straightforward way. A little bit difficult stuff occurs when one likes to manage mouse events on the icon and show a popup menu.
The code we propose in this article allows the user to show and modify an icon on the Windows taskbar, manage mouse events and control a popup menu using a simple interface, without taking care of threading and windowing. The code is encapsulated in a DLL so it should be simple to use in all "C++ like contexts" such as ATL, Console, MFC and Windows application projects.
We've tested the code (developed using Visual Studio 6.0) in Windows ME, 2000 and XP operating systems.
Managing mouse events
For managing mouse events, we'll use a background window. The thread associated with this window (TaskBarThread
) will receive the messages sent by the operating system when the user works on the icon (e.g. left click, right click, ...). Then, the message loop of this thread brings the events to the specified user defined window procedure (TaskBarWinProc
). The following picture shows the flow of a message generated by the user when clicking on the icon.
Figure 1
Managing popup
For managing popup, we'll use a background window as well (associated to the thread PopupThread
and window procedure PopupWinProc
). This window will show the popup on receiving a right click event sent by the thread managing mouse events. The following picture shows the flow of a message generated by the user when right-clicking on the icon and selecting a popup item.
Figure 2
Using the code
Following the "download source" link above, you can get the source code of the DLL (folder taskbaricon_dll) and the source code of a simple console application using the DLL (folder taskbaricon_console). Following the "download demo project", you can get precompiled files ready to use (folder taskbaricon_demo).
The console application shows an icon on the taskbar and reacts to mouse and popup selection events showing a message box describing the event. Further, it allows the user to change icon appearance (i.e. icon frame) pressing a key.
The dynamic link library (DLL) exports two interfaces. The former interface, ISimpleUnknown
, is provided to manage a reference counter. This interface is similar to COM interface IUnknown
, except for the lack of the method QueryInterface
. It is useful for delegating memory allocation & de-allocation to the library instead of the application. Note that, since we use some threads, it is the library that knows exactly when the memory is no longer needed. This interface is just a way to manage memory allocation/de-allocation.
class ISimpleUnknown
{
public:
virtual ULONG AddRef()=0;
virtual ULONG Release()=0;
virtual ULONG GetRef()=0;
};
The latter interface, ITaskBarIcon
, is the "core" interface. It keeps the code for showing an icon on the taskbar, showing a popup and for managing events.
Note: for getting a pointer to this interface, we have to call the static method Create
(instead of the usual new
). This method automatically sets the reference pointer to 1. For releasing the pointer, we have to use the method Release
(instead of the usual delete
).
The function pointer type LP_HANDLER_FUNC
is used for setting up a callback (listener) for managing mouse events. This function will be called by the DLL when a mouse event occurred on the icon. The parameters the DLL will pass to the application are: icon name (char*
), mouse event X coordinate (int
) and mouse event Y coordinate (int
).
The function pointer type LP_POPUP_HANDLER_FUNC
is used to set up a callback (listener) for managing popup events. The DLL will pass back to the application an identifier (int
) that indicates the popup item selected by the user.
Note: we think about a "taskbar icon" as composed by several "icon frames". Only one "icon frame" is showed for each moment. An "icon frame" is implemented as a common Windows icon object (i.e. HICON
). This allows us to create a dynamic icon. Each "icon frame" is represented with a string set by the application using the parameter p_IconName
of the method SetIconFrame
. This string will be passed back by the DLL to the application when a mouse event occurs.
typedef void (__stdcall *LP_HANDLER_FUNC)(char*, int, int);
typedef void (__stdcall *LP_POPUP_HANDLER_FUNC)(int);
class ITaskBarIcon : virtual public ISimpleUnknown
{
public:
virtual void SetIconFrame(
char* p_IconName,
HICON p_IconHandle,
UINT p_IconUID,
char* p_IconTooltipText)=0;
virtual void ChangeIconFrame(char* p_IconName)=0;
virtual void Start(char* p_IconName)=0;
virtual void Stop()=0;
virtual void SetLeftButtonClickHandler(LP_HANDLER_FUNC p_Handler)=0;
virtual void SetRightButtonClickHandler(LP_HANDLER_FUNC p_Handler)=0;
virtual void SetLeftButtonDoubleClickHandler(LP_HANDLER_FUNC p_Handler)=0;
virtual void SetPopupMenuItem(
char* p_MenuItemString,
int p_MenuItemIdentifier)=0;
virtual void SetPopupHandler(LP_POPUP_HANDLER_FUNC p_Handler)=0;
static ITaskBarIcon* Create();
};
Note: For compatibility reasons, in our interfaces, we don't use STL nor MFC. Instead, we'll use simple strings (char*
) and simple "C style" arrays. The following "define
s" fix the max dimensions allowed for strings and arrays.
#define MAX_ICON_NUM 100
#define MAX_ICON_NAME_STRLEN 50
#define MAX_ICON_TOOLTIP_TEST_STRLEN 255
#define MAX_POPUP_ITEM 50
#define MAX_POPUP_ITEM_STRLEN 50
Let's see how to use the interfaces exported by the DLL. The following notes describe the console application supplied with this article.
First of all, it is necessary to include the taskbaricon.h header file.
After that, we can define the callbacks for mouse events and popup selection. When an event occurs on the icon (e.g. mouse left click, mouse right click, ...), or on the popup menu (e.g. the user shows the popup by right clicking and selects an item), the DLL will take care of calling the proper callback. Later, we will show how to register these callbacks.
Let's see the callbacks for mouse events. Note that the first parameter of each callback represents a certain icon frame. The parameters p_x
and p_y
are the mouse coordinates in which the event has taken place. For instance, they could be useful for showing a dialog box.
void __stdcall LeftButtonClickHandler(
char* p_IconNameCurrentlyShowed,
int p_x,
int p_y
)
{
char l_Str[MAX_PATH];
sprintf(
l_Str,
"(CurrentIconFrameName: %s) LeftButtonClickHandler
mouse coordinates <x,y>==<%u,%u>",
p_IconNameCurrentlyShowed
p_x,
p_y);
::MessageBox(
NULL,
l_Str,
"Application Event Handler",
MB_OK);
}
void __stdcall RightButtonClickHandler(
char* p_IconNameCurrentlyShowed,
int p_x,
int p_y
)
{
char l_Str[MAX_PATH];
sprintf(
l_Str,
"(CurrentIconFrameName: %s) RightButtonClickHandler
mouse coordinates <x,y>==<%u,%u>",
p_IconNameCurrentlyShowed
p_x,
p_y);
::MessageBox(
NULL,
l_Str,
"Application Event Handler",
MB_OK);
}
void __stdcall LeftButtonDoubleClickHandler(
char* p_IconNameCurrentlyShowed,
int p_x,
int p_y
)
{
char l_Str[MAX_PATH];
sprintf(
l_Str,
"(CurrentIconFrameName: %s) LeftButtonDoubleClickHandler
mouse coordinates <x,y>==<%u,%u>",
p_IconNameCurrentlyShowed
p_x,
p_y);
::MessageBox(
NULL,
l_Str,
"Application Event Handler",
MB_OK);
}
After that, we can define a callback for popup menu. The parameter p_MenuItemIdentifier
identifies the user choice. The possible values for this parameter are defined by the programmer and passed to the DLL using the API SetPopupMenuItem
described later.
void __stdcall PopupUserActionHandler(int p_MenuItemIdentifier)
{
char l_Text1[]="Option1";
char l_Text2[]="Option2";
char l_Text3[]="Option3";
char l_Str[MAX_PATH];
char l_UserChoice[MAX_PATH];
if (p_MenuItemIdentifier==1)
{
strcpy(l_UserChoice,l_Text1);
}
else
if (p_MenuItemIdentifier==2)
{
strcpy(l_UserChoice,l_Text2);
}
else
if (p_MenuItemIdentifier==3)
{
strcpy(l_UserChoice,l_Text3);
}
else
{
strcpy(l_UserChoice,"Unknown");
}
sprintf(l_Str,"User selection: %s", l_UserChoice);
MessageBox(NULL,l_Str,"Popup menu user choice",MB_OK);
}
Then it is necessary to create a "task bar icon" object. We'll use the methods Create
and Release
instead of new
and delete
to improve code separation.
Note that the reference counter is automatically set to 1.
ITaskBarIcon* l_TaskBarIcon = ITaskBarIcon::Create();
Then we need to create a set of icons. Each of these icons represent one possible "icon frame". In this example, we define a taskbar icon with 4 frames. Each of this frame represents a possible status for the application. These frames are identified with the strings "Disactivated", "Activated", "Idle", "Working".
HINSTANCE hInstance = (HINSTANCE) GetModuleHandle (NULL);
HICON l_Icon1 = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ICON1));
HICON l_Icon2 = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ICON2));
HICON l_Icon3 = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ICON3));
HICON l_Icon4 = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ICON4));
Then we need to add the previously created icons to our task bar icon object. Each of this icon will be one frame of the task bar icon object. For each moment, a single named frame will be showed.
l_TaskBarIcon->SetIconFrame(
"Disactivated",
l_Icon1,
IDI_ICON1,
"Process Disactivated");
l_TaskBarIcon->SetIconFrame(
"Activated",
l_Icon2,
IDI_ICON2,
"Process Activated");
l_TaskBarIcon->SetIconFrame(
"Idle",
l_Icon3,
IDI_ICON3,
"Process Idle");
l_TaskBarIcon->SetIconFrame(
"Working",
l_Icon4,
IDI_ICON4,
"Process Working");
After that, we can set up a popup menu. This will be shown when the user right clicks on the icon. For doing that, we need to set the items belonging to the popup using the method SetPopupMenuItem
. The first parameter is the string that will be shown in the item. The second parameter is the identifier of the item that will be returned in the popup callback whether the user selects this item.
l_TaskBarIcon->SetPopupMenuItem(
"Option1",
1);
l_TaskBarIcon->SetPopupMenuItem(
"Option2",
2);
l_TaskBarIcon->SetPopupMenuItem(
"Option3",
3);
Then we can register the various event handlers defined above.
l_TaskBarIcon->SetLeftButtonClickHandler(LeftButtonClickHandler);
l_TaskBarIcon->SetRightButtonClickHandler(RightButtonClickHandler);
l_TaskBarIcon->SetLeftButtonDoubleClickHandler(LeftButtonDoubleClickHandler);
l_TaskBarIcon->SetPopupHandler(PopupUserActionHandler);
Then we need to call the following method. This is necessary to start windowing management and to show the initial frame (i.e. icon named "Disactivated") of our icon on the taskbar.
l_TaskBarIcon->Start("Disactivated");
In order to change the current icon frame shown, we can use the method ChangeIcon
.
l_TaskBarIcon->ChangeIconFrame("Idle");
l_TaskBarIcon->ChangeIconFrame("Activated");
l_TaskBarIcon->ChangeIconFrame("Working");
l_TaskBarIcon->ChangeIconFrame("Idle");
l_TaskBarIcon->ChangeIconFrame("Disactivated");
In order to quit the task bar icon and the popup threads, we need to call the following method:
l_TaskBarIcon->Stop();
At last, we need to deallocate our task bar icon object. Note that we have to call the method Release
instead of using the delete
instruction directly. The DLL will call delete
internally, when all threads will be ended.
l_TaskBarIcon->Release();
Limitations
- It is possible to set a callback for only three mouse events (e.g. left click, right click and double left click).
- It is necessary to improve error management. A lot of error situations, although they are envisioned, are not managed.
- It is not possible to change the icon tooltip in a dynamic way.
- ...
History
- 16.04.2004
- 20.04.2004
- Minor syntax error corrections in the article;
- "Figure 1" and "Figure 2" updated;
- Minor code tuning.