cscst.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 129k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //+-------------------------------------------------------------------------
  2. //
  3. //  Microsoft Windows
  4. //
  5. //  Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. //  File:       cscst.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include <shellp.h>     // STR_DESKTOPCLASS
  13. #ifdef REPORT_DEVICE_CHANGES
  14. #   include <dbt.h>        // Device change notifications.
  15. #endif // REPORT_DEVICE_CHANGES
  16. #include <sddl.h>       // For ConvertStringSidToSid
  17. #include "cscst.h"
  18. #include "options.h"
  19. #include "statdlg.h"    // CStatusDlg
  20. #include "uihooks.h"    // Self-host notifications
  21. #include "folder.h"
  22. #include "eventlog.h"
  23. #include "msg.h"
  24. #include "purge.h"
  25. #include "security.h"
  26. #if DBG
  27. //
  28. // This code is used to manage the hidden window when we 
  29. // unhide it and display debug output to it via STDBGOUT().
  30. //
  31. #include <commdlg.h>
  32. #include <stdarg.h>
  33. const TCHAR c_szSysTrayOutput[] = TEXT("SysTrayOutput");
  34. int STDebugLevel(void);
  35. void STDebugOnLogEvent(HWND hwndList, LPCTSTR pszText);
  36. void STDebugSaveListboxContent(HWND hwndParent);
  37. DWORD STDebugOpenNetCacheKey(DWORD dwAccess, HKEY *phkey);
  38. #endif // DBG
  39. //
  40. // Size of systray icons.
  41. //
  42. #define CSC_ICON_CX             16
  43. #define CSC_ICON_CY             16
  44. //
  45. // Timer IDs are arbitrary.
  46. //
  47. #define ID_TIMER_FLASHICON    2953
  48. #define ID_TIMER_REMINDER     2954
  49. #define ID_TIMER_STATECHANGE  2955
  50. // Prototypes
  51. void ApplyAdminFolderPolicy(void);   // in admin.cpp
  52. void _RefreshAllExplorerWindows(LPCTSTR pszServer);
  53. // Globals
  54. static HWND g_hWndNotification = NULL;
  55. extern HWND g_hwndStatusDlg;    // in statdlg.cpp
  56. HANDLE g_hToken = NULL;
  57. #ifdef REPORT_DEVICE_CHANGES
  58. HDEVNOTIFY g_hDevNotify = NULL;
  59. #endif // REPORT_DEVICE_CHANGES
  60. //
  61. // RAS Autodial API.
  62. //
  63. typedef BOOL (WINAPI * PFNHLPNBCONNECTION)(LPCTSTR);
  64. #if DBG
  65. //
  66. // Provide some text-form names for state and input values
  67. // to support debug output.  The order of these corresponds 
  68. // to the STS_XXXXX enumeration.
  69. //
  70. LPCTSTR g_pszSysTrayStates[] =      { TEXT("STS_INVALID"),
  71.                                       TEXT("STS_ONLINE"),
  72.                                       TEXT("STS_DIRTY"),
  73.                                       TEXT("STS_MDIRTY"),
  74.                                       TEXT("STS_SERVERBACK"),
  75.                                       TEXT("STS_MSERVERBACK"),
  76.                                       TEXT("STS_OFFLINE"),
  77.                                       TEXT("STS_MOFFLINE"),
  78.                                       TEXT("STS_NONET") };
  79. //
  80. // A simple function to translate a state value to a string.
  81. //
  82. LPCTSTR SysTrayStateStr(eSysTrayState s)
  83. {
  84.     return g_pszSysTrayStates[int(s)];
  85. }
  86. #endif
  87. //
  88. // A simple dynamic list of server names.  A name can be provided
  89. // as either a "\server" or "\servershare" and only the server
  90. // part "\server" is stored.
  91. //
  92. class CServerList
  93. {
  94. public:
  95.     CServerList(void)
  96.         : m_hdpa(DPA_Create(10)) { }
  97.     ~CServerList(void);
  98.     bool Add(LPCTSTR pszServer);
  99.     void Remove(LPCTSTR pszServer);
  100.     void Clear(void);
  101.     int Find(LPCTSTR pszServer);
  102.     int Count(void) const;
  103.     LPCTSTR Get(int iItem) const;
  104.     bool Exists(LPCTSTR pszServer)
  105.         { return -1 != Find(pszServer); }
  106. private:
  107.     HDPA m_hdpa;
  108.     void GetServerFromPath(LPCTSTR pszPath, LPTSTR pszServer, int cchServer);
  109.     //
  110.     // Prevent copy.
  111.     //
  112.     CServerList(const CServerList& rhs);
  113.     CServerList& operator = (const CServerList& rhs);
  114. };
  115. //
  116. // The class that translates CSC agent input and cache status into a subsequent
  117. // systray UI state.  Originally this was a table-driven state machine
  118. // (hence the name).  It later proved sufficient to do a simple scan of cache
  119. // status and determine UI state based on the statistics obtained.  The name
  120. // has been retained for lack of something better.
  121. //
  122. class CStateMachine
  123. {
  124. public:
  125.     CStateMachine(bool bNoNet) : m_bNoNet(bNoNet) { }
  126.     //
  127.     // This is THE function for converting CSC agent input (or a 
  128.     // simple status check) into a systray icon state.
  129.     //
  130.     eSysTrayState TranslateInput(UINT uMsg, LPTSTR pszShare, UINT cchShare);
  131.     void PingServers();
  132.     bool ServerPendingReconnection(LPCTSTR pszServer)
  133.         { return m_PendingReconList.Add(pszServer); }
  134.     void ServerReconnected(LPCTSTR pszServer)
  135.         { m_PendingReconList.Remove(pszServer); }
  136.     void ServerUnavailable(LPCTSTR pszServer)
  137.         { m_PendingReconList.Remove(pszServer); }
  138.     void AllServersUnavailable(void)
  139.         { m_PendingReconList.Clear(); }
  140.     bool IsServerPendingReconnection(LPCTSTR pszServer)
  141.         { return m_PendingReconList.Exists(pszServer); }
  142. private:
  143.     CServerList m_PendingReconList;
  144.     bool        m_bNoNet;
  145.     //
  146.     // Some helper functions for decoding CSC share status values.
  147.     //
  148.     bool ShareIsOffline(DWORD dwCscStatus) const
  149.     {
  150.         return (0 != (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwCscStatus));
  151.     }
  152.     bool ShareHasFiles(LPCTSTR pszShare, bool *pbModified = NULL, bool *pbOpen = NULL) const;
  153.     //
  154.     // Prevent copy.
  155.     //
  156.     CStateMachine(const CStateMachine& rhs);
  157.     CStateMachine& operator = (const CStateMachine& rhs);
  158. };
  159. //
  160. // The CSysTrayUI class encapsulates the manipulation of the systray icon
  161. // so that the rest of the CSCUI code is exposed to only a narrow interface
  162. // to the systray.  It also maintains state information to control flashing
  163. // of the systray icon.   All flashing processing is provided by this class.
  164. //
  165. class CSysTrayUI
  166. {
  167. public:
  168.     ~CSysTrayUI(void);
  169.     //
  170.     // Set the state of the systray icon.  This will only change the
  171.     // icon if the state has changed.  Therefore this function can be
  172.     // called without worrying about excessive redundant updates to 
  173.     // the display.
  174.     //
  175.     bool SetState(eSysTrayState state, LPCTSTR pszServer = NULL);
  176.     //
  177.     // Retrieve the current "state" of the systray UI.  The state
  178.     // is one of the STS_XXXXX codes.
  179.     //
  180.     eSysTrayState GetState(void) const
  181.         { return m_state; }
  182.     //
  183.     // Retrieve the server name to be used in CSCUI elements.
  184.     // If the server name string is empty, that means there are
  185.     // multiple servers in the given state.
  186.     //
  187.     LPCTSTR GetServerName(void) const
  188.         { return m_szServer; }
  189.     //
  190.     // Show the balloon text for the current systray state.
  191.     //
  192.     void ShowReminderBalloon(void);
  193.     //
  194.     // Reset the reminder timer.
  195.     //
  196.     void ResetReminderTimer(bool bRestart);
  197.     //
  198.     // Make any adjustments when a WM_WININICHANGE is received.
  199.     //
  200.     void OnWinIniChange(LPCTSTR pszSection);
  201.     //
  202.     //
  203.     // Get a reference to THE singleton instance.
  204.     //
  205.     static CSysTrayUI& GetInstance(void);
  206. private:
  207.     //
  208.     // A minimal autoptr class to ensure the singleton instance
  209.     // is deleted.
  210.     //
  211.     class autoptr
  212.     {
  213.         public:
  214.             autoptr(void)
  215.                 : m_ptr(NULL) { }
  216.             ~autoptr(void)
  217.                 { delete m_ptr; }
  218.             CSysTrayUI* Get(void) const
  219.                 { return m_ptr; }
  220.             void Set(CSysTrayUI *p)
  221.                 { delete m_ptr; m_ptr = p; }
  222.         private:
  223.             CSysTrayUI *m_ptr;
  224.             autoptr(const autoptr& rhs);
  225.             autoptr& operator = (const autoptr& rhs);
  226.     };
  227.     //
  228.     // Icon info maintained for each UI state.
  229.     //
  230.     struct IconInfo 
  231.     {
  232.         HICON hIcon;           // Handle to icon to display in this state.
  233.         UINT  idIcon;          // ID of icon to display in this state.
  234.         int   iFlashTimeout;   // 0 == No icon flash.  Time is in millisec.
  235.     };
  236.     //
  237.     // Info maintained to describe the various balloon text messages.
  238.     // Combination of state and dwTextFlags are the table keys.
  239.     //
  240.     struct BalloonInfo
  241.     {
  242.         eSysTrayState state;     // SysTray state value.
  243.         DWORD dwTextFlags;       // BTF_XXXXX flags.
  244.         DWORD dwInfoFlags;       // NIIF_XXXXX flag.
  245.         UINT  idHeader;          // Res id for header part.
  246.         UINT  idStatus;          // Res id for status part.
  247.         UINT  idBody;            // Res id for body part.
  248.         UINT  idDirective;       // Res id for directive part.
  249.     };
  250.     //
  251.     // Info maintained to describe the various tooltip text messages.
  252.     //
  253.     struct TooltipInfo
  254.     {
  255.         eSysTrayState state;     // SysTray state value.
  256.         UINT idTooltip;          // Tooltip text resource ID.
  257.     };
  258.     //
  259.     // Info maintained for special-case supression of systray balloons.
  260.     // There are some state transitions that shouldn't generate a balloon.
  261.     // This structure describes each entry in an array of supression info.
  262.     //
  263.     struct BalloonSupression
  264.     {
  265.         eSysTrayState stateFrom; // Transitioning from this state.
  266.         eSysTrayState stateTo;   // Transitioning to this state.
  267.     };
  268.     //
  269.     // Enumeration for controlling what's done to the systray on update.
  270.     //
  271.     enum eUpdateFlags { UF_ICON      = 0x00000001,   // Update the icon.
  272.                         UF_FLASHICON = 0x00000002,   // Flash the icon.
  273.                         UF_BALLOON   = 0x00000004,   // Show the balloon.
  274.                         UF_REMINDER  = 0x00000008 }; // Balloon is a reminder.
  275.     //
  276.     // These flags relate a cache state to balloon text message.
  277.     // They fit into an encoded mask where the lowest 4 bits
  278.     // contain the eSysTrayState (STS_XXXXXX) code.
  279.     //
  280.     //      (STS_OFFLINE | BTF_INITIAL) 
  281.     //
  282.     // would indicate the condition where the state is "offline" for 
  283.     // a single server and the text to be displayed is for the initial
  284.     // notification.
  285.     //
  286.     enum eBalloonTextFlags { 
  287.                              BTF_INITIAL = 0x00000010, // Initial notification
  288.                              BTF_REMIND  = 0x00000020  // Reminder
  289.                            };
  290.     static IconInfo    s_rgIconInfo[];       // The icon info
  291.     static BalloonInfo s_rgBalloonInfo[];    // Balloon configuration info.
  292.     static TooltipInfo s_rgTooltipInfo[];    // Tooltip configuration info.
  293.     static BalloonSupression s_rgBalloonSupression[];
  294.     static const int   s_iMinStateChangeInterval;
  295.     UINT_PTR           m_idFlashingTimer;    // Flash timer id.
  296.     UINT_PTR           m_idReminderTimer;    // Timer for showing reminder balloons.
  297.     UINT_PTR           m_idStateChangeTimer; // Timer for queued state changes.
  298.     UINT               m_iIconFlashTime;     // Period of icon flashes (ms).
  299.     HICON&             m_hIconNoOverlay;     // Icon used for flashing.
  300.     HWND               m_hwndNotify;         // Notification window.
  301.     DWORD              m_dwFlashingExpires;  // Tick count when flash timer expires.
  302.     DWORD              m_dwNextStateChange;  // Tick count for next queued state change.
  303.     TCHAR              m_szServer[MAX_PATH]; // Servername for balloon messages.
  304.     TCHAR              m_szServerQueued[MAX_PATH];
  305.     eSysTrayState      m_state;              // Remember current state.
  306.     eSysTrayState      m_statePrev;
  307.     eSysTrayState      m_stateQueued;        
  308.     bool               m_bFlashOverlay;      // Alternates 0,1 (1 == display overlay, 0 == don't)
  309.     bool               m_bActive;            // 1 == we have an active icon in systray.
  310.     //
  311.     // Enforce singleton existance by making construction
  312.     // and copy operations private.
  313.     //
  314.     CSysTrayUI(HWND hwndNotify);
  315.     CSysTrayUI(const CSysTrayUI& rhs);
  316.     CSysTrayUI& operator = (const CSysTrayUI& rhs);
  317.     void UpdateSysTray(eUpdateFlags uFlags, LPCTSTR pszServer = NULL);
  318.     int GetBalloonInfoIndex(eSysTrayState state, DWORD dwTextFlags);
  319.     bool StateHasBalloonText(eSysTrayState state, DWORD dwTextFlags);
  320.     void GetBalloonInfo(eSysTrayState state,
  321.                         DWORD dwTextFlags,
  322.                         LPTSTR pszTextHdr,
  323.                         int cchTextHdr,
  324.                         LPTSTR pszTextBody,
  325.                         int cchTextBody,
  326.                         DWORD *pdwInfoFlags,
  327.                         UINT *puTimeout);
  328.     
  329.     bool SupressBalloon(eSysTrayState statePrev, eSysTrayState state);
  330.     LPTSTR GetTooltipText(eSysTrayState state,
  331.                           LPTSTR pszText,
  332.                           int cchText);
  333.     bool IconFlashedLongEnough(void);
  334.     void KillIconFlashTimer(void);
  335.     void HandleFlashTimer(void);
  336.     void OnStateChangeTimerExpired(void);
  337.     static VOID CALLBACK FlashTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
  338.     static VOID CALLBACK ReminderTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
  339.     static VOID CALLBACK StateChangeTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
  340. };
  341. #define ICONFLASH_FOREVER     (UINT(-1))
  342. #define ICONFLASH_NONE        0
  343. //
  344. // These rows must stay in the same order as the STS_XXXXX enumeration members.
  345. // For flash timeout values, 0 == no flash, -1 == never stop.
  346. // Everything else is a timeout in milliseconds.
  347. //
  348. CSysTrayUI::IconInfo
  349. CSysTrayUI::s_rgIconInfo[] = {
  350.     { NULL, 0,                  ICONFLASH_NONE    },  /* STS_INVALID     */
  351.     { NULL, 0,                  ICONFLASH_NONE    },  /* STS_ONLINE      */ 
  352.     { NULL, IDI_CSCWARNING,     ICONFLASH_FOREVER },  /* STS_DIRTY       */ 
  353.     { NULL, IDI_CSCWARNING,     ICONFLASH_FOREVER },  /* STS_MDIRTY      */ 
  354.     { NULL, IDI_CSCINFORMATION, ICONFLASH_NONE    },  /* STS_SERVERBACK  */ 
  355.     { NULL, IDI_CSCINFORMATION, ICONFLASH_NONE    },  /* STS_MSERVERBACK */ 
  356.     { NULL, IDI_CSCNORMAL,      ICONFLASH_NONE    },  /* STS_OFFLINE     */ 
  357.     { NULL, IDI_CSCNORMAL,      ICONFLASH_NONE    },  /* STS_MOFFLINE    */ 
  358.     { NULL, IDI_CSCNORMAL,      ICONFLASH_NONE    }}; /* STS_NONET       */
  359. //
  360. // This table describes all information related to displaying the systray balloons.
  361. // The first two columns are the keys to each record; those being a systray UI state
  362. // and a mask of balloon-text flags.
  363. // Notes:
  364. //        1. There's no balloon for STS_NONET.  We found that the user's response is
  365. //           duh, I know I have no net.  
  366. //
  367. //
  368. CSysTrayUI::BalloonInfo
  369. CSysTrayUI::s_rgBalloonInfo[] = {
  370.     { STS_INVALID,    BTF_INITIAL, NIIF_NONE,    0,                 0,                   0,                        0,                   },
  371.     { STS_INVALID,    BTF_REMIND,  NIIF_NONE,    0,                 0,                   0,                        0,                   },
  372.     { STS_OFFLINE,    BTF_INITIAL, NIIF_INFO,    IDS_BTHDR_INITIAL, IDS_BTSTA_OFFLINE,   IDS_BTBOD_OFFLINE,        IDS_BTDIR_VIEWSTATUS },
  373.     { STS_MOFFLINE,   BTF_INITIAL, NIIF_INFO,    IDS_BTHDR_INITIAL, IDS_BTSTA_OFFLINE,   IDS_BTBOD_OFFLINE_M,      IDS_BTDIR_VIEWSTATUS },
  374.     { STS_OFFLINE,    BTF_REMIND,  NIIF_INFO,    IDS_BTHDR_REMIND,  IDS_BTSTA_OFFLINE,   IDS_BTBOD_STILLOFFLINE,   IDS_BTDIR_VIEWSTATUS },
  375.     { STS_MOFFLINE,   BTF_REMIND,  NIIF_INFO,    IDS_BTHDR_REMIND,  IDS_BTSTA_OFFLINE,   IDS_BTBOD_STILLOFFLINE_M, IDS_BTDIR_VIEWSTATUS },
  376. //    { STS_SERVERBACK, BTF_INITIAL, NIIF_INFO,    IDS_BTHDR_INITIAL, IDS_BTSTA_SERVERBACK,IDS_BTBOD_SERVERBACK,     IDS_BTDIR_RECONNECT  },
  377. //    { STS_MSERVERBACK,BTF_INITIAL, NIIF_INFO,    IDS_BTHDR_INITIAL, IDS_BTSTA_SERVERBACK,IDS_BTBOD_SERVERBACK_M,   IDS_BTDIR_RECONNECT  },
  378.     { STS_SERVERBACK, BTF_REMIND,  NIIF_INFO,    IDS_BTHDR_REMIND,  IDS_BTSTA_SERVERBACK,IDS_BTBOD_STILLBACK,      IDS_BTDIR_RECONNECT  },
  379.     { STS_MSERVERBACK,BTF_REMIND,  NIIF_INFO,    IDS_BTHDR_REMIND,  IDS_BTSTA_SERVERBACK,IDS_BTBOD_STILLBACK_M,    IDS_BTDIR_RECONNECT  },
  380.     { STS_DIRTY,      BTF_INITIAL, NIIF_WARNING, IDS_BTHDR_INITIAL, IDS_BTSTA_DIRTY,     IDS_BTBOD_DIRTY,          IDS_BTDIR_SYNC       },
  381.     { STS_MDIRTY,     BTF_INITIAL, NIIF_WARNING, IDS_BTHDR_INITIAL, IDS_BTSTA_DIRTY,     IDS_BTBOD_DIRTY_M,        IDS_BTDIR_SYNC       },
  382.     { STS_DIRTY,      BTF_REMIND,  NIIF_WARNING, IDS_BTHDR_REMIND,  IDS_BTSTA_DIRTY,     IDS_BTBOD_STILLDIRTY,     IDS_BTDIR_SYNC       },
  383.     { STS_MDIRTY,     BTF_REMIND,  NIIF_WARNING, IDS_BTHDR_REMIND,  IDS_BTSTA_DIRTY,     IDS_BTBOD_STILLDIRTY_M,   IDS_BTDIR_SYNC       }
  384. };
  385. //
  386. // This table lists all of the state transitions that do not generate balloons.
  387. // Ideally, I would have a true state machine to control the UI for any given state transition.
  388. // However, since we have quite a few states and since you can transition from any state
  389. // to almost any other state, the state transition table would be large and confusing
  390. // to read.  Instead, I've taken the position to assume all state transitions generate
  391. // the balloon UI associated with the "to" state unless the transition is listed
  392. // in this table.
  393. //
  394. CSysTrayUI::BalloonSupression
  395. CSysTrayUI::s_rgBalloonSupression[] = {
  396.     { STS_MOFFLINE, STS_OFFLINE  },
  397.     { STS_NONET,    STS_OFFLINE  },
  398.     { STS_NONET,    STS_MOFFLINE }
  399.     };
  400. //
  401. // This table describes all information related to displaying tooltip text
  402. // for the systray icon.
  403. //
  404. CSysTrayUI::TooltipInfo
  405. CSysTrayUI::s_rgTooltipInfo[] = {
  406.     { STS_INVALID,     0                   },
  407.     { STS_OFFLINE,     IDS_TT_OFFLINE      },
  408.     { STS_MOFFLINE,    IDS_TT_OFFLINE_M    },
  409.     { STS_SERVERBACK,  IDS_TT_SERVERBACK   },
  410.     { STS_MSERVERBACK, IDS_TT_SERVERBACK_M },
  411.     { STS_DIRTY,       IDS_TT_DIRTY        },
  412.     { STS_MDIRTY,      IDS_TT_DIRTY_M      },
  413.     { STS_NONET,       IDS_TT_NONET        }
  414. };
  415. //
  416. // Wrap the CEventLog class so we can control log initialization
  417. // and also filter events based on the CSCUI event logging level.
  418. // The idea here is to create a CscuiEventLog object whenever you
  419. // want to write to the event log.  The ReportEvent member has
  420. // been designed to handle log initialization as well as filtering
  421. // message output to respect the current CSCUI event logging level
  422. // set in the registry/policy.  It's recommended that the 
  423. // CscuiEventLog object be created as a local variable so that 
  424. // once the reporting is complete, the object is destroyed and
  425. // the system event log handle is closed.
  426. //
  427. class CscuiEventLog
  428. {
  429. public:
  430.     CscuiEventLog(void)
  431.         : m_iEventLoggingLevel(CConfig::GetSingleton().EventLoggingLevel()) { }
  432.     ~CscuiEventLog(void) { }
  433.     HRESULT ReportEvent(WORD wType,
  434.                         WORD wCategory,
  435.                         DWORD dwEventID,
  436.                         PSID lpUserSid = NULL,
  437.                         LPVOID pvRawData = NULL,
  438.                         DWORD cbRawData = 0);
  439.     bool LoggingEnabled(void) const
  440.         { return 0 < m_iEventLoggingLevel; }
  441.     void Push(HRESULT hr, CEventLog::eFmt fmt)
  442.         { m_log.Push(hr, fmt); }
  443.     void Push(LPCTSTR psz)
  444.         { m_log.Push(psz); }
  445. private:
  446.     CEventLog m_log;
  447.     int       m_iEventLoggingLevel;
  448. };
  449. //-----------------------------------------------------------------------------
  450. // CscuiEventLog member functions.
  451. //-----------------------------------------------------------------------------
  452. HRESULT 
  453. CscuiEventLog::ReportEvent(
  454.     WORD wType,
  455.     WORD wCategory,
  456.     DWORD dwEventID,
  457.     PSID lpUserSid,
  458.     LPVOID pvRawData,
  459.     DWORD cbRawData
  460.     )
  461. {
  462.     //
  463.     // Add to this table if you add new event messages.
  464.     //
  465.     static const struct
  466.     {
  467.         DWORD dwEventID;
  468.         int   iLevel;
  469.     } rgEventInfo[] = {{ MSG_I_SERVER_OFFLINE,       1 },
  470.                        { MSG_I_SERVER_AVAILABLE,     3 },
  471.                        { MSG_I_NET_STOPPED,          2 },
  472.                        { MSG_I_NET_STARTED,          2 },
  473.                        { MSG_E_CACHE_CORRUPTED,      0 },
  474.                        { MSG_I_SERVER_AUTORECONNECT, 3 }};
  475.     int iLevel = CConfig::GetSingleton().EventLoggingLevel();
  476.     for (int i = 0; i < ARRAYSIZE(rgEventInfo); i++)
  477.     {
  478.         if (dwEventID == rgEventInfo[i].dwEventID && iLevel >= rgEventInfo[i].iLevel)
  479.         {
  480.             if (SUCCEEDED(m_log.Initialize(TEXT("Offline Files"))))
  481.             {
  482.                 return m_log.ReportEvent(wType, 
  483.                                          wCategory, 
  484.                                          dwEventID, 
  485.                                          lpUserSid, 
  486.                                          pvRawData, 
  487.                                          cbRawData);
  488.             }
  489.         }
  490.     }
  491.     return S_FALSE;
  492. }
  493. //-----------------------------------------------------------------------------
  494. // CServerList member functions.
  495. //-----------------------------------------------------------------------------
  496. CServerList::~CServerList(
  497.     void
  498.     )
  499. {
  500.     if (NULL != m_hdpa)
  501.     {
  502.         int cEntries = DPA_GetPtrCount(m_hdpa);
  503.         LPTSTR pszEntry;
  504.         for (int i = 0; i < cEntries; i++) 
  505.         {
  506.             pszEntry = (LPTSTR)DPA_GetPtr(m_hdpa, i);
  507.             if (NULL != pszEntry)
  508.                 LocalFree(pszEntry);
  509.         }
  510.         DPA_Destroy(m_hdpa);
  511.     }
  512. }
  513. void
  514. CServerList::GetServerFromPath(
  515.     LPCTSTR pszPath,
  516.     LPTSTR pszServer,
  517.     int cchServer
  518.     )
  519. {
  520.     TCHAR szServer[MAX_PATH];
  521.     lstrcpyn(szServer, pszPath, ARRAYSIZE(szServer));
  522.     PathAddBackslash(szServer);
  523.     PathStripToRoot(szServer);
  524.     LPTSTR pszLastBackslash = StrRChr(szServer, szServer + lstrlen(szServer), TEXT('\'));
  525.     if (NULL != pszLastBackslash && pszLastBackslash > (szServer + 2))
  526.         *pszLastBackslash = TEXT('');
  527.     lstrcpyn(pszServer, szServer, cchServer);
  528. }
  529.     
  530. bool
  531. CServerList::Add(
  532.     LPCTSTR pszServer
  533.     )
  534. {
  535.     if (NULL != m_hdpa)
  536.     {
  537.         if (!Exists(pszServer))
  538.         {
  539.             int cchEntry = lstrlen(pszServer) + 1;
  540.             LPTSTR pszEntry = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * cchEntry);
  541.             if (NULL != pszEntry)
  542.             {
  543.                 GetServerFromPath(pszServer, pszEntry, cchEntry);
  544.                 if (-1 != DPA_AppendPtr(m_hdpa, pszEntry))
  545.                     return true;
  546.                 //
  547.                 // Addition to DPA failed.  Delete the string buffer.
  548.                 //
  549.                 LocalFree(pszEntry);
  550.             }
  551.         }
  552.     }
  553.     return false;
  554. }
  555. void
  556. CServerList::Remove(
  557.     LPCTSTR pszServer
  558.     )
  559. {
  560.     int iEntry = Find(pszServer);
  561.     if (-1 != iEntry)
  562.     {
  563.         LPTSTR pszEntry = (LPTSTR)DPA_DeletePtr(m_hdpa, iEntry);
  564.         if (NULL != pszEntry)
  565.             LocalFree(pszEntry);
  566.     }
  567. }
  568. LPCTSTR
  569. CServerList::Get(
  570.     int iItem
  571.     ) const
  572. {
  573.     if (NULL != m_hdpa)
  574.         return (LPCTSTR)DPA_GetPtr(m_hdpa, iItem);
  575.     return NULL;
  576. }
  577. int
  578. CServerList::Count(
  579.     void
  580.     ) const
  581. {
  582.     if (NULL != m_hdpa)
  583.         return DPA_GetPtrCount(m_hdpa);
  584.     return 0;
  585. }
  586.                 
  587. //
  588. // Locate a server name in the "pending reconnection" list.
  589. // pszServer can either be "\server" or "\servershare".
  590. //
  591. // Returns:  Index of entry if found.  -1 if not found.
  592. //
  593. int
  594. CServerList::Find(
  595.     LPCTSTR pszServer
  596.     )
  597. {
  598.     TCHAR szServer[MAX_PATH];
  599.     GetServerFromPath(pszServer, szServer, ARRAYSIZE(szServer));
  600.     if (NULL != m_hdpa)
  601.     {
  602.         int cEntries = DPA_GetPtrCount(m_hdpa);
  603.         LPTSTR pszEntry;
  604.         for (int i = 0; i < cEntries; i++) 
  605.         {
  606.             pszEntry = (LPTSTR)DPA_GetPtr(m_hdpa, i);
  607.             if (NULL != pszEntry)
  608.             {
  609.                 if (0 == lstrcmpi(pszEntry, szServer))
  610.                     return i;
  611.             }
  612.         }
  613.     }
  614.     return -1;        
  615. }
  616. void
  617. CServerList::Clear( 
  618.     void
  619.     )
  620. {
  621.     if (NULL != m_hdpa)
  622.     {
  623.         int cEntries = DPA_GetPtrCount(m_hdpa);
  624.         LPTSTR pszEntry;
  625.         for (int i = 0; i < cEntries; i++) 
  626.         {
  627.             pszEntry = (LPTSTR)DPA_DeletePtr(m_hdpa, i);
  628.             if (NULL != pszEntry)
  629.             {
  630.                 LocalFree(pszEntry);
  631.             }
  632.         }
  633.     }
  634. }
  635. //-----------------------------------------------------------------------------
  636. // CStateMachine member functions.
  637. //-----------------------------------------------------------------------------
  638. //
  639. // Translates a STWM_XXXXX message from the CSC agent into a systray UI state
  640. // code.  The caller also provides a buffer to a server name.  If we find
  641. // a "single server" condition in the cache (i.e. one server is dirty, one
  642. // server is offline etc), then we write the name of this server to this
  643. // buffer.  Otherwise, the buffer remains unchanged.  The goal here is to 
  644. // end up with a buffer containing the name of the applicable server when
  645. // we have one of these one-server conditions.  Ultimately, the server name
  646. // is included in the tray balloon text message.
  647. //
  648. // The function returns one of the STS_XXXXX UI status codes.
  649. //
  650. // This function is rather long.  Much longer than I like a function to be.
  651. // I've tried to break it up into smaller pieces but any chunks were pretty
  652. // much arbitrary.  Without a good logical breakdown, that doesn't make much
  653. // sense.  Even with it's length, it's not a complex function.  It merely 
  654. // enumerates shares in the cache gathering statistics along the way.  From
  655. // these statistics, it decides what the next UI state should be.
  656. //
  657. eSysTrayState
  658. CStateMachine::TranslateInput(
  659.     UINT uMsg,
  660.     LPTSTR pszServer,
  661.     UINT cchServer
  662.     )
  663. {
  664.     //
  665.     // Since this cscui code is running all the time, we don't want to keep 
  666.     // a handle to the event log open.  Therefore, we use this CscuiEventLog
  667.     // object to automatically close the log for us.  The ReportEvent member
  668.     // of CscuiEventLog handles all initialization of the log and determining
  669.     // if the event should actually be logged (depending upon the current CSCUI
  670.     // event logging level).
  671.     //
  672.     CscuiEventLog log;
  673.     bool bServerIsBack = false;
  674.   
  675.     if (STWM_CSCNETUP == uMsg)
  676.     {
  677.         m_bNoNet = false;
  678.         if (TEXT('') != *pszServer)
  679.         {
  680.             STDBGOUT((1, TEXT("Translating STWM_CSCNETUP for server "%s""), pszServer));
  681.             //
  682.             // Server reported back by the CSC agent.
  683.             // Add it's name to a persistent (in memory) list of
  684.             // servers available for reconnection.
  685.             // Also clear the "no net" flag.
  686.             //
  687.             bServerIsBack = true;
  688.             ServerPendingReconnection(pszServer);
  689.             if (log.LoggingEnabled())
  690.             {
  691.                 log.Push(pszServer);
  692.                 log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_SERVER_AVAILABLE);
  693.             }
  694.         }
  695.         else
  696.         {
  697.             STDBGOUT((1, TEXT("Translating STWM_CSCNETUP (no associated server)")));
  698.             if (log.LoggingEnabled())
  699.             {
  700.                 log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_NET_STARTED);
  701.             }
  702.         }
  703.     }
  704.     else if (STWM_CSCNETDOWN == uMsg)
  705.     {
  706.         //
  707.         // This is the only place where transitions from online to
  708.         // offline state are noted in the shell process. (CSCUISetState
  709.         // and OnQueryNetDown execute in WinLogon's process).
  710.         //
  711.         if (TEXT('') != *pszServer)
  712.         {
  713.             STDBGOUT((1, TEXT("Translating STWM_CSCNETDOWN for server "%s""), pszServer));
  714.             if (!m_bNoNet)
  715.             {
  716.                 LPTSTR pszTemp;
  717.                 if (LocalAllocString(&pszTemp, pszServer))
  718.                 {
  719.                     PostToSystray(PWM_REFRESH_SHELL, 0, (LPARAM)pszTemp);
  720.                 }                    
  721.             }                
  722.             //
  723.             // Server reported down by the CSC agent.
  724.             // Remove it's name from the persistent (in memory) list
  725.             // of servers available for reconnection.
  726.             //
  727.             ServerUnavailable(pszServer);
  728.             if (log.LoggingEnabled())
  729.             {
  730.                 log.Push(pszServer);
  731.                 log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_SERVER_OFFLINE);
  732.             }
  733.         }
  734.         else
  735.         {
  736.             STDBGOUT((1, TEXT("Translating STWM_CSCNETDOWN (no associated server)")));
  737.             //
  738.             // Entire network reported down by the CSC agent.
  739.             // Remove all names from the persistent (in memory) list
  740.             // of servers available for reconnection.  m_bNoNet is the only persistent
  741.             // state we have.  Once it is set, the only thing that can reset it
  742.             // is a STWM_CSCNETUP message from the CSC agent.
  743.             //
  744.             if (!m_bNoNet)
  745.                 PostToSystray(PWM_REFRESH_SHELL, 0, 0);
  746.                 
  747.             m_bNoNet = true;
  748.             AllServersUnavailable();
  749.             if (log.LoggingEnabled())
  750.             {
  751.                 log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_NET_STOPPED);
  752.             }
  753.         }
  754.     }
  755.     else if (STWM_STATUSCHECK == uMsg)
  756.     {
  757.         STDBGOUT((1, TEXT("Translating STWM_STATUSCHECK")));
  758.     }
  759.     else if (STWM_CACHE_CORRUPTED == uMsg)
  760.     {
  761.         //
  762.         // Note:  No check for LoggingEnabled().  We always log corrupted cache
  763.         //        regardless of logging level.
  764.         //
  765.         STDBGOUT((1, TEXT("Translating STWM_CACHE_CORRUPTED")));
  766.         log.ReportEvent(EVENTLOG_ERROR_TYPE, 0, MSG_E_CACHE_CORRUPTED);
  767.     }
  768.     //
  769.     // If CSC is disabled or the cache is empty, the default UI state
  770.     // is "online".
  771.     //
  772.     eSysTrayState state = STS_ONLINE;
  773.     if (IsCSCEnabled())
  774.     {
  775.         DWORD dwStatus;
  776.         DWORD dwPinCount;
  777.         DWORD dwHintFlags;
  778.         WIN32_FIND_DATA fd;
  779.         FILETIME ft;
  780.         CCscFindHandle hFind;
  781.         hFind = CacheFindFirst(NULL, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft);
  782.         if (hFind.IsValid())
  783.         {
  784.             //
  785.             // We need these three temporary name lists to reconcile a problem with
  786.             // the way the CSC cache and RDR are designed.  When we enumerate the cache,
  787.             // we enumerate individual shares in the cache.  Each share has some condition
  788.             // (i.e. dirty, offline etc) associated with it.  The problem is that the
  789.             // redirector handles things on a server basis.  So when a particular share
  790.             // is offline, in reality the entire server is offline.  We've decided that
  791.             // the UI should reflect things on a server (computer) basis so we need to 
  792.             // avoid including the states of multiple shares from the same server in
  793.             // our totals.  These three lists are used to store the names of servers
  794.             // with shares in one of the three states (offline, dirty, pending recon).
  795.             // If we enumerate a share with one of these states and find it already
  796.             // exists in the corresponding list, we don't include this share in the
  797.             // statistics.
  798.             // 
  799.             int cShares = 0;
  800.             CServerList OfflineList;
  801.             CServerList DirtyList;
  802.             CServerList BackList;
  803.             //
  804.             // If a server is back, assume we can auto-reconnect it.
  805.             //
  806.             bool bAutoReconnectServer = bServerIsBack;
  807.             TCHAR szAutoReconnectShare[MAX_PATH] = {0};
  808.             DWORD dwPathSpeed = 0;
  809.             do
  810.             {
  811.                 bool bShareIsOnServer       = boolify(PathIsPrefix(pszServer, fd.cFileName));
  812.                 bool bShareHasModifiedFiles = false;
  813.                 bool bShareHasOpenFiles     = false;
  814.                 //
  815.                 // A share participates in the systray UI calculations only if the 
  816.                 // share contains files OR the share is currently "offline".  
  817.                 // Because of the CSC database design, CSC doesn't remove a share 
  818.                 // entry after all it's files have been removed from the cache. 
  819.                 // Therefore we need this extra check to avoid including empty shares in the UI.
  820.                 //
  821.                 if (ShareHasFiles(fd.cFileName, &bShareHasModifiedFiles, &bShareHasOpenFiles) ||
  822.                     ShareIsOffline(dwStatus))
  823.                 {
  824.                     cShares++;
  825.                     if (bShareIsOnServer && (bShareHasModifiedFiles || bShareHasOpenFiles))
  826.                     {
  827.                         //
  828.                         // Auto-reconnect isn't allowed if one or more shares on the server
  829.                         // have open files or files modified offline.  Auto-reconnection
  830.                         // would put the cache into a dirty state.
  831.                         //
  832.                         bAutoReconnectServer = false;
  833.                     }
  834.                     //
  835.                     // A share can be in one of 4 states:
  836.                     //     Online
  837.                     //       Dirty
  838.                     //     Offline
  839.                     //       Pending reconnection ('back')
  840.                     //
  841.                     // Note that our definition of Dirty implies Online, and Pending
  842.                     // Reconnection implies Offline.  That is, an offline share is
  843.                     // never dirty and an online share is never pending reconnection.
  844.                     //
  845.                     //---------------------------------------------------------------------
  846.                     // Is the share online?
  847.                     //---------------------------------------------------------------------
  848.                     if (!ShareIsOffline(dwStatus))
  849.                     {
  850.                         //---------------------------------------------------------------------
  851.                         // Is the share dirty? (online + offline changes)
  852.                         //---------------------------------------------------------------------
  853.                         if (bShareHasModifiedFiles)
  854.                         {
  855.                             STDBGOUT((3, TEXT("Share "%s" is dirty (0x%08X)"), fd.cFileName, dwStatus));
  856.                             DirtyList.Add(fd.cFileName);
  857.                         }
  858.                         else
  859.                         {
  860.                             STDBGOUT((3, TEXT("Share "%s" is online (0x%08X)"), fd.cFileName, dwStatus));
  861.                         }
  862.                     }
  863.                     else    // Offline
  864.                     {
  865.                         //---------------------------------------------------------------------
  866.                         // Is the server back?
  867.                         //---------------------------------------------------------------------
  868.                         if (IsServerPendingReconnection(fd.cFileName))
  869.                         {
  870.                             STDBGOUT((3, TEXT("Share "%s" is pending reconnection (0x%08X)"), fd.cFileName, dwStatus));
  871.                             BackList.Add(fd.cFileName);
  872.                         }
  873.                         else
  874.                         {
  875.                             STDBGOUT((3, TEXT("Share "%s" is OFFLINE (0x%08X)"), fd.cFileName, dwStatus));
  876.                             OfflineList.Add(fd.cFileName);
  877.                         }
  878.                     }
  879.                 }
  880.                 if (!ShareIsOffline(dwStatus))
  881.                 {
  882.                     // It's online, so it can't be pending reconnection.
  883.                     ServerReconnected(fd.cFileName);
  884.                     // ...and there's no need to reconnect it.
  885.                     if (bShareIsOnServer)
  886.                         bAutoReconnectServer = false;
  887.                 }
  888.                 if (bAutoReconnectServer && bShareIsOnServer && TEXT('') == szAutoReconnectShare[0])
  889.                 {
  890.                     //
  891.                     // Remember the share name for possible auto-reconnection.
  892.                     // The transition API is TransitionServerOnline but it takes a share name.
  893.                     // Bad choice of names (IMO) but that's the way Shishir did it in the
  894.                     // CSC APIs. It can be any share on the server.
  895.                     //
  896.                     // However, it's possible to have defunct shares in the
  897.                     // database. Try to find one that's connectable.
  898.                     //
  899.                     if (CSCCheckShareOnlineEx(fd.cFileName, &dwPathSpeed))
  900.                     {
  901.                         STDBGOUT((3, TEXT("Share "%s" alive at %d00 bps"), fd.cFileName, dwPathSpeed));
  902.                         lstrcpyn(szAutoReconnectShare, fd.cFileName, ARRAYSIZE(szAutoReconnectShare));
  903.                     }
  904.                     else
  905.                     {
  906.                         STDBGOUT((3, TEXT("Share "%s" unreachable, error = %d"), fd.cFileName, GetLastError()));
  907.                     }
  908.                 }
  909.             }
  910.             while(CacheFindNext(hFind, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft));
  911.             if (bAutoReconnectServer)
  912.             {
  913.                 //---------------------------------------------------------------------
  914.                 // Handle auto-reconnection.
  915.                 //---------------------------------------------------------------------
  916.                 //
  917.                 if (TEXT('') != szAutoReconnectShare[0])
  918.                 {
  919.                     //
  920.                     // Server was reported "BACK" by the CSC agent and it has no open files
  921.                     // nor files modified offline and it's not on a slow link.  
  922.                     // This makes it a candidate for automatic reconnection.  Try it.
  923.                     //
  924.                     STDBGOUT((1, TEXT("Attempting to auto-reconnect "%s""), szAutoReconnectShare));
  925.                     if (TransitionShareOnline(szAutoReconnectShare, TRUE, TRUE, dwPathSpeed))
  926.                     {
  927.                         //
  928.                         // The server has been reconnected.  Remove it's name from the 
  929.                         // "pending reconnection" list.
  930.                         //
  931.                         ServerReconnected(pszServer);
  932.                         //
  933.                         // Remove this server from the temporary lists we've been keeping.
  934.                         //
  935.                         DirtyList.Remove(pszServer);
  936.                         BackList.Remove(pszServer);
  937.                         OfflineList.Remove(pszServer);
  938.                         if (log.LoggingEnabled())
  939.                         {
  940.                             log.Push(pszServer);
  941.                             log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_SERVER_AUTORECONNECT);
  942.                         }
  943.                     }
  944.                 }
  945.             }
  946.             int cDirty   = DirtyList.Count();
  947.             int cBack    = BackList.Count();
  948.             int cOffline = OfflineList.Count();
  949.             STDBGOUT((2, TEXT("Cache check server results: cShares = %d, cDirty = %d, cBack = %d, cOffline = %d"), 
  950.                      cShares, cDirty, cBack, cOffline));
  951.             //
  952.             // This code path is a waterfall where lower-priority states are overwritten
  953.             // by higher-priority states as they are encountered. The order of this array
  954.             // is important.  It's ordered by increasing priority (no net is 
  955.             // highest priority for systray UI).
  956.             //
  957.             CServerList *pServerList = NULL;
  958.             struct Criteria
  959.             {
  960.                 int           cnt;     // Number of applicable servers found.
  961.                 eSysTrayState state;   // Single-item UI state.
  962.                 eSysTrayState mstate;  // Multi-item UI state.
  963.                 CServerList *pList;    // Ptr to applicable list with server names.
  964.             } rgCriteria[] = { 
  965.                  { cOffline,                    STS_OFFLINE,    STS_MOFFLINE,    &OfflineList },
  966.                  { cBack,                       STS_SERVERBACK, STS_MSERVERBACK, &BackList    },
  967.                  { cDirty,                      STS_DIRTY,      STS_MDIRTY,      &DirtyList   },
  968.                  { cShares && m_bNoNet ? 1 : 0, STS_NONET,      STS_NONET,       NULL         }
  969.                  };
  970.             for (int i = 0; i < ARRAYSIZE(rgCriteria); i++)
  971.             {
  972.                 Criteria& c = rgCriteria[i];
  973.                 if (0 < c.cnt)
  974.                 {
  975.                     state = c.mstate;
  976.                     if (1 == c.cnt)
  977.                     {
  978.                         state = c.state;
  979.                         pServerList = NULL;
  980.                         if (NULL != c.pList && 1 == c.pList->Count())
  981.                         {
  982.                             pServerList = c.pList;
  983.                         }
  984.                     }
  985.                 }
  986.             }
  987.             if (NULL != pServerList)
  988.             {
  989.                 //
  990.                 // We had a single-server condition so write the server name
  991.                 // to the caller's server name buffer.
  992.                 // If we didn't have a single-server condition, the buffer
  993.                 // remains unchanged.
  994.                 //
  995.                 lstrcpyn(pszServer, pServerList->Get(0), cchServer);
  996.             }
  997.         }
  998.     }
  999.     STDBGOUT((1, TEXT("Translated to SysTray UI state %s"), SysTrayStateStr(state)));
  1000.     return state;
  1001. }
  1002. //
  1003. // Ping offline servers. If any are alive, update status and
  1004. // auto-reconnect them if possible.  This is typically done
  1005. // after a sync operation has completed.
  1006. //
  1007. DWORD WINAPI
  1008. _PingServersThread(LPVOID /*pThreadData*/)
  1009. {
  1010.     DWORD dwStatus;
  1011.     WIN32_FIND_DATA fd;
  1012.     HANDLE hFind;
  1013.     hFind = CacheFindFirst(NULL, &fd, &dwStatus, NULL, NULL, NULL);
  1014.     if (INVALID_HANDLE_VALUE != hFind)
  1015.     {
  1016.         CServerList BackList;
  1017.         do
  1018.         {
  1019.             // If the tray state becomes Online or NoNet, we can quit
  1020.             eSysTrayState state = (eSysTrayState)SendToSystray(PWM_QUERY_UISTATE, 0, 0);
  1021.             if (STS_ONLINE == state || STS_NONET == state)
  1022.                 break;
  1023.             // Call BackList.Exists here to avoid extra calls to
  1024.             // CSCCheckShareOnline. (Add also calls Exists)
  1025.             if ((FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwStatus) &&
  1026.                 !BackList.Exists(fd.cFileName))
  1027.             {
  1028.                 if (!CSCCheckShareOnline(fd.cFileName))
  1029.                 {
  1030.                     DWORD dwErr = GetLastError();
  1031.                     if (ERROR_ACCESS_DENIED != dwErr &&
  1032.                         ERROR_LOGON_FAILURE != dwErr)
  1033.                     {
  1034.                         // The share is not reachable
  1035.                         continue;
  1036.                     }
  1037.                     // Access denied or logon failure means the server is
  1038.                     // reachable, but we don't have valid credentials.
  1039.                 }
  1040.                 // The share is offline but available again.
  1041.                 STDBGOUT((1, TEXT("Detected server back: %s"), fd.cFileName));
  1042.                 BackList.Add(fd.cFileName);
  1043.                 // Get the \server name (minus the sharename) and
  1044.                 // tell ourselves that it's back.
  1045.                 LPCTSTR pszServer = BackList.Get(BackList.Count() - 1);
  1046.                 if (pszServer)
  1047.                 {
  1048.                     CSCUISetState(STWM_CSCNETUP, 0, (LPARAM)pszServer);
  1049.                 }
  1050.             }
  1051.         }
  1052.         while(CacheFindNext(hFind, &fd, &dwStatus, NULL, NULL, NULL));
  1053.         CSCFindClose(hFind);
  1054.     }
  1055.     DllRelease();
  1056.     FreeLibraryAndExitThread(g_hInstance, 0);
  1057.     return 0;
  1058. }
  1059. void
  1060. CStateMachine::PingServers()
  1061. {
  1062.     // Don't bother trying if there's no net.
  1063.     if (!m_bNoNet)
  1064.     {
  1065.         DWORD dwThreadID;
  1066.         // Give the thread a reference to the DLL
  1067.         HINSTANCE hInstThisDll = LoadLibrary(c_szDllName);
  1068.         DllAddRef();
  1069.         HANDLE hThread = CreateThread(NULL,
  1070.                                       0,
  1071.                                       _PingServersThread,
  1072.                                       NULL,
  1073.                                       0,
  1074.                                       &dwThreadID);
  1075.         if (hThread)
  1076.         {
  1077.             CloseHandle(hThread);
  1078.         }
  1079.         else
  1080.         {
  1081.             // CreateThread failed, cleanup
  1082.             DllRelease();
  1083.             FreeLibrary(hInstThisDll);
  1084.         }
  1085.     }
  1086. }
  1087. //
  1088. // Determine if a given share has files cached in the CSC cache.
  1089. // 
  1090. //
  1091. bool
  1092. CStateMachine::ShareHasFiles(
  1093.     LPCTSTR pszShare,
  1094.     bool *pbModified,
  1095.     bool *pbOpen
  1096.     ) const
  1097. {
  1098.     //
  1099.     // Exclude the following:
  1100.     //   1. Directories.
  1101.     //   2. Files marked as "locally deleted".
  1102.     //
  1103.     // NOTE:  The filtering done by this function must be the same as 
  1104.     //        in several other places throughout the CSCUI code.
  1105.     //        To locate these, search the source for the comment
  1106.     //        string CSCUI_ITEM_FILTER.
  1107.     //
  1108.     const DWORD fExclude = SSEF_LOCAL_DELETED | 
  1109.                            SSEF_DIRECTORY;
  1110.     //
  1111.     // Stop stats enumeration when we've found all of the following:
  1112.     //   1. At least one file.
  1113.     //   2. At least one modified file.
  1114.     //   3. At least one file with either USER access OR GUEST access.
  1115.     //
  1116.     const DWORD fUnity   = SSUF_TOTAL | 
  1117.                            SSUF_MODIFIED | 
  1118.                            SSUF_ACCUSER | 
  1119.                            SSUF_ACCGUEST | 
  1120.                            SSUF_ACCOR;
  1121.     CSCSHARESTATS ss;
  1122.     CSCGETSTATSINFO si = { fExclude, fUnity, true, false };
  1123.     _GetShareStatisticsForUser(pszShare, // Share name.
  1124.                                &si,
  1125.                                &ss);     // Destination buffer.
  1126.     if (NULL != pbModified)
  1127.     {
  1128.         *pbModified = (0 < ss.cModified);
  1129.     }
  1130.     if (NULL != pbOpen)
  1131.     {
  1132.         *pbOpen = ss.bOpenFiles;
  1133.     }
  1134.     return 0 < ss.cTotal;
  1135. }
  1136. //-----------------------------------------------------------------------------
  1137. // CSysTrayUI member functions.
  1138. //-----------------------------------------------------------------------------
  1139. //
  1140. // This is the minimum interval (in ms) allowed between state changes of
  1141. // the systray UI.  A value of 0 would result in immediate updates as 
  1142. // notifications are received from the CSC agent.  A value of 60000 would
  1143. // cause any state changes received less than 60 seconds after the previous
  1144. // state change to be queued.  60 seconds after the previous state change, 
  1145. // if a state change is queued it is applied to the systray UI.
  1146. // Something to consider is dynamically adjusting 
  1147. //
  1148. const int CSysTrayUI::s_iMinStateChangeInterval = 10000; // 10 seconds.
  1149. CSysTrayUI::CSysTrayUI(
  1150.     HWND hwndNotify
  1151.     ) : m_idFlashingTimer(0),
  1152.         m_idReminderTimer(0),
  1153.         m_idStateChangeTimer(0),
  1154.         m_iIconFlashTime(GetCaretBlinkTime()),
  1155.         m_hIconNoOverlay(s_rgIconInfo[int(STS_OFFLINE)].hIcon), // The offline icon is used
  1156.                                                                 // as the non-overlay icon for 
  1157.                                                                 // flashing.
  1158.         m_hwndNotify(hwndNotify),
  1159.         m_dwFlashingExpires(0),
  1160.         m_dwNextStateChange(0),
  1161.         m_state(STS_ONLINE),
  1162.         m_statePrev(STS_INVALID),
  1163.         m_stateQueued(STS_INVALID),
  1164.         m_bFlashOverlay(false),
  1165.         m_bActive(false)
  1166. {
  1167.     //
  1168.     // Load up the required icons.
  1169.     //
  1170.     for (int i = 0; i < ARRAYSIZE(s_rgIconInfo); i++)
  1171.     {
  1172.         IconInfo& sti = s_rgIconInfo[i];
  1173.         if (NULL == sti.hIcon && 0 != sti.idIcon)
  1174.         {
  1175.             sti.hIcon = (HICON)LoadImage(g_hInstance, 
  1176.                                          MAKEINTRESOURCE(sti.idIcon),
  1177.                                          IMAGE_ICON, 
  1178.                                          CSC_ICON_CX, 
  1179.                                          CSC_ICON_CY, 
  1180.                                          LR_LOADMAP3DCOLORS);
  1181.                         
  1182.             if (NULL == sti.hIcon)
  1183.             {
  1184.                 Trace((TEXT("CSCUI ERROR %d loading Icon ID = %d"), GetLastError(), sti.idIcon));
  1185.             }
  1186.         }
  1187.     }
  1188.     m_szServer[0] = TEXT('');
  1189.     m_szServerQueued[0] = TEXT('');
  1190.     UpdateSysTray(UF_ICON);
  1191. }
  1192. CSysTrayUI::~CSysTrayUI(
  1193.     void
  1194.     )
  1195. {
  1196.     if (0 != m_idStateChangeTimer)
  1197.         KillTimer(m_hwndNotify, m_idStateChangeTimer);
  1198. }
  1199. //
  1200. // Singleton instance access.
  1201. //
  1202. CSysTrayUI& 
  1203. CSysTrayUI::GetInstance(
  1204.     void
  1205.     )
  1206. {
  1207.     static CSysTrayUI TheUI(_FindNotificationWindow());
  1208.     return TheUI;
  1209. }
  1210. //
  1211. // Change the current state of the UI to a new state.
  1212. // Returns:
  1213. //      true    = state was changed.
  1214. //      false   = state was not changed.
  1215. //
  1216. bool
  1217. CSysTrayUI::SetState(
  1218.     eSysTrayState state,
  1219.     LPCTSTR pszServer      // Optional.  Default is NULL.
  1220.     )
  1221. {
  1222.     bool bResult = false;
  1223.     //
  1224.     // Apply a state change only if the state has actually changed.
  1225.     //
  1226.     if (state != m_state)
  1227.     {
  1228.         //
  1229.         // Apply a state change only if there's not a sync in progress.
  1230.         // If there is a sync in progress, we'll receive a CSCWM_DONESYNCING
  1231.         // message when the sync is finished which will trigger a UI update.
  1232.         //
  1233.         if (!::IsSyncInProgress())
  1234.         {
  1235.             if (0 == m_idStateChangeTimer)
  1236.             {
  1237.                 //
  1238.                 // The state change timer is not active.  That means it's OK
  1239.                 // to update the tray UI.
  1240.                 //
  1241.                 STDBGOUT((1, TEXT("Changing SysTray UI state %s -> %s"), 
  1242.                                     SysTrayStateStr(m_state),
  1243.                                     SysTrayStateStr(state)));
  1244.                 m_statePrev = m_state;
  1245.                 m_state     = state;
  1246.                 UpdateSysTray(eUpdateFlags(UF_ICON | UF_BALLOON), pszServer);
  1247.                 //
  1248.                 // Reset the state change timer so that we will not produce a
  1249.                 // visible change in the tray UI for at least another 
  1250.                 // s_iMinStateChangeInterval milliseconds.
  1251.                 // Also invalidate the queued state info so that if the update timer
  1252.                 // expires before we queue a state change, it will be a no-op.
  1253.                 //
  1254.                 STDBGOUT((2, TEXT("Setting state change timer")));
  1255.                 m_stateQueued = STS_INVALID;
  1256.                 m_idStateChangeTimer = SetTimer(m_hwndNotify,
  1257.                                                 ID_TIMER_STATECHANGE,
  1258.                                                 s_iMinStateChangeInterval,
  1259.                                                 StateChangeTimerProc);
  1260.                 bResult  = true;
  1261.             }
  1262.             else
  1263.             {
  1264.                 //
  1265.                 // The state change timer is active so we can't update the tray
  1266.                 // UI right now.  We'll queue up the state information so when the
  1267.                 // timer expires this state will be applied.  Note that the "queue"
  1268.                 // is only ONE item deep.  Each successive addition to the queue
  1269.                 // overwrites the current content.
  1270.                 //
  1271.                 STDBGOUT((2, TEXT("Queueing state change to %s."), SysTrayStateStr(state)));
  1272.                 m_stateQueued = state;
  1273.                 if (NULL != pszServer)
  1274.                 {
  1275.                     lstrcpyn(m_szServerQueued, pszServer, ARRAYSIZE(m_szServerQueued));
  1276.                 }
  1277.                 else
  1278.                 {
  1279.                     m_szServerQueued[0] = TEXT('');
  1280.                 }
  1281.             }
  1282.         }
  1283.         else
  1284.         {
  1285.             STDBGOUT((2, TEXT("Sync in progress.  SysTray state not changed.")));
  1286.         }
  1287.     }
  1288.     return bResult;
  1289. }
  1290. //
  1291. // Called each time the state change timer expires.
  1292. //
  1293. VOID CALLBACK 
  1294. CSysTrayUI::StateChangeTimerProc(
  1295.     HWND hwnd, 
  1296.     UINT uMsg, 
  1297.     UINT_PTR idEvent, 
  1298.     DWORD dwTime
  1299.     )
  1300. {
  1301.     //
  1302.     // Call a non-static function of the singleton instance so
  1303.     // we have access to private members.
  1304.     //
  1305.     CSysTrayUI::GetInstance().OnStateChangeTimerExpired();
  1306. }
  1307. void
  1308. CSysTrayUI::OnStateChangeTimerExpired(
  1309.     void
  1310.     )
  1311. {
  1312.     STDBGOUT((2, TEXT("State change timer expired. Queued state = %s"), 
  1313.              SysTrayStateStr(m_stateQueued)));
  1314.     //
  1315.     // Kill the timer and set it's ID to 0.
  1316.     // This will let SetState() know that the timer has expired and
  1317.     // it's OK to update the tray UI.
  1318.     //
  1319.     if (0 != m_idStateChangeTimer)
  1320.     {
  1321.         KillTimer(m_hwndNotify, m_idStateChangeTimer);
  1322.         m_idStateChangeTimer = 0;
  1323.     }
  1324.     if (int(m_stateQueued) != int(STS_INVALID))
  1325.     {
  1326.         //
  1327.         // Call SetState ONLY if queued info is valid; meaning
  1328.         // there was something in the queue.
  1329.         //
  1330.         SetState(m_stateQueued, m_szServerQueued);
  1331.     }
  1332. }
  1333. //
  1334. // On WM_WININICHANGED update the icon flash timer.
  1335. //
  1336. void
  1337. CSysTrayUI::OnWinIniChange(
  1338.     LPCTSTR pszSection
  1339.     )
  1340. {
  1341.     m_iIconFlashTime = GetCaretBlinkTime();
  1342.     KillIconFlashTimer();
  1343.     UpdateSysTray(UF_FLASHICON);
  1344. }
  1345. //
  1346. // Show the reminder balloon associated with the current UI state.
  1347. //
  1348. void 
  1349. CSysTrayUI::ShowReminderBalloon(
  1350.     void
  1351.     )
  1352. {
  1353.     UpdateSysTray(eUpdateFlags(UF_BALLOON | UF_REMINDER));
  1354. }
  1355.    
  1356. //
  1357. // All roads lead here.
  1358. // This function is the kitchen sink for updating the systray.
  1359. // It's kind of a long function but it centralizes all changes to 
  1360. // the systray.  It's divided into 3 basic parts:
  1361. //
  1362. //  1. Change the tray icon.           (UF_ICON)
  1363. //  2. Flash the tray icon.            (UF_FLASHICON)
  1364. //  3. Display a notification balloon. (UF_BALLOON)
  1365. //  
  1366. // Part or all of these can be performed in a single call depending
  1367. // upon the content of the uFlags argument.
  1368. //
  1369. void 
  1370. CSysTrayUI::UpdateSysTray(
  1371.     eUpdateFlags uFlags,
  1372.     LPCTSTR pszServer       // optional.  Default is NULL.
  1373.     )
  1374. {
  1375.     NOTIFYICONDATA nid = {0};
  1376.     if (!IsWindow(m_hwndNotify))
  1377.         return;
  1378.     //
  1379.     // If an icon is active, we're modifying it.
  1380.     // If none active, we're adding one.
  1381.     //        
  1382.     DWORD nimsg = NIM_MODIFY;
  1383.     nid.cbSize           = sizeof(NOTIFYICONDATA);
  1384.     nid.uID              = PWM_TRAYCALLBACK;
  1385.     nid.uFlags           = NIF_MESSAGE;
  1386.     nid.uCallbackMessage = PWM_TRAYCALLBACK;
  1387.     nid.hWnd             = m_hwndNotify;
  1388.     IconInfo& sti = s_rgIconInfo[int(m_state)];
  1389.     if (NULL != pszServer && TEXT('') != *pszServer)
  1390.     {
  1391.         //
  1392.         // Copy the name of the server to a member variable.
  1393.         // Skip passed the leading "\".
  1394.         //
  1395.         while(*pszServer && TEXT('\') == *pszServer)
  1396.             pszServer++;
  1397.         lstrcpyn(m_szServer, pszServer, ARRAYSIZE(m_szServer));
  1398.     }
  1399.     //
  1400.     // Change the icon --------------------------------------------------------
  1401.     //
  1402.     if (UF_ICON & uFlags)
  1403.     {
  1404.         nid.uFlags |= NIF_ICON;
  1405.         if (0 == sti.idIcon)
  1406.         {
  1407.             //
  1408.             // This state doesn't have an icon.  Delete from systray.
  1409.             //
  1410.             nimsg = NIM_DELETE;
  1411.         }
  1412.         else
  1413.         {
  1414.             if (!m_bActive)
  1415.                 nimsg = NIM_ADD;
  1416.             nid.hIcon = sti.hIcon;
  1417.             //
  1418.             // If applicable, always flash icon when first showing it.
  1419.             //
  1420.             uFlags = eUpdateFlags(uFlags | UF_FLASHICON);
  1421.             //
  1422.             // Set the tooltip.
  1423.             //
  1424.             nid.uFlags |= NIF_TIP;
  1425.             GetTooltipText(m_state, nid.szTip, ARRAYSIZE(nid.szTip));
  1426.         }
  1427.         m_bFlashOverlay = false;
  1428.         KillIconFlashTimer();
  1429.     }
  1430.     //
  1431.     // Flash the icon ---------------------------------------------------------
  1432.     //
  1433.     if (UF_FLASHICON & uFlags)
  1434.     {
  1435.         if (0 != sti.iFlashTimeout)
  1436.         {
  1437.             nid.uFlags |= NIF_ICON; // Flashing is actually displaying a new icon.
  1438.             //
  1439.             // This icon is a flashing icon.
  1440.             //
  1441.             if (0 == m_idFlashingTimer)
  1442.             {
  1443.                 //
  1444.                 // No timer started yet.  Start one.
  1445.                 //
  1446.                 STDBGOUT((2, TEXT("Starting icon flash timer.  Time = %d ms"), m_iIconFlashTime));
  1447.                 m_idFlashingTimer = SetTimer(m_hwndNotify, 
  1448.                                              ID_TIMER_FLASHICON, 
  1449.                                              m_iIconFlashTime,
  1450.                                              FlashTimerProc);
  1451.                 if (0 != m_idFlashingTimer)
  1452.                 {
  1453.                     //
  1454.                     // Set the tick-count when the timer expires.
  1455.                     // An expiration time of (-1) means it never expires.
  1456.                     //
  1457.                     if (ICONFLASH_FOREVER != sti.iFlashTimeout)
  1458.                         m_dwFlashingExpires = GetTickCount() + sti.iFlashTimeout;
  1459.                     else
  1460.                         m_dwFlashingExpires = ICONFLASH_FOREVER;
  1461.                 }
  1462.             }
  1463.             nid.hIcon = m_bFlashOverlay ? sti.hIcon : m_hIconNoOverlay;
  1464.             m_bFlashOverlay = !m_bFlashOverlay; // Toggle flash state.
  1465.         }
  1466.     }
  1467.     //
  1468.     // Update or hide the balloon ---------------------------------------------
  1469.     //
  1470.     if (UF_BALLOON & uFlags)
  1471.     {
  1472.         //
  1473.         // If there's no balloon text mapped to the current UI state and these
  1474.         // balloon flags, any current balloon will be destroyed.  This is because
  1475.         // the tray code destroys the current balloon before displaying the new one
  1476.         // and it doesn't display a new one if it's passed a blank string.
  1477.         //
  1478.         nid.uFlags |= NIF_INFO;
  1479.         DWORD dwBalloonFlags = (UF_REMINDER & uFlags) ? BTF_REMIND : BTF_INITIAL;
  1480.         GetBalloonInfo(m_state, 
  1481.                        dwBalloonFlags, 
  1482.                        nid.szInfoTitle,
  1483.                        ARRAYSIZE(nid.szInfoTitle),
  1484.                        nid.szInfo, 
  1485.                        ARRAYSIZE(nid.szInfo), 
  1486.                        &nid.dwInfoFlags,
  1487.                        &nid.uTimeout);
  1488.         //
  1489.         // Any time we show a balloon, we reset the reminder timer.  
  1490.         // This is so that we don't get a balloon resulting from a state change
  1491.         // immediately followed by a reminder balloon because the reminder
  1492.         // timer expired.
  1493.         //
  1494.         bool bRestartReminderTimer = (BTF_REMIND == dwBalloonFlags && TEXT('') != nid.szInfo[0]) ||
  1495.                                      StateHasBalloonText(m_state, BTF_REMIND);
  1496.         ResetReminderTimer(bRestartReminderTimer);
  1497.     }
  1498.     //
  1499.     // Notify the systray -----------------------------------------------------
  1500.     //
  1501.     if (NIM_DELETE == nimsg)
  1502.         m_bActive = false;
  1503.     if (Shell_NotifyIcon(nimsg, &nid))
  1504.     {
  1505.         if (NIM_ADD == nimsg)
  1506.             m_bActive = true;
  1507.     }
  1508. }
  1509. //
  1510. // Get the balloon text associated with a given systray UI state and with
  1511. // a given set of BTF_XXXXX (Balloon Text Flag) flags.  The information 
  1512. // is stored in the table s_rgBalloonInfo[].  The text and balloon timeout
  1513. // are returned in caller-provided buffers.
  1514. //
  1515. // The balloon text follows this format:
  1516. //
  1517. //    <Header> <Status> n
  1518. // 
  1519. //    <Body>
  1520. //
  1521. //    <Directive>
  1522. //
  1523. // An example would be:
  1524. //
  1525. //    Offline Files - Network Connection Lost
  1526. //
  1527. //    The network connection to '\worf' has been lost.
  1528. //
  1529. //    Click here to view status.
  1530. //
  1531. // state is one of the STS_XXXXX flags.
  1532. // dwTextFlags is a mask of BTF_XXXXX flag bits.
  1533. //
  1534. void
  1535. CSysTrayUI::GetBalloonInfo(
  1536.     eSysTrayState state,
  1537.     DWORD dwTextFlags,
  1538.     LPTSTR pszTextHdr,
  1539.     int cchTextHdr,
  1540.     LPTSTR pszTextBody,
  1541.     int cchTextBody,
  1542.     DWORD *pdwInfoFlags,
  1543.     UINT *puTimeout
  1544.     )
  1545. {
  1546.     *pszTextHdr  = TEXT('');
  1547.     *pszTextBody = TEXT('');
  1548.     if (SupressBalloon(m_statePrev, state))
  1549.     {
  1550.         STDBGOUT((3, TEXT("Balloon supressed")));
  1551.         return;
  1552.     }
  1553.     int i = GetBalloonInfoIndex(state, dwTextFlags);
  1554.     if (-1 != i)
  1555.     {
  1556.         BalloonInfo& bi = s_rgBalloonInfo[i];
  1557.         //
  1558.         // BUGBUG:  Review these buffer sizes.   Allow for localization!
  1559.         //
  1560.         TCHAR szHeader[80];
  1561.         TCHAR szStatus[80];
  1562.         TCHAR szDirective[80];
  1563.         TCHAR szBody[MAX_PATH];
  1564.         TCHAR szFmt[MAX_PATH];
  1565.           
  1566.         if (STS_OFFLINE == state || STS_DIRTY == state || STS_SERVERBACK == state)
  1567.         {
  1568.             //
  1569.             // State has only one server associated with it so that means we'll
  1570.             // be including it in the balloon text body.  Load the format
  1571.             // string from a text resource and embed the server name in it.
  1572.             //
  1573.             LPTSTR rgpstr[] = { m_szServer };
  1574.             LoadString(g_hInstance, bi.idBody, szFmt, ARRAYSIZE(szFmt));
  1575.             FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  1576.                           FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1577.                           szFmt,
  1578.                           0,0,
  1579.                           szBody,
  1580.                           ARRAYSIZE(szBody),
  1581.                           (va_list *)rgpstr);
  1582.         }
  1583.         else
  1584.         {
  1585.             //
  1586.             // State has multiple servers associated with it so that means
  1587.             // there's no name embedded in the body.  It's just a simple string
  1588.             // loaded from a text resource.
  1589.             //
  1590.             LoadString(g_hInstance, bi.idBody, szBody, ARRAYSIZE(szBody));
  1591.         }
  1592.         //
  1593.         // Create the header text.
  1594.         //
  1595.         LoadString(g_hInstance, IDS_BALLOONHDR_FORMAT, szFmt, ARRAYSIZE(szFmt));
  1596.         LoadString(g_hInstance, bi.idHeader, szHeader, ARRAYSIZE(szHeader));
  1597.         LoadString(g_hInstance, bi.idStatus, szStatus, ARRAYSIZE(szStatus));
  1598.         LPTSTR rgpstrHdr[] = { szHeader,
  1599.                                szStatus };
  1600.         FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  1601.                       FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1602.                       szFmt,
  1603.                       0,0,
  1604.                       pszTextHdr,
  1605.                       cchTextHdr,
  1606.                       (va_list *)rgpstrHdr);
  1607.         //
  1608.         // Create the body text.
  1609.         //
  1610.         LoadString(g_hInstance, IDS_BALLOONBODY_FORMAT, szFmt, ARRAYSIZE(szFmt));
  1611.         LoadString(g_hInstance, bi.idDirective, szDirective, ARRAYSIZE(szDirective));
  1612.         LPTSTR rgpstrBody[] = { szBody,
  1613.                                 szDirective };
  1614.         FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  1615.                       FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1616.                       szFmt,
  1617.                       0,0,
  1618.                       pszTextBody,
  1619.                       cchTextBody,
  1620.                       (va_list *)rgpstrBody);
  1621.         if (NULL != pdwInfoFlags)
  1622.         {
  1623.             *pdwInfoFlags = bi.dwInfoFlags;
  1624.         }
  1625.         if (NULL != puTimeout)
  1626.         {
  1627.             CConfig& config = CConfig::GetSingleton();
  1628.             //
  1629.             // Balloon timeout is stored in the registry.
  1630.             //
  1631.             UINT uTimeout = (BTF_INITIAL & dwTextFlags) ? config.InitialBalloonTimeoutSeconds() :
  1632.                                                           config.ReminderBalloonTimeoutSeconds();
  1633.             *puTimeout = uTimeout * 1000;
  1634.         }
  1635.     }
  1636. }
  1637. //
  1638. // Find the index in s_rgBalloonInfo[] for a given state
  1639. // and BTF_XXXXXX flag.
  1640. // Returns -1 if no match in array.
  1641. //
  1642. int
  1643. CSysTrayUI::GetBalloonInfoIndex(
  1644.     eSysTrayState state,
  1645.     DWORD dwTextFlags
  1646.     )
  1647. {
  1648.     //
  1649.     // Scan the balloon info table until we find a record for the 
  1650.     // specified systray UI state and BTF flags.
  1651.     //
  1652.     for (int i = 0; i < ARRAYSIZE(s_rgBalloonInfo); i++)
  1653.     {
  1654.         BalloonInfo& bi = s_rgBalloonInfo[i];
  1655.         if (bi.state == state && 
  1656.             bi.dwTextFlags == dwTextFlags &&
  1657.             0 != bi.idHeader &&
  1658.             0 != bi.idStatus &&
  1659.             0 != bi.idBody &&
  1660.             0 != bi.idDirective)
  1661.         {
  1662.             return i;
  1663.         }
  1664.     }
  1665.     return -1;
  1666. }
  1667.     
  1668. //
  1669. // Determine if a balloon should not be displayed for a particular
  1670. // UI state transition.
  1671. //
  1672. bool
  1673. CSysTrayUI::SupressBalloon(
  1674.     eSysTrayState statePrev,
  1675.     eSysTrayState state
  1676.     )
  1677. {
  1678.     for (int i = 0; i < ARRAYSIZE(s_rgBalloonSupression); i++)
  1679.     {
  1680.         if (statePrev == s_rgBalloonSupression[i].stateFrom &&
  1681.             state     == s_rgBalloonSupression[i].stateTo)
  1682.         {
  1683.             return true;
  1684.         }
  1685.     }
  1686.     return false;
  1687. }
  1688. //
  1689. // Do we have balloon text for a given state and balloon style?
  1690. // state is one of the STS_XXXXX flags.
  1691. // dwTextFlags is a mask of BTF_XXXXX flag bits.
  1692. //
  1693. bool 
  1694. CSysTrayUI::StateHasBalloonText(
  1695.     eSysTrayState state,
  1696.     DWORD dwTextFlags
  1697.     )
  1698. {
  1699.     return (-1 != GetBalloonInfoIndex(state, dwTextFlags));
  1700. }
  1701. LPTSTR 
  1702. CSysTrayUI::GetTooltipText(
  1703.     eSysTrayState state,
  1704.     LPTSTR pszText,
  1705.     int cchText
  1706.     )
  1707. {
  1708.     *pszText = TEXT('');
  1709.     //
  1710.     // Scan the tooltip info table until we find a record for the 
  1711.     // specified systray UI state.
  1712.     //
  1713.     for (int i = 0; i < ARRAYSIZE(s_rgTooltipInfo); i++)
  1714.     {
  1715.         TooltipInfo& tti = s_rgTooltipInfo[i];
  1716.         if (tti.state == state && 0 != tti.idTooltip)
  1717.         {
  1718.             TCHAR szTemp[MAX_PATH];
  1719.             int cchHeader = LoadString(g_hInstance, IDS_TT_HEADER, szTemp, ARRAYSIZE(szTemp));
  1720.             if (STS_OFFLINE == state || STS_DIRTY == state || STS_SERVERBACK == state)
  1721.             {
  1722.                 //
  1723.                 // State has only one server associated with it so that means we'll
  1724.                 // be including it in the tooltip text.  Embed the server name in it.
  1725.                 //
  1726.                 TCHAR szFmt[160];
  1727.                 LPTSTR rgpstr[] = { m_szServer };
  1728.                 LoadString(g_hInstance, tti.idTooltip, szFmt, ARRAYSIZE(szFmt));
  1729.                 FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  1730.                               FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1731.                               szFmt,
  1732.                               0,0,
  1733.                               szTemp + cchHeader,
  1734.                               ARRAYSIZE(szTemp) - cchHeader,
  1735.                               (va_list *)rgpstr);
  1736.             }
  1737.             else
  1738.             {
  1739.                 //
  1740.                 // State has multiple servers associated with it so that means
  1741.                 // there's no name embedded in the tooltip.  It's just a simple string
  1742.                 // loaded from a text resource.
  1743.                 //
  1744.                 LoadString(g_hInstance, 
  1745.                            tti.idTooltip, 
  1746.                            szTemp + cchHeader, 
  1747.                            ARRAYSIZE(szTemp) - cchHeader);
  1748.             }
  1749.             lstrcpyn(pszText, szTemp, cchText);
  1750.         }
  1751.     }
  1752.     return pszText;
  1753. }
  1754. //
  1755. // Stop the flashing icon by killing the timer.
  1756. //
  1757. void 
  1758. CSysTrayUI::KillIconFlashTimer(
  1759.     void
  1760.     )
  1761. {
  1762.     //
  1763.     // Force a final update so we're displaying the proper icon then
  1764.     // kill the timer.
  1765.     //
  1766.     if (0 != m_idFlashingTimer)
  1767.     {
  1768.         KillTimer(m_hwndNotify, m_idFlashingTimer);
  1769.         m_idFlashingTimer = 0;
  1770.     }
  1771. }
  1772. //
  1773. // Called by the OS each time the icon flash timer period expires.
  1774. // I use this rather than handling a WM_TIMER message so that
  1775. // timer processing is contained within the CSysTrayUI class.
  1776. //
  1777. VOID CALLBACK 
  1778. CSysTrayUI::FlashTimerProc(
  1779.     HWND hwnd,
  1780.     UINT uMsg, 
  1781.     UINT_PTR idEvent, 
  1782.     DWORD dwTime
  1783.     )
  1784. {
  1785.     CSysTrayUI::GetInstance().HandleFlashTimer();
  1786. }
  1787. void
  1788. CSysTrayUI::HandleFlashTimer(
  1789.     void
  1790.     )
  1791. {
  1792.     if (IconFlashedLongEnough())
  1793.     {
  1794.         //
  1795.         // Kill the icon flashing timer and the icon will stop flashing.
  1796.         // This doesn't actually kill the timer yet.
  1797.         //
  1798.         STDBGOUT((2, TEXT("Killing icon flash timer")));
  1799.         m_bFlashOverlay = true;
  1800.         UpdateSysTray(UF_FLASHICON);
  1801.         KillIconFlashTimer();
  1802.     }