Windows CE

Saturday, April 11, 2009

Adding System Tray Icon in Wince

Adding Tray Icon in status area, Using Visual C++ (MFC), is not well documenetd in MSDN.
After many attempts, i have been able to add an ICON to the status area.
Lets see the steps required for this in a MFC Visual C++ Application.

Shell_NotifyIcon -- This function sends a message to the system to add, modify, or delete an icon from the taskbar status area.

WINSHELLAPI BOOL WINAPI Shell_NotifyIcon(
  DWORD dwMessage, 
  PNOTIFYICONDATA pnid 
);

The first parameter to Shell_NotifyIcon is either of NIM_ADD, NIM_DELETE, or NIM_MODIFY. The second parameter will be a pointer to NOTIFYICONDATA variable.
The NOTIFYICONDATA structure variable contains all the information regarding the icon to be placed on the status area.

For NOTIFYICONDATA structure check :

I have discussed few members here :

  • hWnd - Handle to the window that receives notification messages associated with an icon in the taskbar status area. 
In MFC applications it can be retrieved by calling this->GetSafeHWnd( ) from main dialog of the application in Dialog based application, and from CMainFrame class in SDI or MDI applications.
  • hIcon - Handle to the icon to be added, modified, or deleted. If the Icon is imported to the project, handle can be obtained by calling LoadIcon API.
  • uCallbackMessage - Application-defined message identifier. The system uses this identifier to send notifications to the window identified in hWnd. These notifications are sent when a mouse event occurs in the bounding rectangle of the icon, or when the icon is selected or activated with the keyboard.
The identifier can be defined a numeric value starting from WM_USER+1 to WM_USER+64k which is not previously assigned for other such identifiers. The application (Dialog based, SDI or MDI) receives ON_MESSAGE with first parameter as the identifier declared above.


Sample Application : 

1.Create a new Dialog Based Application using MFC wizard and name it as TrayIcon.

2.Open the file for CTrayIconDlg, the trayicondlg.cpp file and add the following at the end of preprocessor directives

define TRAYICON WM_USER+100
define ICONNUMBER 4001

3. From Resource View , insert an icon to the project and with id as ID_TRAYICON.
Also add a menu to the project and name it as ID_TRAYMENU. 
Using Menu Designer create a menu item status and add HIDE and SHOW menu in its drop-down menu 
Set the id for the menu item as ID_SHOW and ID_HIDE.
From the ClassWizard,add message handlers for each of them for COMMAND message.
The function that handles them will be OnShow() and OnHide(). 

4. Add a member function to CTrayIconDlg class that will react to the messages send by the system to the application regarding the status area icon.
Define it as  
void OnTrayIconMessage(WPARAM wParam, LPARAM lParam)

Edit the code of the function to add these 

POINT pt;
GetCursorPos(&pt);
// When Mouse Left button is clicked or Tapped in touch screen devices
if( (UINT)lParam == WM_LBUTTONDOWN)
{                                                                                     
CMenu menu;
menu.LoadMenu(ID_TRAYMENU);
CMenu *pContextMenu = menu.GetSubMenu(0);
pContextMenu->TrackPopupMenu( TPM_LEFTALIGN,
pt.x,pt.y,AfxGetMainWnd() );
}

5. Check for tag BEGIN_MESSAGE_MAP and END_MESSAGE_MAP and insert the following in between these two:

ON_MESSAGE(TRAYICON, OnTrayIconMessage) // Mapping the message identifier to the callback function

it should look like :

BEGIN_MESSAGE_MAP(CTrayIconDlg, CDialog)
//{{AFX_MSG_MAP(CTrayIconDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CLOSE()
ON_WM_DESTROY()
ON_MESSAGE(TRAYICON, OnTrayIconMessage) //message identifier
ON_COMMAND(ID_SHOW, OnShow) //Menu message identifier for SHOW
ON_COMMAND(ID_HIDE, OnHide) //Menu message identifier for HIDE
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

6. To add the icon when the application starts, place the following code to OnInitDialog function after the TODO tag

NOTIFYICONDATA nid;
nid.cbSize = sizeof(NOTIFYICONDATA); // Size of Structure
nid.hWnd=this->GetSafeHwnd(); // Application Handle
strcpy(nid.szTip, "My Tray Icon App");
nid.uCallbackMessage=TRAYICON; // message identifier - Callback function mapped to this
nid.uFlags=NIF_MESSAGE|NIF_ICON|NIF_TIP;
nid.hIcon=AfxGetApp( )->LoadIcon(ID_TRAYICON); // ICON to be displayed
nid.uID=ICONNUMBER;  
Shell_NotifyIcon(NIM_ADD,&nid); // NIM_ADD to add the icon

If yours is SDI or MDI application, add the above code to OnCreate function of CMainFrame class.

7. To delete the icon from status area when the application gets destroyed ,add a handler for WM_DESTROY message using ClassWizard..
 The function name will be OnDestroy.
Add the following code

NOTIFYICONDATA nid;
nid.cbSize=sizeof( NOTIFYICONDATA );
nid.hWnd=this->GetSafeHwnd();
nid.uID=ICONNUMBER;
Shell_NotifyIcon( NIM_DELETE , &nid ); // delete the Icon

8. To Show and Hide the Application when Menu Item SHOW and HIDE is clicked edit the following functions 

void CTrayIconDlg::OnStatusHide()
{
// TODO: Add your command handler code here
ShowWindow(SW_HIDE);
}

void CTrayIconDlg::OnStatusShow()
{
// TODO: Add your command handler code here
ShowWindow(SW_SHOWNORMAL);
}

In this way we can add and delete(Modify)the icon to the status area,and menu items can be displayed when clicked on tray icons.

No comments: