Page 1 of 1

Putting a Program Icon in the System Tray with C++ Rate Topic: -----

#1 snoopy11  Icon User is online

  • Engineering ● Software
  • member icon

Reputation: 804
  • View blog
  • Posts: 2,373
  • Joined: 20-March 10

Posted 02 December 2011 - 06:04 PM

Putting a Program Icon in the System Tray with C++


This Tutorial is all about getting program icons onto the System Tray.

The System Tray is located in the Windows Taskbar, usually at the bottom right corner next to the clock. It contains miniature icons for easy access to system functions such as antivirus settings, printer, modem, sound volume, battery status, and more.

Now you too can have your programs icon sitting in the System Tray !!

I have tested this both Code::Blocks/MinGW and Visual Studio 2010
both work fine.

As this is not a Unicode build if you are building it in Visual Studio
you will have to do the following

Goto Project Properties -> Configuration Properties -> General
Under 'Project Defaults' it says Character set in the drop down menu box
select 'Use Multi-Byte Character Set'.

The Resource File.

First of all I would like to discuss the resource.rc file
This is as follows

#include "resource.h"

ICO1 ICON DISCARDABLE "snoopy.ico"



It sets up an icon called 'snoopy.ico' and gives it the label ICO1

We will use this later on in our main .cpp.

The Resource Header.

Our Resource Header is called 'resource.h'.

#include <windows.h>
#include <shellapi.h>

#define ICO1 101
#define ID_TRAY_APP_ICON    1001
#define ID_TRAY_EXIT        1002
#define WM_SYSICON          (WM_USER + 1)



This is our list of defines we give each of them a unique
number this is important. Try to group things in
diferrent hundreds and thousands groupings
obviously the same type of things go in the same type of groups.

ICO1 is given the unique number 101 this is our icon.

WM_SYSICON is a user defined Windows Message
that we will use in a callback procedure later on.

ID_TRAY_APP_ICON
we use in WM_SYSICON

ID_TRAY_EXIT
we use as an identifier in the AppendMenu command later on.

The Main Program main.cpp


Next up is the main.cpp.

I will include all of it at once then explain it piece by piece.
But for those who want to see what the program does, that is
those that can't wait. You can build it with the icon provided.
Naming the icon 'snoopy.ico' and putting it in your project directory.

Actually modi123_1 very kindly supplied me with the snoopy.ico file.
He truly did a wonderful job and is a prince among men.
You will find the snoopy.ico attached to the bottom of this Tutorial.

#include "resource.h"

/*variables*/
UINT WM_TASKBAR = 0;
HWND Hwnd;
HMENU Hmenu;
NOTIFYICONDATA notifyIconData;
TCHAR szTIP[64] = TEXT("Snoopy.. \n Kicks Ass!");
char szClassName[ ] = "Snoopy's System Tray Demo.";



/*procedures  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
void minimize();
void restore();
void InitNotifyIconData();



int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nCmdShow)
{
    /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */
    WM_TASKBAR = RegisterWindowMessageA("TaskbarCreated");
    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE(ICO1));
    wincl.hIconSm = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE(ICO1));
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    wincl.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(255, 255, 255)));
    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    Hwnd = CreateWindowEx (
               0,                   /* Extended possibilites for variation */
               szClassName,         /* Classname */
               szClassName,       /* Title Text */
               WS_OVERLAPPEDWINDOW, /* default window */
               CW_USEDEFAULT,       /* Windows decides the position */
               CW_USEDEFAULT,       /* where the window ends up on the screen */
               544,                 /* The programs width */
               375,                 /* and height in pixels */
               HWND_DESKTOP,        /* The window is a child-window to desktop */
               NULL,                /* No menu */
               hThisInstance,       /* Program Instance handler */
               NULL                 /* No Window Creation data */
           );
    /*Initialize the NOTIFYICONDATA structure only once*/
    InitNotifyIconData();
    /* Make the window visible on the screen */
    ShowWindow (Hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    if ( message==WM_TASKBAR && !IsWindowVisible( Hwnd ) )
    {
        minimize();
        return 0;
    }

    switch (message)                  /* handle the messages */
    {
    case WM_ACTIVATE:
        Shell_NotifyIcon(NIM_ADD, &notifyIconData);
        break;
    case WM_CREATE:

        ShowWindow(Hwnd, SW_HIDE);
        Hmenu = CreatePopupMenu();
        AppendMenu(Hmenu, MF_STRING, ID_TRAY_EXIT,  TEXT( "Exit The Demo" ) );

        break;

    case WM_SYSCOMMAND:
        /*In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter 
		are used internally by the system. To obtain the correct result when testing the value of wParam, 
		an application must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator.*/ 
		
		switch( wParam & 0xFFF0 )  
        {
        case SC_MINIMIZE:
        case SC_CLOSE:  
            minimize() ;
            return 0 ;
            break;
        }
        break;

        
        // Our user defined WM_SYSICON message.
    case WM_SYSICON:
    {

        switch(wParam)
        {
        case ID_TRAY_APP_ICON:
        SetForegroundWindow(Hwnd);

            break;
        }


        if (lParam == WM_LBUTTONUP)
        {

            restore();
        }
        else if (lParam == WM_RBUTTONDOWN) 
        {
            // Get current mouse position.
            POINT curPoint ;
            GetCursorPos( &curPoint ) ;
			SetForegroundWindow(Hwnd);

            // TrackPopupMenu blocks the app until TrackPopupMenu returns

            UINT clicked = TrackPopupMenu(Hmenu,TPM_RETURNCMD | TPM_NONOTIFY,curPoint.x,curPoint.y,0,hwnd,NULL);


            
            SendMessage(hwnd, WM_NULL, 0, 0); // send benign message to window to make sure the menu goes away.
            if (clicked == ID_TRAY_EXIT)
            {
                // quit the application.
                Shell_NotifyIcon(NIM_DELETE, &notifyIconData);
                PostQuitMessage( 0 ) ;
            }
        }
    }
    break;

    // intercept the hittest message..
    case WM_NCHITTEST:
    {
       UINT uHitTest = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
        if(uHitTest == HTCLIENT)
            return HTCAPTION;
        else
            return uHitTest;
    }

    case WM_CLOSE:

        minimize() ;
        return 0;
        break;

    case WM_DESTROY:

        PostQuitMessage (0);
        break;

    }

    return DefWindowProc( hwnd, message, wParam, lParam ) ;
}


void minimize()
{
    // hide the main window
    ShowWindow(Hwnd, SW_HIDE);
}


void restore()
{
    ShowWindow(Hwnd, SW_SHOW);
}

void InitNotifyIconData()
{
    memset( &notifyIconData, 0, sizeof( NOTIFYICONDATA ) ) ;

    notifyIconData.cbSize = sizeof(NOTIFYICONDATA);
    notifyIconData.hWnd = Hwnd;
    notifyIconData.uID = ID_TRAY_APP_ICON;
    notifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    notifyIconData.uCallbackMessage = WM_SYSICON; //Set up our invented Windows Message
    notifyIconData.hIcon = (HICON)LoadIcon( GetModuleHandle(NULL),      MAKEINTRESOURCE(ICO1) ) ;
    strncpy(notifyIconData.szTip, szTIP, sizeof(szTIP));
}




The Explanantion Section.

The first block of code deals with variables.

1st Block

/*variables*/
UINT WM_TASKBAR = 0;
HWND Hwnd;
HMENU Hmenu;
NOTIFYICONDATA notifyIconData;
TCHAR szTIP[64] = TEXT("Snoopy.. \n Kicks Ass!");
char szClassName[ ] = "Snoopy's System Tray Demo.";



WM_TASKBAR is used in RegisterWindowMessageA
it defines a new window message that is guaranteed to be unique throughout the system. In this case that message is "TaskbarCreated".

Hwnd and Hmenu are self explanatory.

notifyIconData sets up a NOTIFYICONDATA structure that
we initialise once and only once in the entire program.

szTIP is a tool tip text that appears above the System Tray Icon when you hover.

szClassName we use to give a unique name to our main application and also
doubles as the heading for our application window.

2nd Block

/*procedures  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
void minimize();
void restore();
void InitNotifyIconData()



First up we Have 'WindowProcedure' which is our windows callback
procedure that we set up in WinMain.

minimize and restore, deal with minimizing and restoring the window
from the system tray.

InitNotifyIconData initialises the NOTIFYICONDATA structure.


3rd Block

This is our main entry point WinMain.

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nCmdShow)
{
    /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */
    WM_TASKBAR = RegisterWindowMessageA("TaskbarCreated");
    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE(ICO1));
    wincl.hIconSm = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE(ICO1));
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    wincl.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(255, 255, 255)));
    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    Hwnd = CreateWindowEx (
               0,                   /* Extended possibilites for variation */
               szClassName,         /* Classname */
               szClassName,       /* Title Text */
               WS_OVERLAPPEDWINDOW, /* default window */
               CW_USEDEFAULT,       /* Windows decides the position */
               CW_USEDEFAULT,       /* where the window ends up on the screen */
               544,                 /* The programs width */
               375,                 /* and height in pixels */
               HWND_DESKTOP,        /* The window is a child-window to desktop */
               NULL,                /* No menu */
               hThisInstance,       /* Program Instance handler */
               NULL                 /* No Window Creation data */
           );
    /*Initialize the NOTIFYICONDATA structure only once*/
    InitNotifyIconData();
    /* Make the window visible on the screen */
    ShowWindow (Hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    return messages.wParam;
}




I am not going to say much about WinMain it's fairly heavily commented anyway
It basically fills a window class structure with the relevant properties and values.
Registers the class with Microsofts OS, Creates a window and then
Initializes the NOTIFYICONDATA structure through a special procedure.

Shows the Window and runs a message pump.

4th Block

This next bit of code is our Windows Callback procedure
Messages are intercepted and sent here for processing.

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    if ( message==WM_TASKBAR && !IsWindowVisible( Hwnd ) )
    {
        minimize();
        return 0;
    }

    switch (message)                  /* handle the messages */
    {
    case WM_ACTIVATE:
        Shell_NotifyIcon(NIM_ADD, &notifyIconData);
        break;
    case WM_CREATE:

        ShowWindow(Hwnd, SW_HIDE);
        Hmenu = CreatePopupMenu();
        AppendMenu(Hmenu, MF_STRING, ID_TRAY_EXIT,  TEXT( "Exit The Demo" ) );

        break;

    case WM_SYSCOMMAND:
        /*In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter 
		are used internally by the system. To obtain the correct result when testing the value of wParam, 
		an application must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator.*/ 
		
		switch( wParam & 0xFFF0 )  
        {
        case SC_MINIMIZE:
        case SC_CLOSE:  
            minimize() ;
            return 0 ;
            break;
        }
        break;

        
        // Our user defined WM_SYSICON message.
    case WM_SYSICON:
    {

        switch(wParam)
        {
        case ID_TRAY_APP_ICON:
        SetForegroundWindow(Hwnd);

            break;
        }


        if (lParam == WM_LBUTTONUP)
        {

            restore();
        }
        else if (lParam == WM_RBUTTONDOWN) 
        {
            // Get current mouse position.
            POINT curPoint ;
            GetCursorPos( &curPoint ) ;
			SetForegroundWindow(Hwnd);

            // TrackPopupMenu blocks the app until TrackPopupMenu returns

            UINT clicked = TrackPopupMenu(Hmenu,TPM_RETURNCMD | TPM_NONOTIFY,curPoint.x,curPoint.y,0,hwnd,NULL);


            
            SendMessage(hwnd, WM_NULL, 0, 0); // send benign message to window to make sure the menu goes away.
            if (clicked == ID_TRAY_EXIT)
            {
                // quit the application.
                Shell_NotifyIcon(NIM_DELETE, &notifyIconData);
                PostQuitMessage( 0 ) ;
            }
        }
    }
    break;

    // intercept the hittest message..
    case WM_NCHITTEST:
    {
       UINT uHitTest = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
        if(uHitTest == HTCLIENT)
            return HTCAPTION;
        else
            return uHitTest;
    }

    case WM_CLOSE:

        minimize() ;
        return 0;
        break;

    case WM_DESTROY:

        PostQuitMessage (0);
        break;

    }

    return DefWindowProc( hwnd, message, wParam, lParam ) ;
}




In case WM_ACTIVATE: we make sure the icon is added to the System Tray
on start up.

In case WM_CREATE we set up the pop-up menu give it a label

'Exit from Demo' and a value ID_TRAY_EXIT


In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter
are used internally by the system. To obtain the correct result when testing the value of wParam,
an application must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator.

WM_SYSICON is a user defined Windows message
it deals with on left button press it calls restore();

on right button press it tracks a popupmenu and see if it has been clicked
if it has we quit the application and remove the icon from the System Tray.

WM_NCHHITTEST deals with non client area hit tests

5th Block

These are our procedures

minimize hides the window.

restore shows the window.

void minimize()
{
    // hide the main window
    ShowWindow(Hwnd, SW_HIDE);
}


void restore()
{
    ShowWindow(Hwnd, SW_SHOW);
}

void InitNotifyIconData()
{
    memset( &notifyIconData, 0, sizeof( NOTIFYICONDATA ) ) ;

    notifyIconData.cbSize = sizeof(NOTIFYICONDATA);
    notifyIconData.hWnd = Hwnd;
    notifyIconData.uID = ID_TRAY_APP_ICON;
    notifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    notifyIconData.uCallbackMessage = WM_SYSICON; //Set up our invented Windows Message
    notifyIconData.hIcon = (HICON)LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE(ICO1) ) ;
    strncpy(notifyIconData.szTip, szTIP, sizeof(szTIP));
}





InitNotifyIconData sets up a NOTIFYICONDATA structure that we
declared early in the program we give these structures certain info
like where to find the icon. The callback message for the structure etc.
More info is here at MSDN.

MSDN Link

References.

MSDN.

You should have three files
resource.rc
resource.h
main.cpp


If you have compiled these successfully you should now
have a working program and an insight on how to do
this in your own software.

Attached File(s)

  • Attached File  snoopy.ico (894bytes)
    Number of downloads: 726


Is This A Good Question/Topic? 2
  • +

Page 1 of 1