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