Figure 3   CMainFrame

MainFrm.h


////////////////////////////////////////////////////////////////
// GRAYSUB 1998 Microsoft Systems Journal. 
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//

////////////////
// Standard MFC main frame window
//
class CMainFrame : public CFrameWnd {
public:
    virtual ~CMainFrame();
protected:
    BOOL m_bEnableSubmenu;
    BOOL m_bEnableFoo;

    CMainFrame();
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

    afx_msg int  OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnEnableSubmenu();
    afx_msg void OnUpdateEnableSubmenu(CCmdUI *pCmdUI);
    afx_msg void OnFoo();
    afx_msg void OnUpdateFoo(CCmdUI *pCmdUI);
    afx_msg void OnEnableFoo();
    afx_msg void OnUpdateEnableFoo(CCmdUI *pCmdUI);
    DECLARE_MESSAGE_MAP()
    DECLARE_DYNCREATE(CMainFrame)
};
MainFrm.cpp

////////////////////////////////////////////////////////////////
// GRAYSUB 1998 Microsoft Systems Journal. 
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// GRAYSUB illustrates how to gray a submenu item. 
// Compiles with Visual C++ 5.0 or later, under Windows 95.
//
#include "StdAfx.h"
#include "GraySub.h"
#include "MainFrm.h"

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

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_WM_CREATE()
    ON_COMMAND(ID_ENABLE_SUBMENU, OnEnableSubmenu)
    ON_UPDATE_COMMAND_UI(ID_ENABLE_SUBMENU, OnUpdateEnableSubmenu)
    ON_COMMAND(ID_FOO, OnFoo)
    ON_UPDATE_COMMAND_UI(ID_FOO, OnUpdateFoo)
    ON_COMMAND(ID_ENABLE_FOO, OnEnableFoo)
    ON_UPDATE_COMMAND_UI(ID_ENABLE_FOO, OnUpdateEnableFoo)
END_MESSAGE_MAP()

CMainFrame::CMainFrame()
{
    m_bEnableSubmenu = FALSE;
    m_bEnableFoo    = FALSE;
}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
    return 0;
}

//////////////////
// Set initial window size to 200 x 200
//
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    BOOL bRet = CFrameWnd::PreCreateWindow(cs);
    cs.cx = cs.cy = 200;
    return bRet;
}

//////////////////
// Enable/Disable the submenu
//
void CMainFrame::OnEnableSubmenu()
{
    m_bEnableSubmenu = !m_bEnableSubmenu;
}

//////////////////
// Set text for File | Enable/Disable Submenu
//
void CMainFrame::OnUpdateEnableSubmenu(CCmdUI *pCmdUI)
{
    pCmdUI->SetText(m_bEnableSubmenu ?
        _T("&Disable Submenu") : _T("&Enable Submenu"));
}

//////////////////
// Handle File | Submenu | Foo
//
void CMainFrame::OnFoo()
{
    AfxMessageBox(_T("Foo to you too."));
}

//////////////////
// Update handler for File | Submenu | Foo handles updating
// of the command and the submenu item itself.
//
void CMainFrame::OnUpdateFoo(CCmdUI *pCmdUI)
{
    if (pCmdUI->m_pSubMenu) {
        // update submenu item (File | Submenu)
        UINT fGray = m_bEnableSubmenu ? 0 : MF_GRAYED;
        pCmdUI->m_pMenu->EnableMenuItem(pCmdUI->m_nIndex,
            MF_BYPOSITION|fGray);
    } else
        // update command item (File | Submenu | Foo)
        pCmdUI->Enable(m_bEnableFoo);
}

/////////////////
// Enable/Disable the File | Submenu | Foo command
//
void CMainFrame::OnEnableFoo()
{
    m_bEnableFoo = !m_bEnableFoo;
}

/////////////////
// Set text for File | Submenu | Enable/Disable Foo
//
void CMainFrame::OnUpdateEnableFoo(CCmdUI *pCmdUI)
{
    pCmdUI->SetText(m_bEnableFoo ?
        _T("&Disable Foo") : _T("&Enable Foo"));
}

Figure 4   CTrayIcon

TrayIcon.h


////////////////////////////////////////////////////////////////
// CTrayIcon 1999 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.

#ifndef _TRAYICON_H
#define _TRAYICON_H

#include "Subclass.h"

////////////////
// CTrayIcon manages an icon in the Windows 95 system tray. 
// The sample program TRAYTEST shows how to use it.
// 
class CTrayIcon : public CCmdTarget {
public:
   CTrayIcon(UINT uID);
   ~CTrayIcon();

   // Call this to receive tray notifications
   void SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg);

   // SetIcon functions. To remove icon, call SetIcon(0)
   //
   BOOL SetIcon(UINT uID); // main variant you want to use
   BOOL SetIcon(HICON hicon, LPCTSTR lpTip);
   BOOL SetIcon(LPCTSTR lpResName, LPCTSTR lpTip)
      { return SetIcon(lpResName ? 
         AfxGetApp()->LoadIcon(lpResName) : NULL, lpTip); }
   BOOL SetStandardIcon(LPCTSTR lpszIconName, LPCTSTR lpTip)
      { return SetIcon(::LoadIcon(NULL, lpszIconName), lpTip); }

// Following is obsolete. CTrayIcon does default handling automatically.
// virtual LRESULT OnTrayNotification(WPARAM uID, LPARAM lEvent);
   virtual LRESULT OnTrayNotify(WPARAM uID, LPARAM lEvent);
   virtual LRESULT OnTaskBarCreate(WPARAM wp, LPARAM lp);

protected:
   NOTIFYICONDATA m_nid;        // struct for Shell_NotifyIcon args

   // private class used to hook tray notification and taskbarcreated
   class CTrayHook : public CSubclassWnd {
   private:
      CTrayIcon* m_pTrayIcon;
      virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
      friend CTrayIcon;
   };
   friend CTrayHook;
   CTrayHook m_notifyHook; // trap tray notifications
   CTrayHook m_parentHook; // trap taskbarcreated message
   DECLARE_DYNAMIC(CTrayIcon)
};

#endif
TrayIcon.cpp

//////////////////////////////////////////////////////////////////
// 1999 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 on Windows 95
//
#include "stdafx.h"
#include "trayicon.h"
#include <afxpriv.h>    // for AfxLoadString

#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. CTrayIcon responds by
// re-installing its icon.
//
const UINT WM_TASKBARCREATED = ::RegisterWindowMessage(_T("TaskbarCreated"));

IMPLEMENT_DYNAMIC(CTrayIcon, CCmdTarget)

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
}

//////////////////
// Set notification window. It must be created already.
//
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);
}

//////////////////
// 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 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);
}

//////////////////
// Common SetIcon for all overloads. 
//
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;
}

//////////////////
// Same hook class used for both notification window and top-level
// parent; hook function determines which.
//
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_TASKBARCREATED) {
        m_pTrayIcon->OnTaskBarCreate(wp, lp);
    }
    return CSubclassWnd::WindowProc(msg, wp, lp);
}

/////////////////
// Default event handler handles right-menu and doubleclick.
// Override to do something different.
//
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 interpret 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"
        // 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);

    } 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 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;
}