Figure 1   CTrayIcon

 ////////////////////////////////////////////////////////////////
// TRAYTEST 1996 Microsoft Systems Journal.
// See TRAYTEST.CPP for description of program.
//
#include "resource.h"

class CMyApp : public CWinApp {
public:
virtual BOOL InitInstance();
//{{AFX_MSG(CMyApp)
afx_msg void OnAppAbout();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

TRAYTEST.CPP

 ////////////////////////////////////////////////////////////////
// TRAYTEST 1996 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// TRAYTEST illustrates how to use CTrayIcon.
// All the activity takes place in MainFrm.cpp.

#include "stdafx.h"
#include "TrayTest.h"
#include "mainfrm.h"

CMyApp theApp;

BEGIN_MESSAGE_MAP(CMyApp, CWinApp)
//{{AFX_MSG_MAP(CMyApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CMyApp::InitInstance()
{
// Create main frame window (don't use doc/view stuff)
//
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
pMainFrame->ShowWindow(SW_HIDE);
pMainFrame->UpdateWindow();
m_pMainWnd = pMainFrame;
OnAppAbout();
return TRUE;
}

void CMyApp::OnAppAbout()
{
CDialog(IDD_ABOUTBOX).DoModal();
}

MAINFRM.H

 #include "trayicon.h"

//////////////////
// Main frame for TRAYTEST.
//
class CMainFrame : public CFrameWnd {
public:
CMainFrame();
virtual ~CMainFrame();
protected:
DECLARE_DYNAMIC(CMainFrame)
CStatusBar m_wndStatusBar;

CTrayIcon m_trayIcon; // my tray icon
CEdit m_wndEdit; // to display tray notifications
int m_iWhichIcon; // 0/1 which HICON to use
BOOL m_bShutdown; // OK to terminate TRAYTEST
BOOL m_bShowTrayNotifications; // display info in main window

//{{AFX_MSG(CMainFrame)
afx_msg LRESULT OnTrayNotification(WPARAM wp, LPARAM lp);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnToggleIcon();
afx_msg void OnViewClear();
afx_msg void OnViewNotifications();
afx_msg void OnUpdateViewClear(CCmdUI* pCmdUI);
afx_msg void OnUpdateViewNotifications(CCmdUI* pCmdUI);
afx_msg void OnClose();
afx_msg void OnAppOpen();
afx_msg void OnAppSuspend();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

MAINFRM.CPP

 ////////////////////////////////////////////////////////////////
// Main frame window implementation
//
#include "stdafx.h"
#include "TrayTest.h"
#include "mainfrm.h"

// Message ID used for tray notifications
#define WM_MY_TRAY_NOTIFICATION WM_USER+0

IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_MESSAGE(WM_MY_TRAY_NOTIFICATION, OnTrayNotification)
ON_WM_CREATE()
ON_COMMAND(ID_VIEW_CLEAR, OnViewClear)
ON_COMMAND(ID_TOGGLE_ICON, OnToggleIcon)
ON_COMMAND(ID_VIEW_NOTIFICATIONS, OnViewNotifications)
ON_UPDATE_COMMAND_UI(ID_VIEW_CLEAR, OnUpdateViewClear)
ON_UPDATE_COMMAND_UI(ID_VIEW_NOTIFICATIONS, OnUpdateViewNotifications)
ON_WM_CLOSE()
ON_COMMAND(ID_APP_OPEN, OnAppOpen)
ON_COMMAND(ID_APP_SUSPEND, OnAppSuspend)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

static UINT BASED_CODE indicators[] = {
ID_SEPARATOR, // status line indicator
};

CMainFrame::CMainFrame() : m_trayIcon(IDR_TRAYICON)
{
m_bShowTrayNotifications = TRUE;
m_bShutdown = FALSE;
}

CMainFrame::~CMainFrame()
{
}

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

if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
return -1; // fail to create

// Create child edit control for displaying messages
CRect rc;
if (!m_wndEdit.Create(
WS_VISIBLE|WS_CHILD|WS_VSCROLL|ES_MULTILINE|ES_READONLY, rc, this,
AFX_IDW_PANE_FIRST))

return -1;

// Set up tray icon
m_trayIcon.SetNotificationWnd(this, WM_MY_TRAY_NOTIFICATION);
m_iWhichIcon = 1;
m_trayIcon.SetIcon(IDI_MYICON);

return 0;
}

//////////////////
// Close window. Unless we are shutting down, just hide it.
//
void CMainFrame::OnClose()
{
if (m_bShutdown)
CFrameWnd::OnClose();
else
ShowWindow(SW_HIDE);
}

//////////////////
// Handle notification from tray icon: display a message.
//
LRESULT CMainFrame::OnTrayNotification(WPARAM uID, LPARAM lEvent)
{
if (m_bShowTrayNotifications) {
static LPCSTR MouseMessages[] = { "WM_MOUSEMOVE",
"WM_LBUTTONDOWN", "WM_LBUTTONUP", "WM_LBUTTONDBLCLK",
"WM_RBUTTONDOWN", "WM_RBUTTONUP", "WM_RBUTTONDBLCLK",
"WM_MBUTTONDOWN", "WM_MBUTTONUP", "WM_MBUTTONDBLCLK" };

CString s;
s.Format("Tray notification: ID=%d, lEvent=0x%04x %s\r\n",
uID, lEvent, WM_MOUSEFIRST<=lEvent && lEvent<=WM_MOUSELAST ?
MouseMessages[lEvent-WM_MOUSEFIRST] : "(Unknown)");

m_wndEdit.SetSel(-1, -1); // end of edit text
m_wndEdit.ReplaceSel(s); // append string..
m_wndEdit.SendMessage(EM_SCROLLCARET); // ..and make visible
}

// let tray icon do default stuff
return m_trayIcon.OnTrayNotification(uID, lEvent);
}

////////////////////////////////////////////////////////////////
// Command handlers below.
//
void CMainFrame::OnViewClear()
{
m_wndEdit.SetWindowText("");
}

void CMainFrame::OnUpdateViewClear(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_wndEdit.GetLineCount() > 1 || m_wndEdit.LineLength() > 0);
}

void CMainFrame::OnToggleIcon()
{
m_iWhichIcon=!m_iWhichIcon;
m_trayIcon.SetIcon(m_iWhichIcon ? IDI_MYICON : IDI_MYICON2);
}

void CMainFrame::OnViewNotifications()
{
m_bShowTrayNotifications = !m_bShowTrayNotifications;
}

void CMainFrame::OnUpdateViewNotifications(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_bShowTrayNotifications);
}

void CMainFrame::OnAppOpen()
{
ShowWindow(SW_NORMAL);
SetForegroundWindow();
}

void CMainFrame::OnAppSuspend()
{
m_bShutdown = TRUE; // really exit
SendMessage(WM_CLOSE);
}

TRAYICON.H

 ////////////////////////////////////////////////////////////////
// CTrayIcon Copyright 1996 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

////////////////
// CTrayIcon manages an icon in the Windows 95 system tray.
//
class CTrayIcon : public CCmdTarget {
protected:
DECLARE_DYNAMIC(CTrayIcon)
NOTIFYICONDATA m_nid; // struct for Shell_NotifyIcon args

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, LPCSTR lpTip);
BOOL SetIcon(LPCTSTR lpResName, LPCSTR lpTip)
{ return SetIcon(lpResName ?
AfxGetApp()->LoadIcon(lpResName) : NULL, lpTip); }
BOOL SetStandardIcon(LPCTSTR lpszIconName, LPCSTR lpTip)
{ return SetIcon(::LoadIcon(NULL, lpszIconName), lpTip); }

virtual LRESULT OnTrayNotification(WPARAM uID, LPARAM lEvent);
};

#endif

TRAYICON.CPP

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

#include "stdafx.h"
#include "trayicon.h"
#include <afxpriv.h> // for AfxLoadString

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

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

//////////////////
// 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, LPCSTR 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)
strncpy(m_nid.szTip, lpTip, sizeof(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;
}

/////////////////
// Default event handler handles right-menu and doubleclick.
// Call this function from your own notification handler.
//
LRESULT CTrayIcon::OnTrayNotification(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);

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

return 1; // handled
}

Figure 6   CWindowPlacement

WINPLACE.H

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

////////////////
// CWindowPlacement reads and writes WINDOWPLACEMENT 
// from/to application profile and CArchive.
//
struct CWindowPlacement : public WINDOWPLACEMENT {
   CWindowPlacement();
   ~CWindowPlacement();
   
   // Read/write to app profile
   void GetProfileWP(LPCSTR lpKeyName);
   void WriteProfileWP(LPCSTR lpKeyName);

   // Save/restore window pos (from app profile)
   void Save(LPCSTR lpKeyName, CWnd* pWnd);
   BOOL Restore(LPCSTR lpKeyName, CWnd* pWnd);

   // Save/restore from archive
   friend CArchive& operator<<(CArchive& ar, const CWindowPlacement& wp);
   friend CArchive& operator>>(CArchive& ar, CWindowPlacement& wp);
};

WINPLACE.CPP

 ////////////////////////////////////////////////////////////////
// CWindowPlacement 1996 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
#include "stdafx.h"
#include "winplace.h"

CWindowPlacement::CWindowPlacement()
{
   // Note: "length" is inherited from WINDOWPLACEMENT
   length = sizeof(WINDOWPLACEMENT);
}

CWindowPlacement::~CWindowPlacement()
{
}

//////////////////
// Restore window placement from profile key
BOOL CWindowPlacement::Restore(LPCSTR lpKeyName, CWnd* pWnd)
{
   GetProfileWP(lpKeyName);

   // Only restore if window intersets the screen.
   //
   CRect rcTemp, rcScreen(0,0,GetSystemMetrics(SM_CXSCREEN),
      GetSystemMetrics(SM_CYSCREEN));
   if (!::IntersectRect(&rcTemp, &rcNormalPosition, &rcScreen))
      return FALSE;

   pWnd->SetWindowPlacement(this);  // set placement
   return TRUE;
}

//////////////////
// Get window placement from profile.
void CWindowPlacement::GetProfileWP(LPCSTR lpKeyName)
{
   CWinApp *pApp = AfxGetApp();
   ASSERT_VALID(pApp);

   showCmd = pApp->GetProfileInt(lpKeyName, "wp.showCmd", showCmd);
   flags   = pApp->GetProfileInt(lpKeyName, "wp.flags", flags);

ptMinPosition.x = pApp->GetProfileInt(lpKeyName, "wp.ptMinPosition.x", 
      ptMinPosition.x);
ptMinPosition.y = pApp->GetProfileInt(lpKeyName, "wp.ptMinPosition.y",
      ptMinPosition.y);
ptMaxPosition.x = pApp->GetProfileInt(lpKeyName, "wp.ptMaxPosition.x", 
      ptMaxPosition.x);
ptMaxPosition.y = pApp->GetProfileInt(lpKeyName, "wp.ptMaxPosition.y",
      ptMaxPosition.y);

   RECT& rc = rcNormalPosition;  // because I hate typing
   rc.left  = pApp->GetProfileInt(lpKeyName, "wp.left",   rc.left);
   rc.right = pApp->GetProfileInt(lpKeyName, "wp.right",  rc.right);
   rc.top   = pApp->GetProfileInt(lpKeyName, "wp.top",    rc.top);
   rc.bottom= pApp->GetProfileInt(lpKeyName, "wp.bottom", rc.bottom);
}

////////////////
// Save window placement in app profile
void CWindowPlacement::Save(LPCSTR lpKeyName, CWnd* pWnd)
{
   pWnd->GetWindowPlacement(this);
   WriteProfileWP(lpKeyName);
}

//////////////////
// Write window placement to app profile
void CWindowPlacement::WriteProfileWP(LPCSTR lpKeyName)
{
   CWinApp *pApp = AfxGetApp();
   ASSERT_VALID(pApp);
   pApp->WriteProfileInt(lpKeyName, "wp.showCmd",         showCmd);
   pApp->WriteProfileInt(lpKeyName, "wp.flags",           flags);
   pApp->WriteProfileInt(lpKeyName, "wp.ptMinPosition.x", ptMinPosition.x);
   pApp->WriteProfileInt(lpKeyName, "wp.ptMinPosition.y", ptMinPosition.y);
   pApp->WriteProfileInt(lpKeyName, "wp.ptMaxPosition.x", ptMaxPosition.x);
   pApp->WriteProfileInt(lpKeyName, "wp.ptMaxPosition.y", ptMaxPosition.y);
   pApp->WriteProfileInt(lpKeyName, "wp.left",  rcNormalPosition.left);
   pApp->WriteProfileInt(lpKeyName, "wp.right", rcNormalPosition.right);
   pApp->WriteProfileInt(lpKeyName, "wp.top",   rcNormalPosition.top);
   pApp->WriteProfileInt(lpKeyName, "wp.bottom",rcNormalPosition.bottom);
}

// The ugly casts are required to help the VC++ 3.0 compiler decide which
// operator<< or operator>> to use. If you're using VC++ 4.0 or later, you 
// can delete this stuff.
//
#if (_MSC_VER < 1000)      // 1000 = VC++ 4.0
#define UINT_CAST (LONG)
#define UINT_CASTREF (LONG&)
#else
#define UINT_CAST
#define UINT_CASTREF
#endif

//////////////////
// Write window placement to archive
// WARNING: archiving functions are untested.
CArchive& operator<<(CArchive& ar, const CWindowPlacement& wp)
{
   ar << UINT_CAST wp.length;
   ar << UINT_CAST wp.flags;
   ar << UINT_CAST wp.showCmd;
   ar << wp.ptMinPosition;
   ar << wp.ptMaxPosition;
   ar << wp.rcNormalPosition;
   return ar;
}

//////////////////
// Read window placement from archive
// WARNING: archiving functions are untested.
CArchive& operator>>(CArchive& ar, CWindowPlacement& wp)
{
   ar >> UINT_CASTREF wp.length;
   ar >> UINT_CASTREF wp.flags;
   ar >> UINT_CASTREF wp.showCmd;
   ar >> wp.ptMinPosition;
   ar >> wp.ptMaxPosition;
   ar >> wp.rcNormalPosition;
   return ar;
}

Figure 7   TWMOD.CPP

 ///////////////////////////////////////////////////////////////////////////
// Changes to TRACEWIN that implement saving/restoring the window position.

// (From MainFrm.h)
class CMainFrame : public CFrameWnd {
public:
   static const char* REGKEY; // registry key for saved settings
                                     .
                                     .
                                     .
};

// (From MainFrm.cpp, TRACEWIN's main window)
const char* CMainFrame::REGKEY = "Settings";
void CMainFrame::OnClose() 
{
   // Save current settings in registry
   CWindowPlacement wp;
   wp.Save(REGKEY, this);
                                     .
                                     . (Save other stuff too)
                                     .
CFrameWnd::OnClose();
}

// (From App.cpp, TRACEWIN's app object)
BOOL CApp::InitInstance()
{
   // Save settings in registry, not INI file
   SetRegistryKey("MSJ");
                                     .
                                     . Create main window
                                     .
// Load window placement from profile
   CWindowPlacement wp;
   if (!wp.Restore(CMainFrame::REGKEY, pMainFrame))
      pMainFrame->ShowWindow(m_nCmdShow);
                                     .
                                     .
                                     .
   return TRUE;
}