Solved

Why does my newly created (in VC++. Net) Tray Icon disappear when i hover the mouse pointer over it?

Posted on 2004-08-25
13
735 Views
Last Modified: 2013-11-20
Why does my newly created (in VC++. Net) Tray Icon disappear when i hover the mouse pointer over it?
Im using Win 2000, Visual C++.Net and am using 32 x 32 icons for tray.
0
Comment
Question by:kullyN
  • 7
  • 4
13 Comments
 
LVL 86

Expert Comment

by:jkr
Comment Utility
You need to handle the callback message that is registered when passing NOTIFYICONDATA::uCallbackMessage to 'Shell_NotifyIcon()', otherwise the icon will be removed.
0
 
LVL 86

Accepted Solution

by:
jkr earned 500 total points
Comment Utility
BTW, see also http://www.codeproject.com/shell/StealthDialog.asp ("Basic use of Shell_NotifyIcon in Win32")
0
 

Author Comment

by:kullyN
Comment Utility
Hi,

This is my code although i predominantly got some of the code from MSDN - can you point out where im going wrong in specific?


CTrayIcon::CTrayIcon()
{
      ASSERT(FALSE);
}


CTrayIcon::CTrayIcon(UINT uID)
{
      // Initialize NOTIFYICONDATA
      memset(&m_nid, 0 , sizeof(m_nid));
      m_nid.cbSize = sizeof(m_nid);
      m_nid.uID = uID;      // never changes after construction

      m_notifyHook.m_pTrayIcon = this; // notification window hook
      m_parentHook.m_pTrayIcon = this;      // parent window hook

      // Use resource string as tip if there is one
      AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
}

CTrayIcon::~CTrayIcon()
{
      SetIcon(0); // remove icon from system tray
}

void CTrayIcon::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg)
{
      // If the following assert fails, you're probably
      // calling me before you created your window. Oops.
      ASSERT(pNotifyWnd==NULL || ::IsWindow(pNotifyWnd->GetSafeHwnd()));
      m_nid.hWnd = pNotifyWnd->GetSafeHwnd();

      ASSERT(uCbMsg==0 || uCbMsg>=WM_USER);
      m_nid.uCallbackMessage = uCbMsg;

      CWnd* pParentWnd = pNotifyWnd ? pNotifyWnd->GetTopLevelParent() : NULL;

      // Install window hooks. Must be different because
      // taskbar creation message only goes to top-level parent.
      m_notifyHook.HookWindow(pNotifyWnd);
      if (pParentWnd!=pNotifyWnd)
            m_parentHook.HookWindow(pParentWnd);
}

BOOL CTrayIcon::SetIcon(UINT uID)
{
      HICON hicon=NULL;
      if (uID) {
      
            AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
            hicon = AfxGetApp()->LoadIcon(uID);
      }
      return SetIcon(hicon, NULL);
}

BOOL CTrayIcon::SetIcon(HICON hicon, LPCTSTR lpTip)
{
      UINT msg;
      m_nid.uFlags = 0;

      // Set the icon
      if (hicon)
      {
            // Add or replace icon in system tray
            msg = m_nid.hIcon ? NIM_MODIFY : NIM_ADD;
            m_nid.hIcon = hicon;
            m_nid.uFlags |= NIF_ICON;
      }
      else
      {
            // remove icon from tray
            if (m_nid.hIcon==NULL)
            return TRUE;            // already deleted
            
            msg = NIM_DELETE;
      }

      // Use the tip, if any
      if (lpTip)
      {
            _tcsncpy(m_nid.szTip, lpTip, countof(m_nid.szTip));
      }

      if (m_nid.szTip[0])
      {
            m_nid.uFlags |= NIF_TIP;
      }

      // Use callback if any
      if (m_nid.uCallbackMessage && m_nid.hWnd)
      {
            m_nid.uFlags |= NIF_MESSAGE;
      }

      // Do it
      BOOL bRet = Shell_NotifyIcon(msg, &m_nid);
      if (msg==NIM_DELETE || !bRet)
      {
            m_nid.hIcon = NULL;      // failed
      }
      return bRet;
}

BOOL CTrayIcon::ShowBalloonTip(LPCTSTR szMsg, LPCTSTR szTitle,
      UINT uTimeout, DWORD dwInfoFlags)
{
      m_nid.cbSize=sizeof(NOTIFYICONDATA);
      m_nid.uFlags = NIF_INFO;
      m_nid.uTimeout = uTimeout;
      m_nid.dwInfoFlags = dwInfoFlags;
      strcpy(m_nid.szInfo,szMsg ? szMsg : _T(""));
      strcpy(m_nid.szInfoTitle,szTitle ? szTitle : _T(""));
      return Shell_NotifyIcon(NIM_MODIFY, &m_nid);
}


BOOL CTrayIcon::ShowBalloonTip(UINT uID, LPCTSTR szTitle,
      UINT uTimeout, DWORD dwInfoFlags)
{
      CString s;
      return s.LoadString(uID) ?
            ShowBalloonTip(s, szTitle, uTimeout, dwInfoFlags) : FALSE;
}


LRESULT CTrayIcon::CTrayHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
      if (msg==m_pTrayIcon->m_nid.uCallbackMessage &&       m_hWnd==m_pTrayIcon->m_nid.hWnd)
      {
            m_pTrayIcon->OnTrayNotify(wp, lp);
            //      } else if (msg==WM_DESTROY) {
            //            m_pTrayIcon->SetIcon(NULL);
      }
      else if (msg==WM_TASKBARCREATED)
      {
            m_pTrayIcon->OnTaskBarCreate(wp, lp);
      }
      return CSubclassWnd::WindowProc(msg, wp, lp);
}

LRESULT CTrayIcon::OnTrayNotify(WPARAM wID, LPARAM lEvent)
{
      if (wID!=m_nid.uID || (lEvent!=WM_RBUTTONUP && lEvent!=WM_LBUTTONDBLCLK))
            return 0;

      // If there's a resource menu with the same ID as the icon, use it as
      // the right-button popup menu. CTrayIcon will interprets the first
      // item in the menu as the default command for WM_LBUTTONDBLCLK
      //
      CMenu menu;
      if (!menu.LoadMenu(m_nid.uID))
            return 0;
      CMenu* pSubMenu = menu.GetSubMenu(0);
      if (!pSubMenu)
            return 0;

      if (lEvent==WM_RBUTTONUP)
      {
            // Make first menu item the default (bold font)
            ::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE);

            // Display the menu at the current mouse location. There's a "bug"
            // (Microsoft calls it a feature) in Windows 95 that requires calling
            // SetForegroundWindow. To find out more, search for Q135788 in MSDN.
            //
            CPoint mouse;
            GetCursorPos(&mouse);
            ::SetForegroundWindow(m_nid.hWnd);      
            ::TrackPopupMenu(pSubMenu->m_hMenu, 0, mouse.x, mouse.y, 0,
                  m_nid.hWnd, NULL);
            ::PostMessage(m_nid.hWnd, WM_NULL, 0, 0);

      }
      else  // double click: execute first menu item
            ::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->GetMenuItemID(0), 0);

      return 1; // handled
}

LRESULT CTrayIcon::OnTaskBarCreate(WPARAM wp, LPARAM lp)
{
      // Reinstall taskbar icon
      HICON hIcon = m_nid.hIcon;
      m_nid.hIcon = NULL;
      if (hIcon)
            SetIcon(hIcon, NULL); // will reuse current tip
      return 0;
}
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
When you handle the callback message, you should return 0 and not pass the message on to the default handler.
0
 

Author Comment

by:kullyN
Comment Utility
Are you talking aboput the below process - i have returned 0 there and it does not stop the icon disappearing - is this right below?

LRESULT CTrayIcon::CTrayHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
      if (msg==m_pTrayIcon->m_nid.uCallbackMessage &&       m_hWnd==m_pTrayIcon->m_nid.hWnd)
      {
            return 0;
            // m_pTrayIcon->OnTrayNotify(wp, lp);
            //      } else if (msg==WM_DESTROY) {
            //            m_pTrayIcon->SetIcon(NULL);
      }
      else if (msg == WM_TASKBARCREATED)
      {
            m_pTrayIcon->OnTaskBarCreate(wp, lp);
      }
      return CSubclassWnd::WindowProc(msg, wp, lp);
}
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:kullyN
Comment Utility
Ok, I also forgot to mention that im using a test harness class to call SetIcon on the three icons already created. Could this be causing the error?
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
I'd make that read

     if (msg==m_pTrayIcon->m_nid.uCallbackMessage &&      m_hWnd==m_pTrayIcon->m_nid.hWnd)
     {
         m_pTrayIcon->OnTrayNotify(wp, lp);
              } else if (msg==WM_DESTROY) {
                   m_pTrayIcon->SetIcon(NULL);

         return 0;
    }

That you are using test classes could also be the reason though... Is the above handler being called at all?
0
 

Author Comment

by:kullyN
Comment Utility
In referring to this the above CTrayIcon class is now called CTrafficLights.

This is my test harness code:

// TestHarness.cpp : implementation file
//

#include "stdafx.h"
#include "StatusMonitor.h"
#include "TestHarness.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
      CAboutDlg();

// Dialog Data
      //{{AFX_DATA(CAboutDlg)
      enum { IDD = IDD_ABOUTBOX };
      //}}AFX_DATA

      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CAboutDlg)
      protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
      //}}AFX_VIRTUAL

// Implementation
protected:
      //{{AFX_MSG(CAboutDlg)
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};


#define WM_MY_TRAY_NOTIFICATION WM_USER+0


CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
      //{{AFX_DATA_INIT(CAboutDlg)
      //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
      CDialog::DoDataExchange(pDX);
      //{{AFX_DATA_MAP(CAboutDlg)
      //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
      //{{AFX_MSG_MAP(CAboutDlg)
            // No message handlers
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTestHarness dialog

CTestHarness::CTestHarness(CWnd* pParent /*=NULL*/)
            : CDialog(CTestHarness::IDD, pParent)
{
      //{{AFX_DATA_INIT(CTestHarness)
      m_blnShowBalloon = TRUE;
      //}}AFX_DATA_INIT
      // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
      m_hIcon = AfxGetApp()->LoadIcon(IDI_MAINFRAME);
      
      m_trafficlights = new CTrafficLights(IDI_GREEN);
      m_trafficlights->SetIcon(IDI_GREEN);
}




/*LRESULT CTestHarness::OnTrayNotification(WPARAM wp, LPARAM lp){                    
      return m_trafficlights->OnTrayNotify(wp, lp);
}*/



void CTestHarness::DoDataExchange(CDataExchange* pDX)
{
      CDialog::DoDataExchange(pDX);
      //{{AFX_DATA_MAP(CTestHarness)
      DDX_Check(pDX, IDC_SHOW_BALLOON, m_blnShowBalloon);
      //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTestHarness, CDialog)
      //{{AFX_MSG_MAP(CTestHarness)
      ON_WM_SYSCOMMAND()
      ON_WM_PAINT()
      ON_WM_QUERYDRAGICON()
      ON_BN_CLICKED(IDC_RED, OnRed)
      ON_BN_CLICKED(IDC_AMBER, OnAmber)
      ON_BN_CLICKED(IDC_GREEN, OnGreen)
      ON_BN_CLICKED(IDC_SHOW_BALLOON, OnShowBalloon)
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTestHarness message handlers

BOOL CTestHarness::OnInitDialog()
{
      CDialog::OnInitDialog();

      // Add "About..." menu item to system menu.

      // IDM_ABOUTBOX must be in the system command range.
      ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
      ASSERT(IDM_ABOUTBOX < 0xF000);

      CMenu* pSysMenu = GetSystemMenu(FALSE);
      if (pSysMenu != NULL)
      {
            CString strAboutMenu;
            strAboutMenu.LoadString(IDS_ABOUTBOX);
            if (!strAboutMenu.IsEmpty())
            {
                  pSysMenu->AppendMenu(MF_SEPARATOR);
                  pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
            }
      }

      // Set the icon for this dialog.  The framework does this automatically
      //  when the application's main window is not a dialog
      SetIcon(m_hIcon, TRUE);                  // Set big icon
      SetIcon(m_hIcon, FALSE);            // Set small icon
      
      // TODO: Add extra initialization here
      
      return TRUE;  // return TRUE  unless you set the focus to a control
}

void CTestHarness::OnSysCommand(UINT nID, LPARAM lParam)
{
      if ((nID & 0xFFF0) == IDM_ABOUTBOX)
      {
            CAboutDlg dlgAbout;
            dlgAbout.DoModal();
      }
      else
      {
            CDialog::OnSysCommand(nID, lParam);
      }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CTestHarness::OnPaint()
{
      if (IsIconic())
      {
            CPaintDC dc(this); // device context for painting

            SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

            // Center icon in client rectangle
            int cxIcon = GetSystemMetrics(SM_CXICON);
            int cyIcon = GetSystemMetrics(SM_CYICON);
            CRect rect;
            GetClientRect(&rect);
            int x = (rect.Width() - cxIcon + 1) / 2;
            int y = (rect.Height() - cyIcon + 1) / 2;

            // Draw the icon
            dc.DrawIcon(x, y, m_hIcon);
      }
      else
      {
            CDialog::OnPaint();
      }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CTestHarness::OnQueryDragIcon()
{
      return (HCURSOR) m_hIcon;
}


void CTestHarness::OnRed()
{
      m_trafficlights->SetIcon(IDI_RED);
      if (m_blnShowBalloon)
            m_trafficlights->ShowBalloonTip("Network connection failed","System Failure",10, NIIF_WARNING);
}

void CTestHarness::OnAmber()
{
      m_trafficlights->SetIcon(IDI_AMBER);
      
}

void CTestHarness::OnGreen()
{
      m_trafficlights->SetIcon(IDI_GREEN);      
}

void CTestHarness::OnShowBalloon()
{
      UpdateData(TRUE);
      
}
0
 

Author Comment

by:kullyN
Comment Utility
This is the full code - im using three main IDD images - GREEN, AMBER and RED


// TrafficLight.cpp
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "TrafficLightAPI.h"
#include <afxpriv.h>            // for AfxLoadString


#undef _WIN32_IE
#define _WIN32_IE 0x0500


#define countof(x)      (sizeof(x)/sizeof(x[0]))


// Windows sends this message when the taskbar is created. This can happen
// if it crashes and Windows has to restart it. CTrafficLightAPI responds by
// re-installing its icon.
const UINT WM_TASKBARCREATED = ::RegisterWindowMessage(_T("TaskbarCreated"));

IMPLEMENT_DYNAMIC(CTrafficLightAPI, CCmdTarget)

CTrafficLightAPI::CTrafficLightAPI()
{
      ASSERT(FALSE);
}

CTrafficLightAPI::CTrafficLightAPI(UINT uID)
{
      // Initialize NOTIFYICONDATA for use within the
      memset(&m_nid, 0 , sizeof(m_nid));
      m_nid.cbSize = sizeof(m_nid);
      m_nid.uID = uID;                                    // never changes after construction

      m_notifyHook.m_pTrafficLights = this;      // notification window hook
      m_parentHook.m_pTrafficLights = this;      // parent window hook

      // Use resource string as tip if there is one
      AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
}

CTrafficLightAPI::~CTrafficLightAPI()
{
      SetIcon(0); // remove icon from system tray
}

// Set notification window. It must be created already.
void CTrafficLightAPI::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg)
{
      // If the following assert fails, you're probably
      // calling me before you created your window. Oops.
      ASSERT(pNotifyWnd==NULL || ::IsWindow(pNotifyWnd->GetSafeHwnd()));
      m_nid.hWnd = pNotifyWnd->GetSafeHwnd();

      ASSERT(uCbMsg==0 || uCbMsg>=WM_USER);
      m_nid.uCallbackMessage = uCbMsg;

      CWnd* pParentWnd = pNotifyWnd ? pNotifyWnd->GetTopLevelParent() : NULL;

      // Install window hooks. Must be different because
      // taskbar creation message only goes to top-level parent.
      m_notifyHook.HookWindow(pNotifyWnd);
      if (pParentWnd!=pNotifyWnd)
      {
            m_parentHook.HookWindow(pParentWnd);
      }
}


// This is the main variant for setting the icon.
// Sets both the icon and tooltip from resource ID
// To remove the icon, call SetIcon(0)
BOOL CTrafficLightAPI::SetIcon(UINT uID)
{
      HICON hicon=NULL;
      if (uID)
      {      
            AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
            hicon = AfxGetApp()->LoadIcon(uID);
      }
      return SetIcon(hicon, NULL);
}


// Common SetIcon for all overloads.
BOOL CTrafficLightAPI::SetIcon(HICON hicon, LPCTSTR lpTip)
{
      UINT msg;
      m_nid.uFlags = 0;

      // Set the icon
      if (hicon)
      {
            // Add or replace icon in system tray
            msg = m_nid.hIcon ? NIM_MODIFY : NIM_ADD;
            m_nid.hIcon = hicon;
            m_nid.uFlags |= NIF_ICON;
      }
      else
      {
            // remove icon from tray
            if (m_nid.hIcon==NULL)
            return TRUE;            // already deleted
            
            msg = NIM_DELETE;
      }

      // Use the tip, if any
      if (lpTip)
            _tcsncpy(m_nid.szTip, lpTip, countof(m_nid.szTip));
      if (m_nid.szTip[0])
            m_nid.uFlags |= NIF_TIP;
      
      // Use callback if any
      if (m_nid.uCallbackMessage && m_nid.hWnd)
      {
            m_nid.uFlags |= NIF_MESSAGE;
      }

      // Do it
      BOOL bRet = Shell_NotifyIcon(msg, &m_nid);
      if (msg==NIM_DELETE || !bRet)
      {
            m_nid.hIcon = NULL;      // failed
      }
      return bRet;
}


// Show balloon tip: args give message, tiemout, etc.
BOOL CTrafficLightAPI::ShowBalloonTip(LPCTSTR szMsg, LPCTSTR szTitle, UINT uTimeout, DWORD dwInfoFlags)
{
      m_nid.cbSize=sizeof(NOTIFYICONDATA);
      m_nid.uFlags = NIF_INFO;
      m_nid.uTimeout = uTimeout;
      m_nid.dwInfoFlags = dwInfoFlags;
      strcpy(m_nid.szInfo,szMsg ? szMsg : _T(""));
      strcpy(m_nid.szInfoTitle,szTitle ? szTitle : _T(""));
      return Shell_NotifyIcon(NIM_MODIFY, &m_nid);
}


BOOL CTrafficLightAPI::ShowBalloonTip(UINT uID, LPCTSTR szTitle, UINT uTimeout, DWORD dwInfoFlags)
{
      CString s;
      return s.LoadString(uID) ?      ShowBalloonTip(s, szTitle, uTimeout, dwInfoFlags) : FALSE;
}


// Same hook class used for both notifcation window and toplevel
// parent; hook function determines which.
LRESULT CTrafficLightAPI::CTrayHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
      if (msg==m_pTrafficLights->m_nid.uCallbackMessage && m_hWnd==m_pTrafficLights->m_nid.hWnd)
      {
            m_pTrafficLights->OnTrayNotify(wp, lp);
            //      } else if (msg==WM_DESTROY) {
            //            m_pTrafficLights->SetIcon(NULL);
      }
      else if (msg == WM_TASKBARCREATED)
      {
            m_pTrafficLights->OnTaskBarCreate(wp, lp);
      }
      return CSubclassWnd::WindowProc(msg, wp, lp);
}


// Default event handler handles right-menu and doubleclick.
// Override to do something different.
LRESULT CTrafficLightAPI::OnTrayNotify(WPARAM wID, LPARAM lEvent)
{
      if (wID!=m_nid.uID || (lEvent!=WM_RBUTTONUP && lEvent!=WM_LBUTTONDBLCLK))
            return 0;

      // If there's a resource menu with the same ID as the icon, use it as
      // the right-button popup menu. CTrafficLightAPI will interprets the first
      // item in the menu as the default command for WM_LBUTTONDBLCLK
      CMenu menu;
      if (!menu.LoadMenu(m_nid.uID))
            return 0;
      CMenu* pSubMenu = menu.GetSubMenu(0);
      if (!pSubMenu)
            return 0;

      if (lEvent==WM_RBUTTONUP)
      {
            // Make first menu item the default (bold font)
            ::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE);

            // Display the menu at the current mouse location. There's a "bug"
            // (Microsoft calls it a feature) in Windows 95 that requires calling
            // SetForegroundWindow. To find out more, search for Q135788 in MSDN.
            CPoint mouse;
            GetCursorPos(&mouse);
            ::SetForegroundWindow(m_nid.hWnd);      
            ::TrackPopupMenu(pSubMenu->m_hMenu, 0, mouse.x, mouse.y, 0,      m_nid.hWnd, NULL);
            ::PostMessage(m_nid.hWnd, WM_NULL, 0, 0);

      }
      else  // double click: execute first menu item
            ::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->GetMenuItemID(0), 0);

      return 1; // handled
}

// Explorer had to restart the taskbar: add icons again
LRESULT CTrafficLightAPI::OnTaskBarCreate(WPARAM wp, LPARAM lp)
{
      // Reinstall taskbar icon
      HICON hIcon = m_nid.hIcon;
      m_nid.hIcon = NULL;
      if (hIcon)
      {
        SetIcon(hIcon, NULL); // will reuse current tip
      }
      return 0;
}

//// SubClass.cpp
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "Subclass.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//////////////////
// The message hook map is derived from CMapPtrToPtr, which associates
// a pointer with another pointer. It maps an HWND to a CSubclassWnd, like
// the way MFC's internal maps map HWND's to CWnd's. The first CSubclassWnd
// attached to a window is stored in the map; all other CSubclassWnd's for that
// window are then chained via CSubclassWnd::m_pNext.
//
class CSubclassWndMap : private CMapPtrToPtr {
public:
      CSubclassWndMap();
      ~CSubclassWndMap();
      static CSubclassWndMap& GetHookMap();
      void Add(HWND hwnd, CSubclassWnd* pSubclassWnd);
      void Remove(CSubclassWnd* pSubclassWnd);
      void RemoveAll(HWND hwnd);
      CSubclassWnd* Lookup(HWND hwnd);
};

// This trick is used so the hook map isn't
// instantiated until someone actually requests it.
//
#define      theHookMap      (CSubclassWndMap::GetHookMap())

IMPLEMENT_DYNAMIC(CSubclassWnd, CWnd);

CSubclassWnd::CSubclassWnd()
{
      m_pNext = NULL;
      m_pOldWndProc = NULL;      
      m_hWnd  = NULL;
}

CSubclassWnd::~CSubclassWnd()
{
      if (m_hWnd)
            HookWindow((HWND)NULL);            // unhook window
}

//////////////////
// Hook a window.
// This installs a new window proc that directs messages to the CSubclassWnd.
// pWnd=NULL to remove.
//
BOOL CSubclassWnd::HookWindow(HWND hwnd)
{
      ASSERT_VALID(this);
      if (hwnd) {
            // Hook the window
            ASSERT(m_hWnd==NULL);
            ASSERT(::IsWindow(hwnd));
            theHookMap.Add(hwnd, this);                  // Add to map of hooks
      }
      else if (m_hWnd)
      {
            // Unhook the window
            theHookMap.Remove(this);                        // Remove from map
            m_pOldWndProc = NULL;
      }
      m_hWnd = hwnd;
      return TRUE;
}

//////////////////
// Window proc-like virtual function which specific CSubclassWnds will
// override to do stuff. Default passes the message to the next hook;
// the last hook passes the message to the original window.
// You MUST call this at the end of your WindowProc if you want the real
// window to get the message. This is just like CWnd::WindowProc, except that
// a CSubclassWnd is not a window.
//
LRESULT CSubclassWnd::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
//      ASSERT_VALID(this);  // removed for speed
      ASSERT(m_pOldWndProc);
      return m_pNext ? m_pNext->WindowProc(msg, wp, lp) :      
            ::CallWindowProc(m_pOldWndProc, m_hWnd, msg, wp, lp);
}

//////////////////
// Like calling base class WindowProc, but with no args, so individual
// message handlers can do the default thing. Like CWnd::Default
//
LRESULT CSubclassWnd::Default()
{
      // MFC stores current MSG in thread state
      MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
      // Note: must explicitly call CSubclassWnd::WindowProc to avoid infinte
      // recursion on virtual function
      return CSubclassWnd::WindowProc(curMsg.message, curMsg.wParam, curMsg.lParam);
}

#ifdef _DEBUG
void CSubclassWnd::AssertValid() const
{
      CObject::AssertValid();
      ASSERT(m_hWnd==NULL || ::IsWindow(m_hWnd));
      if (m_hWnd)
      {
            for (CSubclassWnd* p = theHookMap.Lookup(m_hWnd); p; p=p->m_pNext)
            {
                  if (p==this)
                  break;
            }
            ASSERT(p); // should have found it!
      }
}

void CSubclassWnd::Dump(CDumpContext& dc) const
{
      CObject::Dump(dc);
}

#endif

//////////////////
// Subclassed window proc for message hooks. Replaces AfxWndProc (or whatever
// else was there before.)
//
LRESULT CALLBACK
HookWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
#ifdef _USRDLL
      // If this is a DLL, need to set up MFC state
      AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif

      // Set up MFC message state just in case anyone wants it
      // This is just like AfxCallWindowProc, but we can't use that because
      // a CSubclassWnd is not a CWnd.
      //
      MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
      MSG  oldMsg = curMsg;   // save for nesting
      curMsg.hwnd            = hwnd;
      curMsg.message = msg;
      curMsg.wParam  = wp;
      curMsg.lParam  = lp;

      // Get hook object for this window. Get from hook map
      CSubclassWnd* pSubclassWnd = theHookMap.Lookup(hwnd);
      ASSERT(pSubclassWnd);

      LRESULT lr;
      if (msg==WM_NCDESTROY) {
            // Window is being destroyed: unhook all hooks (for this window)
            // and pass msg to orginal window proc
            //
            WNDPROC wndproc = pSubclassWnd->m_pOldWndProc;
            theHookMap.RemoveAll(hwnd);
            lr = ::CallWindowProc(wndproc, hwnd, msg, wp, lp);

      } else {
            // pass to msg hook
            lr = pSubclassWnd->WindowProc(msg, wp, lp);
      }
      curMsg = oldMsg;                  // pop state
      return lr;
}


// CSubclassWndMap implementation
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CSubclassWndMap::CSubclassWndMap()
{
}

CSubclassWndMap::~CSubclassWndMap()
{
// This assert bombs when posting WM_QUIT, so I've deleted it.
//      ASSERT(IsEmpty());      // all hooks should be removed!      
}

//////////////////
// Get the one and only global hook map
//
CSubclassWndMap& CSubclassWndMap::GetHookMap()
{
      // By creating theMap here, C++ doesn't instantiate it until/unless
      // it's ever used! This is a good trick to use in C++, to
      // instantiate/initialize a static object the first time it's used.
      //
      static CSubclassWndMap theMap;
      return theMap;
}

/////////////////
// Add hook to map; i.e., associate hook with window
//
void CSubclassWndMap::Add(HWND hwnd, CSubclassWnd* pSubclassWnd)
{
      ASSERT(hwnd && ::IsWindow(hwnd));

      // Add to front of list
      pSubclassWnd->m_pNext = Lookup(hwnd);
      SetAt(hwnd, pSubclassWnd);
      
      if (pSubclassWnd->m_pNext==NULL) {
            // If this is the first hook added, subclass the window
            pSubclassWnd->m_pOldWndProc =
                  (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)HookWndProc);

      } else {
            // just copy wndproc from next hook
            pSubclassWnd->m_pOldWndProc = pSubclassWnd->m_pNext->m_pOldWndProc;
      }
      ASSERT(pSubclassWnd->m_pOldWndProc);
}

//////////////////
// Remove hook from map
//
void CSubclassWndMap::Remove(CSubclassWnd* pUnHook)
{
      HWND hwnd = pUnHook->m_hWnd;
      ASSERT(hwnd && ::IsWindow(hwnd));

      CSubclassWnd* pHook = Lookup(hwnd);
      ASSERT(pHook);
      if (pHook==pUnHook) {
            // hook to remove is the one in the hash table: replace w/next
            if (pHook->m_pNext)
                  SetAt(hwnd, pHook->m_pNext);
            else {
                  // This is the last hook for this window: restore wnd proc
                  RemoveKey(hwnd);
                  SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)pHook->m_pOldWndProc);
            }
      } else {
            // Hook to remove is in the middle: just remove from linked list
            while (pHook->m_pNext!=pUnHook)
                  pHook = pHook->m_pNext;
            ASSERT(pHook && pHook->m_pNext==pUnHook);
            pHook->m_pNext = pUnHook->m_pNext;
      }
}

//////////////////
// Remove all the hooks for a window
//
void CSubclassWndMap::RemoveAll(HWND hwnd)
{
      CSubclassWnd* pSubclassWnd;
      while ((pSubclassWnd = Lookup(hwnd))!=NULL)
            pSubclassWnd->HookWindow((HWND)NULL);      // (unhook)
}

/////////////////
// Find first hook associate with window
//
CSubclassWnd* CSubclassWndMap::Lookup(HWND hwnd)
{
      CSubclassWnd* pFound = NULL;
      if (!CMapPtrToPtr::Lookup(hwnd, (void*&)pFound))
            return NULL;
      ASSERT_KINDOF(CSubclassWnd, pFound);
      return pFound;
}


///// TestHarness.cpp : implementation file
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "StatusMonitor.h"
#include "TestHarness.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
      CAboutDlg();

// Dialog Data
      //{{AFX_DATA(CAboutDlg)
      enum { IDD = IDD_ABOUTBOX };
      //}}AFX_DATA

      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CAboutDlg)
      protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
      //}}AFX_VIRTUAL

// Implementation
protected:
      //{{AFX_MSG(CAboutDlg)
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};


#define WM_MY_TRAY_NOTIFICATION WM_USER+0


CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
      //{{AFX_DATA_INIT(CAboutDlg)
      //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
      CDialog::DoDataExchange(pDX);
      //{{AFX_DATA_MAP(CAboutDlg)
      //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
      //{{AFX_MSG_MAP(CAboutDlg)
            // No message handlers
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTestHarness dialog

CTestHarness::CTestHarness(CWnd* pParent /*=NULL*/)
            : CDialog(CTestHarness::IDD, pParent)
{
      //{{AFX_DATA_INIT(CTestHarness)
      m_blnShowBalloon = TRUE;
      //}}AFX_DATA_INIT
      // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
      m_hIcon = AfxGetApp()->LoadIcon(IDI_MAINFRAME);
      
      m_trafficlights = new CTrafficLightAPI(IDI_GREEN);
      m_trafficlights->SetIcon(IDI_GREEN);
}

void CTestHarness::DoDataExchange(CDataExchange* pDX)
{
      CDialog::DoDataExchange(pDX);
      //{{AFX_DATA_MAP(CTestHarness)
      DDX_Check(pDX, IDC_SHOW_BALLOON, m_blnShowBalloon);
      //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTestHarness, CDialog)
      //{{AFX_MSG_MAP(CTestHarness)
      ON_WM_SYSCOMMAND()
      ON_WM_PAINT()
      ON_WM_QUERYDRAGICON()
      ON_BN_CLICKED(IDC_RED, OnRed)
      ON_BN_CLICKED(IDC_AMBER, OnAmber)
      ON_BN_CLICKED(IDC_GREEN, OnGreen)
      ON_BN_CLICKED(IDC_SHOW_BALLOON, OnShowBalloon)
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTestHarness message handlers

BOOL CTestHarness::OnInitDialog()
{
      CDialog::OnInitDialog();

      // Add "About..." menu item to system menu.

      // IDM_ABOUTBOX must be in the system command range.
      ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
      ASSERT(IDM_ABOUTBOX < 0xF000);

      CMenu* pSysMenu = GetSystemMenu(FALSE);
      if (pSysMenu != NULL)
      {
            CString strAboutMenu;
            strAboutMenu.LoadString(IDS_ABOUTBOX);
            if (!strAboutMenu.IsEmpty())
            {
                  pSysMenu->AppendMenu(MF_SEPARATOR);
                  pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
            }
      }

      // Set the icon for this dialog.  The framework does this automatically
      //  when the application's main window is not a dialog
      SetIcon(m_hIcon, TRUE);                  // Set big icon
      SetIcon(m_hIcon, FALSE);            // Set small icon
      
      // TODO: Add extra initialization here
      
      return TRUE;  // return TRUE  unless you set the focus to a control
}

void CTestHarness::OnSysCommand(UINT nID, LPARAM lParam)
{
      if ((nID & 0xFFF0) == IDM_ABOUTBOX)
      {
            CAboutDlg dlgAbout;
            dlgAbout.DoModal();
      }
      else
      {
            CDialog::OnSysCommand(nID, lParam);
      }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CTestHarness::OnPaint()
{
      if (IsIconic())
      {
            CPaintDC dc(this); // device context for painting

            SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

            // Center icon in client rectangle
            int cxIcon = GetSystemMetrics(SM_CXICON);
            int cyIcon = GetSystemMetrics(SM_CYICON);
            CRect rect;
            GetClientRect(&rect);
            int x = (rect.Width() - cxIcon + 1) / 2;
            int y = (rect.Height() - cyIcon + 1) / 2;

            // Draw the icon
            dc.DrawIcon(x, y, m_hIcon);
      }
      else
      {
            CDialog::OnPaint();
      }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CTestHarness::OnQueryDragIcon()
{
      return (HCURSOR) m_hIcon;
}


void CTestHarness::OnRed()
{
      m_trafficlights->SetIcon(IDI_RED);
      if (m_blnShowBalloon)
            m_trafficlights->ShowBalloonTip("Network connection failed","System Failure",10, NIIF_WARNING);
}

void CTestHarness::OnAmber()
{
      m_trafficlights->SetIcon(IDI_AMBER);
      
}

void CTestHarness::OnGreen()
{
      m_trafficlights->SetIcon(IDI_GREEN);      
}

void CTestHarness::OnShowBalloon()
{
      UpdateData(TRUE);
      
}



////// StatusMonitor.cpp : Defines the class behaviors for the application.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "StatusMonitor.h"
#include "TestHarness.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


// CStatusMonitorApp

BEGIN_MESSAGE_MAP(CStatusMonitorApp, CWinApp)
      //{{AFX_MSG_MAP(CStatusMonitorApp)
            // NOTE - the ClassWizard will add and remove mapping macros here.
            //    DO NOT EDIT what you see in these blocks of generated code!
      //}}AFX_MSG
      ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CStatusMonitorApp construction

CStatusMonitorApp::CStatusMonitorApp()
{
      // TODO: add construction code here,
      // Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CStatusMonitorApp object

CStatusMonitorApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CStatusMonitorApp initialization

BOOL CStatusMonitorApp::InitInstance()
{
      AfxEnableControlContainer();

      // Standard initialization
      // If you are not using these features and wish to reduce the size
      //  of your final executable, you should remove from the following
      //  the specific initialization routines you do not need.

#ifdef _AFXDLL
      Enable3dControls();                  // Call this when using MFC in a shared DLL
#else
      Enable3dControlsStatic();      // Call this when linking to MFC statically
#endif

      CTestHarness dlg;
      m_pMainWnd = &dlg;
      int nResponse = dlg.DoModal();
      if (nResponse == IDOK)
      {
            // TODO: Place code here to handle when the dialog is
            //  dismissed with OK
      }
      else if (nResponse == IDCANCEL)
      {
            // TODO: Place code here to handle when the dialog is
            //  dismissed with Cancel
      }

      // Since the dialog has been closed, return FALSE so that we exit the
      //  application, rather than start the application's message pump.
      return FALSE;
}



0
 

Author Comment

by:kullyN
Comment Utility
Im increasing the points on this as the problem poses more difficulty!
0
 

Author Comment

by:kullyN
Comment Utility
On your last comment, jkr:

I put break points around this function and I run the application when I hover over the trayicon - it still disappears and this piece of code is not broken into.
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Introduction: Database storage, where is the exe actually on the disc? Playing a game selected randomly (how to generate random numbers).  Error trapping with try..catch to help the code run even if something goes wrong. Continuing from the seve…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now