Code/Resource
Windows Develop
Linux-Unix program
Internet-Socket-Network
Web Server
Browser Client
Ftp Server
Ftp Client
Browser Plugins
Proxy Server
Email Server
Email Client
WEB Mail
Firewall-Security
Telnet Server
Telnet Client
ICQ-IM-Chat
Search Engine
Sniffer Package capture
Remote Control
xml-soap-webservice
P2P
WEB(ASP,PHP,...)
TCP/IP Stack
SNMP
Grid Computing
SilverLight
DNS
Cluster Service
Network Security
Communication-Mobile
Game Program
Editor
Multimedia program
Graph program
Compiler program
Compress-Decompress algrithms
Crypt_Decrypt algrithms
Mathimatics-Numerical algorithms
MultiLanguage
Disk/Storage
Java Develop
assembly language
Applications
Other systems
Database system
Embeded-SCM Develop
FlashMX/Flex
source in ebook
Delphi VCL
OS Develop
MiddleWare
MPI
MacOS develop
LabView
ELanguage
Software/Tools
E-Books
Artical/Document
cscst.cpp
Package: shell.rar [view]
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 129k
Category:
Windows Kernel
Development Platform:
Visual C++
- //+-------------------------------------------------------------------------
- //
- // Microsoft Windows
- //
- // Copyright (C) Microsoft Corporation, 1997 - 1999
- //
- // File: cscst.cpp
- //
- //--------------------------------------------------------------------------
- #include "pch.h"
- #pragma hdrstop
- #include <shellp.h> // STR_DESKTOPCLASS
- #ifdef REPORT_DEVICE_CHANGES
- # include <dbt.h> // Device change notifications.
- #endif // REPORT_DEVICE_CHANGES
- #include <sddl.h> // For ConvertStringSidToSid
- #include "cscst.h"
- #include "options.h"
- #include "statdlg.h" // CStatusDlg
- #include "uihooks.h" // Self-host notifications
- #include "folder.h"
- #include "eventlog.h"
- #include "msg.h"
- #include "purge.h"
- #include "security.h"
- #if DBG
- //
- // This code is used to manage the hidden window when we
- // unhide it and display debug output to it via STDBGOUT().
- //
- #include <commdlg.h>
- #include <stdarg.h>
- const TCHAR c_szSysTrayOutput[] = TEXT("SysTrayOutput");
- int STDebugLevel(void);
- void STDebugOnLogEvent(HWND hwndList, LPCTSTR pszText);
- void STDebugSaveListboxContent(HWND hwndParent);
- DWORD STDebugOpenNetCacheKey(DWORD dwAccess, HKEY *phkey);
- #endif // DBG
- //
- // Size of systray icons.
- //
- #define CSC_ICON_CX 16
- #define CSC_ICON_CY 16
- //
- // Timer IDs are arbitrary.
- //
- #define ID_TIMER_FLASHICON 2953
- #define ID_TIMER_REMINDER 2954
- #define ID_TIMER_STATECHANGE 2955
- // Prototypes
- void ApplyAdminFolderPolicy(void); // in admin.cpp
- void _RefreshAllExplorerWindows(LPCTSTR pszServer);
- // Globals
- static HWND g_hWndNotification = NULL;
- extern HWND g_hwndStatusDlg; // in statdlg.cpp
- HANDLE g_hToken = NULL;
- #ifdef REPORT_DEVICE_CHANGES
- HDEVNOTIFY g_hDevNotify = NULL;
- #endif // REPORT_DEVICE_CHANGES
- //
- // RAS Autodial API.
- //
- typedef BOOL (WINAPI * PFNHLPNBCONNECTION)(LPCTSTR);
- #if DBG
- //
- // Provide some text-form names for state and input values
- // to support debug output. The order of these corresponds
- // to the STS_XXXXX enumeration.
- //
- LPCTSTR g_pszSysTrayStates[] = { TEXT("STS_INVALID"),
- TEXT("STS_ONLINE"),
- TEXT("STS_DIRTY"),
- TEXT("STS_MDIRTY"),
- TEXT("STS_SERVERBACK"),
- TEXT("STS_MSERVERBACK"),
- TEXT("STS_OFFLINE"),
- TEXT("STS_MOFFLINE"),
- TEXT("STS_NONET") };
- //
- // A simple function to translate a state value to a string.
- //
- LPCTSTR SysTrayStateStr(eSysTrayState s)
- {
- return g_pszSysTrayStates[int(s)];
- }
- #endif
- //
- // A simple dynamic list of server names. A name can be provided
- // as either a "\server" or "\servershare" and only the server
- // part "\server" is stored.
- //
- class CServerList
- {
- public:
- CServerList(void)
- : m_hdpa(DPA_Create(10)) { }
- ~CServerList(void);
- bool Add(LPCTSTR pszServer);
- void Remove(LPCTSTR pszServer);
- void Clear(void);
- int Find(LPCTSTR pszServer);
- int Count(void) const;
- LPCTSTR Get(int iItem) const;
- bool Exists(LPCTSTR pszServer)
- { return -1 != Find(pszServer); }
- private:
- HDPA m_hdpa;
- void GetServerFromPath(LPCTSTR pszPath, LPTSTR pszServer, int cchServer);
- //
- // Prevent copy.
- //
- CServerList(const CServerList& rhs);
- CServerList& operator = (const CServerList& rhs);
- };
- //
- // The class that translates CSC agent input and cache status into a subsequent
- // systray UI state. Originally this was a table-driven state machine
- // (hence the name). It later proved sufficient to do a simple scan of cache
- // status and determine UI state based on the statistics obtained. The name
- // has been retained for lack of something better.
- //
- class CStateMachine
- {
- public:
- CStateMachine(bool bNoNet) : m_bNoNet(bNoNet) { }
- //
- // This is THE function for converting CSC agent input (or a
- // simple status check) into a systray icon state.
- //
- eSysTrayState TranslateInput(UINT uMsg, LPTSTR pszShare, UINT cchShare);
- void PingServers();
- bool ServerPendingReconnection(LPCTSTR pszServer)
- { return m_PendingReconList.Add(pszServer); }
- void ServerReconnected(LPCTSTR pszServer)
- { m_PendingReconList.Remove(pszServer); }
- void ServerUnavailable(LPCTSTR pszServer)
- { m_PendingReconList.Remove(pszServer); }
- void AllServersUnavailable(void)
- { m_PendingReconList.Clear(); }
- bool IsServerPendingReconnection(LPCTSTR pszServer)
- { return m_PendingReconList.Exists(pszServer); }
- private:
- CServerList m_PendingReconList;
- bool m_bNoNet;
- //
- // Some helper functions for decoding CSC share status values.
- //
- bool ShareIsOffline(DWORD dwCscStatus) const
- {
- return (0 != (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwCscStatus));
- }
- bool ShareHasFiles(LPCTSTR pszShare, bool *pbModified = NULL, bool *pbOpen = NULL) const;
- //
- // Prevent copy.
- //
- CStateMachine(const CStateMachine& rhs);
- CStateMachine& operator = (const CStateMachine& rhs);
- };
- //
- // The CSysTrayUI class encapsulates the manipulation of the systray icon
- // so that the rest of the CSCUI code is exposed to only a narrow interface
- // to the systray. It also maintains state information to control flashing
- // of the systray icon. All flashing processing is provided by this class.
- //
- class CSysTrayUI
- {
- public:
- ~CSysTrayUI(void);
- //
- // Set the state of the systray icon. This will only change the
- // icon if the state has changed. Therefore this function can be
- // called without worrying about excessive redundant updates to
- // the display.
- //
- bool SetState(eSysTrayState state, LPCTSTR pszServer = NULL);
- //
- // Retrieve the current "state" of the systray UI. The state
- // is one of the STS_XXXXX codes.
- //
- eSysTrayState GetState(void) const
- { return m_state; }
- //
- // Retrieve the server name to be used in CSCUI elements.
- // If the server name string is empty, that means there are
- // multiple servers in the given state.
- //
- LPCTSTR GetServerName(void) const
- { return m_szServer; }
- //
- // Show the balloon text for the current systray state.
- //
- void ShowReminderBalloon(void);
- //
- // Reset the reminder timer.
- //
- void ResetReminderTimer(bool bRestart);
- //
- // Make any adjustments when a WM_WININICHANGE is received.
- //
- void OnWinIniChange(LPCTSTR pszSection);
- //
- //
- // Get a reference to THE singleton instance.
- //
- static CSysTrayUI& GetInstance(void);
- private:
- //
- // A minimal autoptr class to ensure the singleton instance
- // is deleted.
- //
- class autoptr
- {
- public:
- autoptr(void)
- : m_ptr(NULL) { }
- ~autoptr(void)
- { delete m_ptr; }
- CSysTrayUI* Get(void) const
- { return m_ptr; }
- void Set(CSysTrayUI *p)
- { delete m_ptr; m_ptr = p; }
- private:
- CSysTrayUI *m_ptr;
- autoptr(const autoptr& rhs);
- autoptr& operator = (const autoptr& rhs);
- };
- //
- // Icon info maintained for each UI state.
- //
- struct IconInfo
- {
- HICON hIcon; // Handle to icon to display in this state.
- UINT idIcon; // ID of icon to display in this state.
- int iFlashTimeout; // 0 == No icon flash. Time is in millisec.
- };
- //
- // Info maintained to describe the various balloon text messages.
- // Combination of state and dwTextFlags are the table keys.
- //
- struct BalloonInfo
- {
- eSysTrayState state; // SysTray state value.
- DWORD dwTextFlags; // BTF_XXXXX flags.
- DWORD dwInfoFlags; // NIIF_XXXXX flag.
- UINT idHeader; // Res id for header part.
- UINT idStatus; // Res id for status part.
- UINT idBody; // Res id for body part.
- UINT idDirective; // Res id for directive part.
- };
- //
- // Info maintained to describe the various tooltip text messages.
- //
- struct TooltipInfo
- {
- eSysTrayState state; // SysTray state value.
- UINT idTooltip; // Tooltip text resource ID.
- };
- //
- // Info maintained for special-case supression of systray balloons.
- // There are some state transitions that shouldn't generate a balloon.
- // This structure describes each entry in an array of supression info.
- //
- struct BalloonSupression
- {
- eSysTrayState stateFrom; // Transitioning from this state.
- eSysTrayState stateTo; // Transitioning to this state.
- };
- //
- // Enumeration for controlling what's done to the systray on update.
- //
- enum eUpdateFlags { UF_ICON = 0x00000001, // Update the icon.
- UF_FLASHICON = 0x00000002, // Flash the icon.
- UF_BALLOON = 0x00000004, // Show the balloon.
- UF_REMINDER = 0x00000008 }; // Balloon is a reminder.
- //
- // These flags relate a cache state to balloon text message.
- // They fit into an encoded mask where the lowest 4 bits
- // contain the eSysTrayState (STS_XXXXXX) code.
- //
- // (STS_OFFLINE | BTF_INITIAL)
- //
- // would indicate the condition where the state is "offline" for
- // a single server and the text to be displayed is for the initial
- // notification.
- //
- enum eBalloonTextFlags {
- BTF_INITIAL = 0x00000010, // Initial notification
- BTF_REMIND = 0x00000020 // Reminder
- };
- static IconInfo s_rgIconInfo[]; // The icon info
- static BalloonInfo s_rgBalloonInfo[]; // Balloon configuration info.
- static TooltipInfo s_rgTooltipInfo[]; // Tooltip configuration info.
- static BalloonSupression s_rgBalloonSupression[];
- static const int s_iMinStateChangeInterval;
- UINT_PTR m_idFlashingTimer; // Flash timer id.
- UINT_PTR m_idReminderTimer; // Timer for showing reminder balloons.
- UINT_PTR m_idStateChangeTimer; // Timer for queued state changes.
- UINT m_iIconFlashTime; // Period of icon flashes (ms).
- HICON& m_hIconNoOverlay; // Icon used for flashing.
- HWND m_hwndNotify; // Notification window.
- DWORD m_dwFlashingExpires; // Tick count when flash timer expires.
- DWORD m_dwNextStateChange; // Tick count for next queued state change.
- TCHAR m_szServer[MAX_PATH]; // Servername for balloon messages.
- TCHAR m_szServerQueued[MAX_PATH];
- eSysTrayState m_state; // Remember current state.
- eSysTrayState m_statePrev;
- eSysTrayState m_stateQueued;
- bool m_bFlashOverlay; // Alternates 0,1 (1 == display overlay, 0 == don't)
- bool m_bActive; // 1 == we have an active icon in systray.
- //
- // Enforce singleton existance by making construction
- // and copy operations private.
- //
- CSysTrayUI(HWND hwndNotify);
- CSysTrayUI(const CSysTrayUI& rhs);
- CSysTrayUI& operator = (const CSysTrayUI& rhs);
- void UpdateSysTray(eUpdateFlags uFlags, LPCTSTR pszServer = NULL);
- int GetBalloonInfoIndex(eSysTrayState state, DWORD dwTextFlags);
- bool StateHasBalloonText(eSysTrayState state, DWORD dwTextFlags);
- void GetBalloonInfo(eSysTrayState state,
- DWORD dwTextFlags,
- LPTSTR pszTextHdr,
- int cchTextHdr,
- LPTSTR pszTextBody,
- int cchTextBody,
- DWORD *pdwInfoFlags,
- UINT *puTimeout);
- bool SupressBalloon(eSysTrayState statePrev, eSysTrayState state);
- LPTSTR GetTooltipText(eSysTrayState state,
- LPTSTR pszText,
- int cchText);
- bool IconFlashedLongEnough(void);
- void KillIconFlashTimer(void);
- void HandleFlashTimer(void);
- void OnStateChangeTimerExpired(void);
- static VOID CALLBACK FlashTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
- static VOID CALLBACK ReminderTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
- static VOID CALLBACK StateChangeTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
- };
- #define ICONFLASH_FOREVER (UINT(-1))
- #define ICONFLASH_NONE 0
- //
- // These rows must stay in the same order as the STS_XXXXX enumeration members.
- // For flash timeout values, 0 == no flash, -1 == never stop.
- // Everything else is a timeout in milliseconds.
- //
- CSysTrayUI::IconInfo
- CSysTrayUI::s_rgIconInfo[] = {
- { NULL, 0, ICONFLASH_NONE }, /* STS_INVALID */
- { NULL, 0, ICONFLASH_NONE }, /* STS_ONLINE */
- { NULL, IDI_CSCWARNING, ICONFLASH_FOREVER }, /* STS_DIRTY */
- { NULL, IDI_CSCWARNING, ICONFLASH_FOREVER }, /* STS_MDIRTY */
- { NULL, IDI_CSCINFORMATION, ICONFLASH_NONE }, /* STS_SERVERBACK */
- { NULL, IDI_CSCINFORMATION, ICONFLASH_NONE }, /* STS_MSERVERBACK */
- { NULL, IDI_CSCNORMAL, ICONFLASH_NONE }, /* STS_OFFLINE */
- { NULL, IDI_CSCNORMAL, ICONFLASH_NONE }, /* STS_MOFFLINE */
- { NULL, IDI_CSCNORMAL, ICONFLASH_NONE }}; /* STS_NONET */
- //
- // This table describes all information related to displaying the systray balloons.
- // The first two columns are the keys to each record; those being a systray UI state
- // and a mask of balloon-text flags.
- // Notes:
- // 1. There's no balloon for STS_NONET. We found that the user's response is
- // duh, I know I have no net.
- //
- //
- CSysTrayUI::BalloonInfo
- CSysTrayUI::s_rgBalloonInfo[] = {
- { STS_INVALID, BTF_INITIAL, NIIF_NONE, 0, 0, 0, 0, },
- { STS_INVALID, BTF_REMIND, NIIF_NONE, 0, 0, 0, 0, },
- { STS_OFFLINE, BTF_INITIAL, NIIF_INFO, IDS_BTHDR_INITIAL, IDS_BTSTA_OFFLINE, IDS_BTBOD_OFFLINE, IDS_BTDIR_VIEWSTATUS },
- { STS_MOFFLINE, BTF_INITIAL, NIIF_INFO, IDS_BTHDR_INITIAL, IDS_BTSTA_OFFLINE, IDS_BTBOD_OFFLINE_M, IDS_BTDIR_VIEWSTATUS },
- { STS_OFFLINE, BTF_REMIND, NIIF_INFO, IDS_BTHDR_REMIND, IDS_BTSTA_OFFLINE, IDS_BTBOD_STILLOFFLINE, IDS_BTDIR_VIEWSTATUS },
- { STS_MOFFLINE, BTF_REMIND, NIIF_INFO, IDS_BTHDR_REMIND, IDS_BTSTA_OFFLINE, IDS_BTBOD_STILLOFFLINE_M, IDS_BTDIR_VIEWSTATUS },
- // { STS_SERVERBACK, BTF_INITIAL, NIIF_INFO, IDS_BTHDR_INITIAL, IDS_BTSTA_SERVERBACK,IDS_BTBOD_SERVERBACK, IDS_BTDIR_RECONNECT },
- // { STS_MSERVERBACK,BTF_INITIAL, NIIF_INFO, IDS_BTHDR_INITIAL, IDS_BTSTA_SERVERBACK,IDS_BTBOD_SERVERBACK_M, IDS_BTDIR_RECONNECT },
- { STS_SERVERBACK, BTF_REMIND, NIIF_INFO, IDS_BTHDR_REMIND, IDS_BTSTA_SERVERBACK,IDS_BTBOD_STILLBACK, IDS_BTDIR_RECONNECT },
- { STS_MSERVERBACK,BTF_REMIND, NIIF_INFO, IDS_BTHDR_REMIND, IDS_BTSTA_SERVERBACK,IDS_BTBOD_STILLBACK_M, IDS_BTDIR_RECONNECT },
- { STS_DIRTY, BTF_INITIAL, NIIF_WARNING, IDS_BTHDR_INITIAL, IDS_BTSTA_DIRTY, IDS_BTBOD_DIRTY, IDS_BTDIR_SYNC },
- { STS_MDIRTY, BTF_INITIAL, NIIF_WARNING, IDS_BTHDR_INITIAL, IDS_BTSTA_DIRTY, IDS_BTBOD_DIRTY_M, IDS_BTDIR_SYNC },
- { STS_DIRTY, BTF_REMIND, NIIF_WARNING, IDS_BTHDR_REMIND, IDS_BTSTA_DIRTY, IDS_BTBOD_STILLDIRTY, IDS_BTDIR_SYNC },
- { STS_MDIRTY, BTF_REMIND, NIIF_WARNING, IDS_BTHDR_REMIND, IDS_BTSTA_DIRTY, IDS_BTBOD_STILLDIRTY_M, IDS_BTDIR_SYNC }
- };
- //
- // This table lists all of the state transitions that do not generate balloons.
- // Ideally, I would have a true state machine to control the UI for any given state transition.
- // However, since we have quite a few states and since you can transition from any state
- // to almost any other state, the state transition table would be large and confusing
- // to read. Instead, I've taken the position to assume all state transitions generate
- // the balloon UI associated with the "to" state unless the transition is listed
- // in this table.
- //
- CSysTrayUI::BalloonSupression
- CSysTrayUI::s_rgBalloonSupression[] = {
- { STS_MOFFLINE, STS_OFFLINE },
- { STS_NONET, STS_OFFLINE },
- { STS_NONET, STS_MOFFLINE }
- };
- //
- // This table describes all information related to displaying tooltip text
- // for the systray icon.
- //
- CSysTrayUI::TooltipInfo
- CSysTrayUI::s_rgTooltipInfo[] = {
- { STS_INVALID, 0 },
- { STS_OFFLINE, IDS_TT_OFFLINE },
- { STS_MOFFLINE, IDS_TT_OFFLINE_M },
- { STS_SERVERBACK, IDS_TT_SERVERBACK },
- { STS_MSERVERBACK, IDS_TT_SERVERBACK_M },
- { STS_DIRTY, IDS_TT_DIRTY },
- { STS_MDIRTY, IDS_TT_DIRTY_M },
- { STS_NONET, IDS_TT_NONET }
- };
- //
- // Wrap the CEventLog class so we can control log initialization
- // and also filter events based on the CSCUI event logging level.
- // The idea here is to create a CscuiEventLog object whenever you
- // want to write to the event log. The ReportEvent member has
- // been designed to handle log initialization as well as filtering
- // message output to respect the current CSCUI event logging level
- // set in the registry/policy. It's recommended that the
- // CscuiEventLog object be created as a local variable so that
- // once the reporting is complete, the object is destroyed and
- // the system event log handle is closed.
- //
- class CscuiEventLog
- {
- public:
- CscuiEventLog(void)
- : m_iEventLoggingLevel(CConfig::GetSingleton().EventLoggingLevel()) { }
- ~CscuiEventLog(void) { }
- HRESULT ReportEvent(WORD wType,
- WORD wCategory,
- DWORD dwEventID,
- PSID lpUserSid = NULL,
- LPVOID pvRawData = NULL,
- DWORD cbRawData = 0);
- bool LoggingEnabled(void) const
- { return 0 < m_iEventLoggingLevel; }
- void Push(HRESULT hr, CEventLog::eFmt fmt)
- { m_log.Push(hr, fmt); }
- void Push(LPCTSTR psz)
- { m_log.Push(psz); }
- private:
- CEventLog m_log;
- int m_iEventLoggingLevel;
- };
- //-----------------------------------------------------------------------------
- // CscuiEventLog member functions.
- //-----------------------------------------------------------------------------
- HRESULT
- CscuiEventLog::ReportEvent(
- WORD wType,
- WORD wCategory,
- DWORD dwEventID,
- PSID lpUserSid,
- LPVOID pvRawData,
- DWORD cbRawData
- )
- {
- //
- // Add to this table if you add new event messages.
- //
- static const struct
- {
- DWORD dwEventID;
- int iLevel;
- } rgEventInfo[] = {{ MSG_I_SERVER_OFFLINE, 1 },
- { MSG_I_SERVER_AVAILABLE, 3 },
- { MSG_I_NET_STOPPED, 2 },
- { MSG_I_NET_STARTED, 2 },
- { MSG_E_CACHE_CORRUPTED, 0 },
- { MSG_I_SERVER_AUTORECONNECT, 3 }};
- int iLevel = CConfig::GetSingleton().EventLoggingLevel();
- for (int i = 0; i < ARRAYSIZE(rgEventInfo); i++)
- {
- if (dwEventID == rgEventInfo[i].dwEventID && iLevel >= rgEventInfo[i].iLevel)
- {
- if (SUCCEEDED(m_log.Initialize(TEXT("Offline Files"))))
- {
- return m_log.ReportEvent(wType,
- wCategory,
- dwEventID,
- lpUserSid,
- pvRawData,
- cbRawData);
- }
- }
- }
- return S_FALSE;
- }
- //-----------------------------------------------------------------------------
- // CServerList member functions.
- //-----------------------------------------------------------------------------
- CServerList::~CServerList(
- void
- )
- {
- if (NULL != m_hdpa)
- {
- int cEntries = DPA_GetPtrCount(m_hdpa);
- LPTSTR pszEntry;
- for (int i = 0; i < cEntries; i++)
- {
- pszEntry = (LPTSTR)DPA_GetPtr(m_hdpa, i);
- if (NULL != pszEntry)
- LocalFree(pszEntry);
- }
- DPA_Destroy(m_hdpa);
- }
- }
- void
- CServerList::GetServerFromPath(
- LPCTSTR pszPath,
- LPTSTR pszServer,
- int cchServer
- )
- {
- TCHAR szServer[MAX_PATH];
- lstrcpyn(szServer, pszPath, ARRAYSIZE(szServer));
- PathAddBackslash(szServer);
- PathStripToRoot(szServer);
- LPTSTR pszLastBackslash = StrRChr(szServer, szServer + lstrlen(szServer), TEXT('\'));
- if (NULL != pszLastBackslash && pszLastBackslash > (szServer + 2))
- *pszLastBackslash = TEXT('');
- lstrcpyn(pszServer, szServer, cchServer);
- }
- bool
- CServerList::Add(
- LPCTSTR pszServer
- )
- {
- if (NULL != m_hdpa)
- {
- if (!Exists(pszServer))
- {
- int cchEntry = lstrlen(pszServer) + 1;
- LPTSTR pszEntry = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * cchEntry);
- if (NULL != pszEntry)
- {
- GetServerFromPath(pszServer, pszEntry, cchEntry);
- if (-1 != DPA_AppendPtr(m_hdpa, pszEntry))
- return true;
- //
- // Addition to DPA failed. Delete the string buffer.
- //
- LocalFree(pszEntry);
- }
- }
- }
- return false;
- }
- void
- CServerList::Remove(
- LPCTSTR pszServer
- )
- {
- int iEntry = Find(pszServer);
- if (-1 != iEntry)
- {
- LPTSTR pszEntry = (LPTSTR)DPA_DeletePtr(m_hdpa, iEntry);
- if (NULL != pszEntry)
- LocalFree(pszEntry);
- }
- }
- LPCTSTR
- CServerList::Get(
- int iItem
- ) const
- {
- if (NULL != m_hdpa)
- return (LPCTSTR)DPA_GetPtr(m_hdpa, iItem);
- return NULL;
- }
- int
- CServerList::Count(
- void
- ) const
- {
- if (NULL != m_hdpa)
- return DPA_GetPtrCount(m_hdpa);
- return 0;
- }
- //
- // Locate a server name in the "pending reconnection" list.
- // pszServer can either be "\server" or "\servershare".
- //
- // Returns: Index of entry if found. -1 if not found.
- //
- int
- CServerList::Find(
- LPCTSTR pszServer
- )
- {
- TCHAR szServer[MAX_PATH];
- GetServerFromPath(pszServer, szServer, ARRAYSIZE(szServer));
- if (NULL != m_hdpa)
- {
- int cEntries = DPA_GetPtrCount(m_hdpa);
- LPTSTR pszEntry;
- for (int i = 0; i < cEntries; i++)
- {
- pszEntry = (LPTSTR)DPA_GetPtr(m_hdpa, i);
- if (NULL != pszEntry)
- {
- if (0 == lstrcmpi(pszEntry, szServer))
- return i;
- }
- }
- }
- return -1;
- }
- void
- CServerList::Clear(
- void
- )
- {
- if (NULL != m_hdpa)
- {
- int cEntries = DPA_GetPtrCount(m_hdpa);
- LPTSTR pszEntry;
- for (int i = 0; i < cEntries; i++)
- {
- pszEntry = (LPTSTR)DPA_DeletePtr(m_hdpa, i);
- if (NULL != pszEntry)
- {
- LocalFree(pszEntry);
- }
- }
- }
- }
- //-----------------------------------------------------------------------------
- // CStateMachine member functions.
- //-----------------------------------------------------------------------------
- //
- // Translates a STWM_XXXXX message from the CSC agent into a systray UI state
- // code. The caller also provides a buffer to a server name. If we find
- // a "single server" condition in the cache (i.e. one server is dirty, one
- // server is offline etc), then we write the name of this server to this
- // buffer. Otherwise, the buffer remains unchanged. The goal here is to
- // end up with a buffer containing the name of the applicable server when
- // we have one of these one-server conditions. Ultimately, the server name
- // is included in the tray balloon text message.
- //
- // The function returns one of the STS_XXXXX UI status codes.
- //
- // This function is rather long. Much longer than I like a function to be.
- // I've tried to break it up into smaller pieces but any chunks were pretty
- // much arbitrary. Without a good logical breakdown, that doesn't make much
- // sense. Even with it's length, it's not a complex function. It merely
- // enumerates shares in the cache gathering statistics along the way. From
- // these statistics, it decides what the next UI state should be.
- //
- eSysTrayState
- CStateMachine::TranslateInput(
- UINT uMsg,
- LPTSTR pszServer,
- UINT cchServer
- )
- {
- //
- // Since this cscui code is running all the time, we don't want to keep
- // a handle to the event log open. Therefore, we use this CscuiEventLog
- // object to automatically close the log for us. The ReportEvent member
- // of CscuiEventLog handles all initialization of the log and determining
- // if the event should actually be logged (depending upon the current CSCUI
- // event logging level).
- //
- CscuiEventLog log;
- bool bServerIsBack = false;
- if (STWM_CSCNETUP == uMsg)
- {
- m_bNoNet = false;
- if (TEXT('') != *pszServer)
- {
- STDBGOUT((1, TEXT("Translating STWM_CSCNETUP for server "%s""), pszServer));
- //
- // Server reported back by the CSC agent.
- // Add it's name to a persistent (in memory) list of
- // servers available for reconnection.
- // Also clear the "no net" flag.
- //
- bServerIsBack = true;
- ServerPendingReconnection(pszServer);
- if (log.LoggingEnabled())
- {
- log.Push(pszServer);
- log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_SERVER_AVAILABLE);
- }
- }
- else
- {
- STDBGOUT((1, TEXT("Translating STWM_CSCNETUP (no associated server)")));
- if (log.LoggingEnabled())
- {
- log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_NET_STARTED);
- }
- }
- }
- else if (STWM_CSCNETDOWN == uMsg)
- {
- //
- // This is the only place where transitions from online to
- // offline state are noted in the shell process. (CSCUISetState
- // and OnQueryNetDown execute in WinLogon's process).
- //
- if (TEXT('') != *pszServer)
- {
- STDBGOUT((1, TEXT("Translating STWM_CSCNETDOWN for server "%s""), pszServer));
- if (!m_bNoNet)
- {
- LPTSTR pszTemp;
- if (LocalAllocString(&pszTemp, pszServer))
- {
- PostToSystray(PWM_REFRESH_SHELL, 0, (LPARAM)pszTemp);
- }
- }
- //
- // Server reported down by the CSC agent.
- // Remove it's name from the persistent (in memory) list
- // of servers available for reconnection.
- //
- ServerUnavailable(pszServer);
- if (log.LoggingEnabled())
- {
- log.Push(pszServer);
- log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_SERVER_OFFLINE);
- }
- }
- else
- {
- STDBGOUT((1, TEXT("Translating STWM_CSCNETDOWN (no associated server)")));
- //
- // Entire network reported down by the CSC agent.
- // Remove all names from the persistent (in memory) list
- // of servers available for reconnection. m_bNoNet is the only persistent
- // state we have. Once it is set, the only thing that can reset it
- // is a STWM_CSCNETUP message from the CSC agent.
- //
- if (!m_bNoNet)
- PostToSystray(PWM_REFRESH_SHELL, 0, 0);
- m_bNoNet = true;
- AllServersUnavailable();
- if (log.LoggingEnabled())
- {
- log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_NET_STOPPED);
- }
- }
- }
- else if (STWM_STATUSCHECK == uMsg)
- {
- STDBGOUT((1, TEXT("Translating STWM_STATUSCHECK")));
- }
- else if (STWM_CACHE_CORRUPTED == uMsg)
- {
- //
- // Note: No check for LoggingEnabled(). We always log corrupted cache
- // regardless of logging level.
- //
- STDBGOUT((1, TEXT("Translating STWM_CACHE_CORRUPTED")));
- log.ReportEvent(EVENTLOG_ERROR_TYPE, 0, MSG_E_CACHE_CORRUPTED);
- }
- //
- // If CSC is disabled or the cache is empty, the default UI state
- // is "online".
- //
- eSysTrayState state = STS_ONLINE;
- if (IsCSCEnabled())
- {
- DWORD dwStatus;
- DWORD dwPinCount;
- DWORD dwHintFlags;
- WIN32_FIND_DATA fd;
- FILETIME ft;
- CCscFindHandle hFind;
- hFind = CacheFindFirst(NULL, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft);
- if (hFind.IsValid())
- {
- //
- // We need these three temporary name lists to reconcile a problem with
- // the way the CSC cache and RDR are designed. When we enumerate the cache,
- // we enumerate individual shares in the cache. Each share has some condition
- // (i.e. dirty, offline etc) associated with it. The problem is that the
- // redirector handles things on a server basis. So when a particular share
- // is offline, in reality the entire server is offline. We've decided that
- // the UI should reflect things on a server (computer) basis so we need to
- // avoid including the states of multiple shares from the same server in
- // our totals. These three lists are used to store the names of servers
- // with shares in one of the three states (offline, dirty, pending recon).
- // If we enumerate a share with one of these states and find it already
- // exists in the corresponding list, we don't include this share in the
- // statistics.
- //
- int cShares = 0;
- CServerList OfflineList;
- CServerList DirtyList;
- CServerList BackList;
- //
- // If a server is back, assume we can auto-reconnect it.
- //
- bool bAutoReconnectServer = bServerIsBack;
- TCHAR szAutoReconnectShare[MAX_PATH] = {0};
- DWORD dwPathSpeed = 0;
- do
- {
- bool bShareIsOnServer = boolify(PathIsPrefix(pszServer, fd.cFileName));
- bool bShareHasModifiedFiles = false;
- bool bShareHasOpenFiles = false;
- //
- // A share participates in the systray UI calculations only if the
- // share contains files OR the share is currently "offline".
- // Because of the CSC database design, CSC doesn't remove a share
- // entry after all it's files have been removed from the cache.
- // Therefore we need this extra check to avoid including empty shares in the UI.
- //
- if (ShareHasFiles(fd.cFileName, &bShareHasModifiedFiles, &bShareHasOpenFiles) ||
- ShareIsOffline(dwStatus))
- {
- cShares++;
- if (bShareIsOnServer && (bShareHasModifiedFiles || bShareHasOpenFiles))
- {
- //
- // Auto-reconnect isn't allowed if one or more shares on the server
- // have open files or files modified offline. Auto-reconnection
- // would put the cache into a dirty state.
- //
- bAutoReconnectServer = false;
- }
- //
- // A share can be in one of 4 states:
- // Online
- // Dirty
- // Offline
- // Pending reconnection ('back')
- //
- // Note that our definition of Dirty implies Online, and Pending
- // Reconnection implies Offline. That is, an offline share is
- // never dirty and an online share is never pending reconnection.
- //
- //---------------------------------------------------------------------
- // Is the share online?
- //---------------------------------------------------------------------
- if (!ShareIsOffline(dwStatus))
- {
- //---------------------------------------------------------------------
- // Is the share dirty? (online + offline changes)
- //---------------------------------------------------------------------
- if (bShareHasModifiedFiles)
- {
- STDBGOUT((3, TEXT("Share "%s" is dirty (0x%08X)"), fd.cFileName, dwStatus));
- DirtyList.Add(fd.cFileName);
- }
- else
- {
- STDBGOUT((3, TEXT("Share "%s" is online (0x%08X)"), fd.cFileName, dwStatus));
- }
- }
- else // Offline
- {
- //---------------------------------------------------------------------
- // Is the server back?
- //---------------------------------------------------------------------
- if (IsServerPendingReconnection(fd.cFileName))
- {
- STDBGOUT((3, TEXT("Share "%s" is pending reconnection (0x%08X)"), fd.cFileName, dwStatus));
- BackList.Add(fd.cFileName);
- }
- else
- {
- STDBGOUT((3, TEXT("Share "%s" is OFFLINE (0x%08X)"), fd.cFileName, dwStatus));
- OfflineList.Add(fd.cFileName);
- }
- }
- }
- if (!ShareIsOffline(dwStatus))
- {
- // It's online, so it can't be pending reconnection.
- ServerReconnected(fd.cFileName);
- // ...and there's no need to reconnect it.
- if (bShareIsOnServer)
- bAutoReconnectServer = false;
- }
- if (bAutoReconnectServer && bShareIsOnServer && TEXT('') == szAutoReconnectShare[0])
- {
- //
- // Remember the share name for possible auto-reconnection.
- // The transition API is TransitionServerOnline but it takes a share name.
- // Bad choice of names (IMO) but that's the way Shishir did it in the
- // CSC APIs. It can be any share on the server.
- //
- // However, it's possible to have defunct shares in the
- // database. Try to find one that's connectable.
- //
- if (CSCCheckShareOnlineEx(fd.cFileName, &dwPathSpeed))
- {
- STDBGOUT((3, TEXT("Share "%s" alive at %d00 bps"), fd.cFileName, dwPathSpeed));
- lstrcpyn(szAutoReconnectShare, fd.cFileName, ARRAYSIZE(szAutoReconnectShare));
- }
- else
- {
- STDBGOUT((3, TEXT("Share "%s" unreachable, error = %d"), fd.cFileName, GetLastError()));
- }
- }
- }
- while(CacheFindNext(hFind, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft));
- if (bAutoReconnectServer)
- {
- //---------------------------------------------------------------------
- // Handle auto-reconnection.
- //---------------------------------------------------------------------
- //
- if (TEXT('') != szAutoReconnectShare[0])
- {
- //
- // Server was reported "BACK" by the CSC agent and it has no open files
- // nor files modified offline and it's not on a slow link.
- // This makes it a candidate for automatic reconnection. Try it.
- //
- STDBGOUT((1, TEXT("Attempting to auto-reconnect "%s""), szAutoReconnectShare));
- if (TransitionShareOnline(szAutoReconnectShare, TRUE, TRUE, dwPathSpeed))
- {
- //
- // The server has been reconnected. Remove it's name from the
- // "pending reconnection" list.
- //
- ServerReconnected(pszServer);
- //
- // Remove this server from the temporary lists we've been keeping.
- //
- DirtyList.Remove(pszServer);
- BackList.Remove(pszServer);
- OfflineList.Remove(pszServer);
- if (log.LoggingEnabled())
- {
- log.Push(pszServer);
- log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_SERVER_AUTORECONNECT);
- }
- }
- }
- }
- int cDirty = DirtyList.Count();
- int cBack = BackList.Count();
- int cOffline = OfflineList.Count();
- STDBGOUT((2, TEXT("Cache check server results: cShares = %d, cDirty = %d, cBack = %d, cOffline = %d"),
- cShares, cDirty, cBack, cOffline));
- //
- // This code path is a waterfall where lower-priority states are overwritten
- // by higher-priority states as they are encountered. The order of this array
- // is important. It's ordered by increasing priority (no net is
- // highest priority for systray UI).
- //
- CServerList *pServerList = NULL;
- struct Criteria
- {
- int cnt; // Number of applicable servers found.
- eSysTrayState state; // Single-item UI state.
- eSysTrayState mstate; // Multi-item UI state.
- CServerList *pList; // Ptr to applicable list with server names.
- } rgCriteria[] = {
- { cOffline, STS_OFFLINE, STS_MOFFLINE, &OfflineList },
- { cBack, STS_SERVERBACK, STS_MSERVERBACK, &BackList },
- { cDirty, STS_DIRTY, STS_MDIRTY, &DirtyList },
- { cShares && m_bNoNet ? 1 : 0, STS_NONET, STS_NONET, NULL }
- };
- for (int i = 0; i < ARRAYSIZE(rgCriteria); i++)
- {
- Criteria& c = rgCriteria[i];
- if (0 < c.cnt)
- {
- state = c.mstate;
- if (1 == c.cnt)
- {
- state = c.state;
- pServerList = NULL;
- if (NULL != c.pList && 1 == c.pList->Count())
- {
- pServerList = c.pList;
- }
- }
- }
- }
- if (NULL != pServerList)
- {
- //
- // We had a single-server condition so write the server name
- // to the caller's server name buffer.
- // If we didn't have a single-server condition, the buffer
- // remains unchanged.
- //
- lstrcpyn(pszServer, pServerList->Get(0), cchServer);
- }
- }
- }
- STDBGOUT((1, TEXT("Translated to SysTray UI state %s"), SysTrayStateStr(state)));
- return state;
- }
- //
- // Ping offline servers. If any are alive, update status and
- // auto-reconnect them if possible. This is typically done
- // after a sync operation has completed.
- //
- DWORD WINAPI
- _PingServersThread(LPVOID /*pThreadData*/)
- {
- DWORD dwStatus;
- WIN32_FIND_DATA fd;
- HANDLE hFind;
- hFind = CacheFindFirst(NULL, &fd, &dwStatus, NULL, NULL, NULL);
- if (INVALID_HANDLE_VALUE != hFind)
- {
- CServerList BackList;
- do
- {
- // If the tray state becomes Online or NoNet, we can quit
- eSysTrayState state = (eSysTrayState)SendToSystray(PWM_QUERY_UISTATE, 0, 0);
- if (STS_ONLINE == state || STS_NONET == state)
- break;
- // Call BackList.Exists here to avoid extra calls to
- // CSCCheckShareOnline. (Add also calls Exists)
- if ((FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwStatus) &&
- !BackList.Exists(fd.cFileName))
- {
- if (!CSCCheckShareOnline(fd.cFileName))
- {
- DWORD dwErr = GetLastError();
- if (ERROR_ACCESS_DENIED != dwErr &&
- ERROR_LOGON_FAILURE != dwErr)
- {
- // The share is not reachable
- continue;
- }
- // Access denied or logon failure means the server is
- // reachable, but we don't have valid credentials.
- }
- // The share is offline but available again.
- STDBGOUT((1, TEXT("Detected server back: %s"), fd.cFileName));
- BackList.Add(fd.cFileName);
- // Get the \server name (minus the sharename) and
- // tell ourselves that it's back.
- LPCTSTR pszServer = BackList.Get(BackList.Count() - 1);
- if (pszServer)
- {
- CSCUISetState(STWM_CSCNETUP, 0, (LPARAM)pszServer);
- }
- }
- }
- while(CacheFindNext(hFind, &fd, &dwStatus, NULL, NULL, NULL));
- CSCFindClose(hFind);
- }
- DllRelease();
- FreeLibraryAndExitThread(g_hInstance, 0);
- return 0;
- }
- void
- CStateMachine::PingServers()
- {
- // Don't bother trying if there's no net.
- if (!m_bNoNet)
- {
- DWORD dwThreadID;
- // Give the thread a reference to the DLL
- HINSTANCE hInstThisDll = LoadLibrary(c_szDllName);
- DllAddRef();
- HANDLE hThread = CreateThread(NULL,
- 0,
- _PingServersThread,
- NULL,
- 0,
- &dwThreadID);
- if (hThread)
- {
- CloseHandle(hThread);
- }
- else
- {
- // CreateThread failed, cleanup
- DllRelease();
- FreeLibrary(hInstThisDll);
- }
- }
- }
- //
- // Determine if a given share has files cached in the CSC cache.
- //
- //
- bool
- CStateMachine::ShareHasFiles(
- LPCTSTR pszShare,
- bool *pbModified,
- bool *pbOpen
- ) const
- {
- //
- // Exclude the following:
- // 1. Directories.
- // 2. Files marked as "locally deleted".
- //
- // NOTE: The filtering done by this function must be the same as
- // in several other places throughout the CSCUI code.
- // To locate these, search the source for the comment
- // string CSCUI_ITEM_FILTER.
- //
- const DWORD fExclude = SSEF_LOCAL_DELETED |
- SSEF_DIRECTORY;
- //
- // Stop stats enumeration when we've found all of the following:
- // 1. At least one file.
- // 2. At least one modified file.
- // 3. At least one file with either USER access OR GUEST access.
- //
- const DWORD fUnity = SSUF_TOTAL |
- SSUF_MODIFIED |
- SSUF_ACCUSER |
- SSUF_ACCGUEST |
- SSUF_ACCOR;
- CSCSHARESTATS ss;
- CSCGETSTATSINFO si = { fExclude, fUnity, true, false };
- _GetShareStatisticsForUser(pszShare, // Share name.
- &si,
- &ss); // Destination buffer.
- if (NULL != pbModified)
- {
- *pbModified = (0 < ss.cModified);
- }
- if (NULL != pbOpen)
- {
- *pbOpen = ss.bOpenFiles;
- }
- return 0 < ss.cTotal;
- }
- //-----------------------------------------------------------------------------
- // CSysTrayUI member functions.
- //-----------------------------------------------------------------------------
- //
- // This is the minimum interval (in ms) allowed between state changes of
- // the systray UI. A value of 0 would result in immediate updates as
- // notifications are received from the CSC agent. A value of 60000 would
- // cause any state changes received less than 60 seconds after the previous
- // state change to be queued. 60 seconds after the previous state change,
- // if a state change is queued it is applied to the systray UI.
- // Something to consider is dynamically adjusting
- //
- const int CSysTrayUI::s_iMinStateChangeInterval = 10000; // 10 seconds.
- CSysTrayUI::CSysTrayUI(
- HWND hwndNotify
- ) : m_idFlashingTimer(0),
- m_idReminderTimer(0),
- m_idStateChangeTimer(0),
- m_iIconFlashTime(GetCaretBlinkTime()),
- m_hIconNoOverlay(s_rgIconInfo[int(STS_OFFLINE)].hIcon), // The offline icon is used
- // as the non-overlay icon for
- // flashing.
- m_hwndNotify(hwndNotify),
- m_dwFlashingExpires(0),
- m_dwNextStateChange(0),
- m_state(STS_ONLINE),
- m_statePrev(STS_INVALID),
- m_stateQueued(STS_INVALID),
- m_bFlashOverlay(false),
- m_bActive(false)
- {
- //
- // Load up the required icons.
- //
- for (int i = 0; i < ARRAYSIZE(s_rgIconInfo); i++)
- {
- IconInfo& sti = s_rgIconInfo[i];
- if (NULL == sti.hIcon && 0 != sti.idIcon)
- {
- sti.hIcon = (HICON)LoadImage(g_hInstance,
- MAKEINTRESOURCE(sti.idIcon),
- IMAGE_ICON,
- CSC_ICON_CX,
- CSC_ICON_CY,
- LR_LOADMAP3DCOLORS);
- if (NULL == sti.hIcon)
- {
- Trace((TEXT("CSCUI ERROR %d loading Icon ID = %d"), GetLastError(), sti.idIcon));
- }
- }
- }
- m_szServer[0] = TEXT('');
- m_szServerQueued[0] = TEXT('');
- UpdateSysTray(UF_ICON);
- }
- CSysTrayUI::~CSysTrayUI(
- void
- )
- {
- if (0 != m_idStateChangeTimer)
- KillTimer(m_hwndNotify, m_idStateChangeTimer);
- }
- //
- // Singleton instance access.
- //
- CSysTrayUI&
- CSysTrayUI::GetInstance(
- void
- )
- {
- static CSysTrayUI TheUI(_FindNotificationWindow());
- return TheUI;
- }
- //
- // Change the current state of the UI to a new state.
- // Returns:
- // true = state was changed.
- // false = state was not changed.
- //
- bool
- CSysTrayUI::SetState(
- eSysTrayState state,
- LPCTSTR pszServer // Optional. Default is NULL.
- )
- {
- bool bResult = false;
- //
- // Apply a state change only if the state has actually changed.
- //
- if (state != m_state)
- {
- //
- // Apply a state change only if there's not a sync in progress.
- // If there is a sync in progress, we'll receive a CSCWM_DONESYNCING
- // message when the sync is finished which will trigger a UI update.
- //
- if (!::IsSyncInProgress())
- {
- if (0 == m_idStateChangeTimer)
- {
- //
- // The state change timer is not active. That means it's OK
- // to update the tray UI.
- //
- STDBGOUT((1, TEXT("Changing SysTray UI state %s -> %s"),
- SysTrayStateStr(m_state),
- SysTrayStateStr(state)));
- m_statePrev = m_state;
- m_state = state;
- UpdateSysTray(eUpdateFlags(UF_ICON | UF_BALLOON), pszServer);
- //
- // Reset the state change timer so that we will not produce a
- // visible change in the tray UI for at least another
- // s_iMinStateChangeInterval milliseconds.
- // Also invalidate the queued state info so that if the update timer
- // expires before we queue a state change, it will be a no-op.
- //
- STDBGOUT((2, TEXT("Setting state change timer")));
- m_stateQueued = STS_INVALID;
- m_idStateChangeTimer = SetTimer(m_hwndNotify,
- ID_TIMER_STATECHANGE,
- s_iMinStateChangeInterval,
- StateChangeTimerProc);
- bResult = true;
- }
- else
- {
- //
- // The state change timer is active so we can't update the tray
- // UI right now. We'll queue up the state information so when the
- // timer expires this state will be applied. Note that the "queue"
- // is only ONE item deep. Each successive addition to the queue
- // overwrites the current content.
- //
- STDBGOUT((2, TEXT("Queueing state change to %s."), SysTrayStateStr(state)));
- m_stateQueued = state;
- if (NULL != pszServer)
- {
- lstrcpyn(m_szServerQueued, pszServer, ARRAYSIZE(m_szServerQueued));
- }
- else
- {
- m_szServerQueued[0] = TEXT('');
- }
- }
- }
- else
- {
- STDBGOUT((2, TEXT("Sync in progress. SysTray state not changed.")));
- }
- }
- return bResult;
- }
- //
- // Called each time the state change timer expires.
- //
- VOID CALLBACK
- CSysTrayUI::StateChangeTimerProc(
- HWND hwnd,
- UINT uMsg,
- UINT_PTR idEvent,
- DWORD dwTime
- )
- {
- //
- // Call a non-static function of the singleton instance so
- // we have access to private members.
- //
- CSysTrayUI::GetInstance().OnStateChangeTimerExpired();
- }
- void
- CSysTrayUI::OnStateChangeTimerExpired(
- void
- )
- {
- STDBGOUT((2, TEXT("State change timer expired. Queued state = %s"),
- SysTrayStateStr(m_stateQueued)));
- //
- // Kill the timer and set it's ID to 0.
- // This will let SetState() know that the timer has expired and
- // it's OK to update the tray UI.
- //
- if (0 != m_idStateChangeTimer)
- {
- KillTimer(m_hwndNotify, m_idStateChangeTimer);
- m_idStateChangeTimer = 0;
- }
- if (int(m_stateQueued) != int(STS_INVALID))
- {
- //
- // Call SetState ONLY if queued info is valid; meaning
- // there was something in the queue.
- //
- SetState(m_stateQueued, m_szServerQueued);
- }
- }
- //
- // On WM_WININICHANGED update the icon flash timer.
- //
- void
- CSysTrayUI::OnWinIniChange(
- LPCTSTR pszSection
- )
- {
- m_iIconFlashTime = GetCaretBlinkTime();
- KillIconFlashTimer();
- UpdateSysTray(UF_FLASHICON);
- }
- //
- // Show the reminder balloon associated with the current UI state.
- //
- void
- CSysTrayUI::ShowReminderBalloon(
- void
- )
- {
- UpdateSysTray(eUpdateFlags(UF_BALLOON | UF_REMINDER));
- }
- //
- // All roads lead here.
- // This function is the kitchen sink for updating the systray.
- // It's kind of a long function but it centralizes all changes to
- // the systray. It's divided into 3 basic parts:
- //
- // 1. Change the tray icon. (UF_ICON)
- // 2. Flash the tray icon. (UF_FLASHICON)
- // 3. Display a notification balloon. (UF_BALLOON)
- //
- // Part or all of these can be performed in a single call depending
- // upon the content of the uFlags argument.
- //
- void
- CSysTrayUI::UpdateSysTray(
- eUpdateFlags uFlags,
- LPCTSTR pszServer // optional. Default is NULL.
- )
- {
- NOTIFYICONDATA nid = {0};
- if (!IsWindow(m_hwndNotify))
- return;
- //
- // If an icon is active, we're modifying it.
- // If none active, we're adding one.
- //
- DWORD nimsg = NIM_MODIFY;
- nid.cbSize = sizeof(NOTIFYICONDATA);
- nid.uID = PWM_TRAYCALLBACK;
- nid.uFlags = NIF_MESSAGE;
- nid.uCallbackMessage = PWM_TRAYCALLBACK;
- nid.hWnd = m_hwndNotify;
- IconInfo& sti = s_rgIconInfo[int(m_state)];
- if (NULL != pszServer && TEXT('') != *pszServer)
- {
- //
- // Copy the name of the server to a member variable.
- // Skip passed the leading "\".
- //
- while(*pszServer && TEXT('\') == *pszServer)
- pszServer++;
- lstrcpyn(m_szServer, pszServer, ARRAYSIZE(m_szServer));
- }
- //
- // Change the icon --------------------------------------------------------
- //
- if (UF_ICON & uFlags)
- {
- nid.uFlags |= NIF_ICON;
- if (0 == sti.idIcon)
- {
- //
- // This state doesn't have an icon. Delete from systray.
- //
- nimsg = NIM_DELETE;
- }
- else
- {
- if (!m_bActive)
- nimsg = NIM_ADD;
- nid.hIcon = sti.hIcon;
- //
- // If applicable, always flash icon when first showing it.
- //
- uFlags = eUpdateFlags(uFlags | UF_FLASHICON);
- //
- // Set the tooltip.
- //
- nid.uFlags |= NIF_TIP;
- GetTooltipText(m_state, nid.szTip, ARRAYSIZE(nid.szTip));
- }
- m_bFlashOverlay = false;
- KillIconFlashTimer();
- }
- //
- // Flash the icon ---------------------------------------------------------
- //
- if (UF_FLASHICON & uFlags)
- {
- if (0 != sti.iFlashTimeout)
- {
- nid.uFlags |= NIF_ICON; // Flashing is actually displaying a new icon.
- //
- // This icon is a flashing icon.
- //
- if (0 == m_idFlashingTimer)
- {
- //
- // No timer started yet. Start one.
- //
- STDBGOUT((2, TEXT("Starting icon flash timer. Time = %d ms"), m_iIconFlashTime));
- m_idFlashingTimer = SetTimer(m_hwndNotify,
- ID_TIMER_FLASHICON,
- m_iIconFlashTime,
- FlashTimerProc);
- if (0 != m_idFlashingTimer)
- {
- //
- // Set the tick-count when the timer expires.
- // An expiration time of (-1) means it never expires.
- //
- if (ICONFLASH_FOREVER != sti.iFlashTimeout)
- m_dwFlashingExpires = GetTickCount() + sti.iFlashTimeout;
- else
- m_dwFlashingExpires = ICONFLASH_FOREVER;
- }
- }
- nid.hIcon = m_bFlashOverlay ? sti.hIcon : m_hIconNoOverlay;
- m_bFlashOverlay = !m_bFlashOverlay; // Toggle flash state.
- }
- }
- //
- // Update or hide the balloon ---------------------------------------------
- //
- if (UF_BALLOON & uFlags)
- {
- //
- // If there's no balloon text mapped to the current UI state and these
- // balloon flags, any current balloon will be destroyed. This is because
- // the tray code destroys the current balloon before displaying the new one
- // and it doesn't display a new one if it's passed a blank string.
- //
- nid.uFlags |= NIF_INFO;
- DWORD dwBalloonFlags = (UF_REMINDER & uFlags) ? BTF_REMIND : BTF_INITIAL;
- GetBalloonInfo(m_state,
- dwBalloonFlags,
- nid.szInfoTitle,
- ARRAYSIZE(nid.szInfoTitle),
- nid.szInfo,
- ARRAYSIZE(nid.szInfo),
- &nid.dwInfoFlags,
- &nid.uTimeout);
- //
- // Any time we show a balloon, we reset the reminder timer.
- // This is so that we don't get a balloon resulting from a state change
- // immediately followed by a reminder balloon because the reminder
- // timer expired.
- //
- bool bRestartReminderTimer = (BTF_REMIND == dwBalloonFlags && TEXT('') != nid.szInfo[0]) ||
- StateHasBalloonText(m_state, BTF_REMIND);
- ResetReminderTimer(bRestartReminderTimer);
- }
- //
- // Notify the systray -----------------------------------------------------
- //
- if (NIM_DELETE == nimsg)
- m_bActive = false;
- if (Shell_NotifyIcon(nimsg, &nid))
- {
- if (NIM_ADD == nimsg)
- m_bActive = true;
- }
- }
- //
- // Get the balloon text associated with a given systray UI state and with
- // a given set of BTF_XXXXX (Balloon Text Flag) flags. The information
- // is stored in the table s_rgBalloonInfo[]. The text and balloon timeout
- // are returned in caller-provided buffers.
- //
- // The balloon text follows this format:
- //
- // <Header> <Status> n
- //
- // <Body>
- //
- // <Directive>
- //
- // An example would be:
- //
- // Offline Files - Network Connection Lost
- //
- // The network connection to '\worf' has been lost.
- //
- // Click here to view status.
- //
- // state is one of the STS_XXXXX flags.
- // dwTextFlags is a mask of BTF_XXXXX flag bits.
- //
- void
- CSysTrayUI::GetBalloonInfo(
- eSysTrayState state,
- DWORD dwTextFlags,
- LPTSTR pszTextHdr,
- int cchTextHdr,
- LPTSTR pszTextBody,
- int cchTextBody,
- DWORD *pdwInfoFlags,
- UINT *puTimeout
- )
- {
- *pszTextHdr = TEXT('');
- *pszTextBody = TEXT('');
- if (SupressBalloon(m_statePrev, state))
- {
- STDBGOUT((3, TEXT("Balloon supressed")));
- return;
- }
- int i = GetBalloonInfoIndex(state, dwTextFlags);
- if (-1 != i)
- {
- BalloonInfo& bi = s_rgBalloonInfo[i];
- //
- // BUGBUG: Review these buffer sizes. Allow for localization!
- //
- TCHAR szHeader[80];
- TCHAR szStatus[80];
- TCHAR szDirective[80];
- TCHAR szBody[MAX_PATH];
- TCHAR szFmt[MAX_PATH];
- if (STS_OFFLINE == state || STS_DIRTY == state || STS_SERVERBACK == state)
- {
- //
- // State has only one server associated with it so that means we'll
- // be including it in the balloon text body. Load the format
- // string from a text resource and embed the server name in it.
- //
- LPTSTR rgpstr[] = { m_szServer };
- LoadString(g_hInstance, bi.idBody, szFmt, ARRAYSIZE(szFmt));
- FormatMessage(FORMAT_MESSAGE_FROM_STRING |
- FORMAT_MESSAGE_ARGUMENT_ARRAY,
- szFmt,
- 0,0,
- szBody,
- ARRAYSIZE(szBody),
- (va_list *)rgpstr);
- }
- else
- {
- //
- // State has multiple servers associated with it so that means
- // there's no name embedded in the body. It's just a simple string
- // loaded from a text resource.
- //
- LoadString(g_hInstance, bi.idBody, szBody, ARRAYSIZE(szBody));
- }
- //
- // Create the header text.
- //
- LoadString(g_hInstance, IDS_BALLOONHDR_FORMAT, szFmt, ARRAYSIZE(szFmt));
- LoadString(g_hInstance, bi.idHeader, szHeader, ARRAYSIZE(szHeader));
- LoadString(g_hInstance, bi.idStatus, szStatus, ARRAYSIZE(szStatus));
- LPTSTR rgpstrHdr[] = { szHeader,
- szStatus };
- FormatMessage(FORMAT_MESSAGE_FROM_STRING |
- FORMAT_MESSAGE_ARGUMENT_ARRAY,
- szFmt,
- 0,0,
- pszTextHdr,
- cchTextHdr,
- (va_list *)rgpstrHdr);
- //
- // Create the body text.
- //
- LoadString(g_hInstance, IDS_BALLOONBODY_FORMAT, szFmt, ARRAYSIZE(szFmt));
- LoadString(g_hInstance, bi.idDirective, szDirective, ARRAYSIZE(szDirective));
- LPTSTR rgpstrBody[] = { szBody,
- szDirective };
- FormatMessage(FORMAT_MESSAGE_FROM_STRING |
- FORMAT_MESSAGE_ARGUMENT_ARRAY,
- szFmt,
- 0,0,
- pszTextBody,
- cchTextBody,
- (va_list *)rgpstrBody);
- if (NULL != pdwInfoFlags)
- {
- *pdwInfoFlags = bi.dwInfoFlags;
- }
- if (NULL != puTimeout)
- {
- CConfig& config = CConfig::GetSingleton();
- //
- // Balloon timeout is stored in the registry.
- //
- UINT uTimeout = (BTF_INITIAL & dwTextFlags) ? config.InitialBalloonTimeoutSeconds() :
- config.ReminderBalloonTimeoutSeconds();
- *puTimeout = uTimeout * 1000;
- }
- }
- }
- //
- // Find the index in s_rgBalloonInfo[] for a given state
- // and BTF_XXXXXX flag.
- // Returns -1 if no match in array.
- //
- int
- CSysTrayUI::GetBalloonInfoIndex(
- eSysTrayState state,
- DWORD dwTextFlags
- )
- {
- //
- // Scan the balloon info table until we find a record for the
- // specified systray UI state and BTF flags.
- //
- for (int i = 0; i < ARRAYSIZE(s_rgBalloonInfo); i++)
- {
- BalloonInfo& bi = s_rgBalloonInfo[i];
- if (bi.state == state &&
- bi.dwTextFlags == dwTextFlags &&
- 0 != bi.idHeader &&
- 0 != bi.idStatus &&
- 0 != bi.idBody &&
- 0 != bi.idDirective)
- {
- return i;
- }
- }
- return -1;
- }
- //
- // Determine if a balloon should not be displayed for a particular
- // UI state transition.
- //
- bool
- CSysTrayUI::SupressBalloon(
- eSysTrayState statePrev,
- eSysTrayState state
- )
- {
- for (int i = 0; i < ARRAYSIZE(s_rgBalloonSupression); i++)
- {
- if (statePrev == s_rgBalloonSupression[i].stateFrom &&
- state == s_rgBalloonSupression[i].stateTo)
- {
- return true;
- }
- }
- return false;
- }
- //
- // Do we have balloon text for a given state and balloon style?
- // state is one of the STS_XXXXX flags.
- // dwTextFlags is a mask of BTF_XXXXX flag bits.
- //
- bool
- CSysTrayUI::StateHasBalloonText(
- eSysTrayState state,
- DWORD dwTextFlags
- )
- {
- return (-1 != GetBalloonInfoIndex(state, dwTextFlags));
- }
- LPTSTR
- CSysTrayUI::GetTooltipText(
- eSysTrayState state,
- LPTSTR pszText,
- int cchText
- )
- {
- *pszText = TEXT('');
- //
- // Scan the tooltip info table until we find a record for the
- // specified systray UI state.
- //
- for (int i = 0; i < ARRAYSIZE(s_rgTooltipInfo); i++)
- {
- TooltipInfo& tti = s_rgTooltipInfo[i];
- if (tti.state == state && 0 != tti.idTooltip)
- {
- TCHAR szTemp[MAX_PATH];
- int cchHeader = LoadString(g_hInstance, IDS_TT_HEADER, szTemp, ARRAYSIZE(szTemp));
- if (STS_OFFLINE == state || STS_DIRTY == state || STS_SERVERBACK == state)
- {
- //
- // State has only one server associated with it so that means we'll
- // be including it in the tooltip text. Embed the server name in it.
- //
- TCHAR szFmt[160];
- LPTSTR rgpstr[] = { m_szServer };
- LoadString(g_hInstance, tti.idTooltip, szFmt, ARRAYSIZE(szFmt));
- FormatMessage(FORMAT_MESSAGE_FROM_STRING |
- FORMAT_MESSAGE_ARGUMENT_ARRAY,
- szFmt,
- 0,0,
- szTemp + cchHeader,
- ARRAYSIZE(szTemp) - cchHeader,
- (va_list *)rgpstr);
- }
- else
- {
- //
- // State has multiple servers associated with it so that means
- // there's no name embedded in the tooltip. It's just a simple string
- // loaded from a text resource.
- //
- LoadString(g_hInstance,
- tti.idTooltip,
- szTemp + cchHeader,
- ARRAYSIZE(szTemp) - cchHeader);
- }
- lstrcpyn(pszText, szTemp, cchText);
- }
- }
- return pszText;
- }
- //
- // Stop the flashing icon by killing the timer.
- //
- void
- CSysTrayUI::KillIconFlashTimer(
- void
- )
- {
- //
- // Force a final update so we're displaying the proper icon then
- // kill the timer.
- //
- if (0 != m_idFlashingTimer)
- {
- KillTimer(m_hwndNotify, m_idFlashingTimer);
- m_idFlashingTimer = 0;
- }
- }
- //
- // Called by the OS each time the icon flash timer period expires.
- // I use this rather than handling a WM_TIMER message so that
- // timer processing is contained within the CSysTrayUI class.
- //
- VOID CALLBACK
- CSysTrayUI::FlashTimerProc(
- HWND hwnd,
- UINT uMsg,
- UINT_PTR idEvent,
- DWORD dwTime
- )
- {
- CSysTrayUI::GetInstance().HandleFlashTimer();
- }
- void
- CSysTrayUI::HandleFlashTimer(
- void
- )
- {
- if (IconFlashedLongEnough())
- {
- //
- // Kill the icon flashing timer and the icon will stop flashing.
- // This doesn't actually kill the timer yet.
- //
- STDBGOUT((2, TEXT("Killing icon flash timer")));
- m_bFlashOverlay = true;
- UpdateSysTray(UF_FLASHICON);
- KillIconFlashTimer();
- }