• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 873
  • Last Modified:

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

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
kullyN
Asked:
kullyN
  • 7
  • 4
1 Solution
 
jkrCommented:
You need to handle the callback message that is registered when passing NOTIFYICONDATA::uCallbackMessage to 'Shell_NotifyIcon()', otherwise the icon will be removed.
0
 
jkrCommented:
BTW, see also http://www.codeproject.com/shell/StealthDialog.asp ("Basic use of Shell_NotifyIcon in Win32")
0
 
kullyNAuthor Commented:
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
Cloud Class® Course: C++ 11 Fundamentals

This course will introduce you to C++ 11 and teach you about syntax fundamentals.

 
jkrCommented:
When you handle the callback message, you should return 0 and not pass the message on to the default handler.
0
 
kullyNAuthor Commented:
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
 
kullyNAuthor Commented:
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
 
jkrCommented:
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
 
kullyNAuthor Commented:
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
 
kullyNAuthor Commented:
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
 
kullyNAuthor Commented:
Im increasing the points on this as the problem poses more difficulty!
0
 
kullyNAuthor Commented:
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
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 7
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now