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

Windows Kernel

Development Platform:

Visual C++

  1. //+-------------------------------------------------------------------------
  2. //
  3. //  Microsoft Windows
  4. //
  5. //  Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. //  File:       purge.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include <cscuiext.h>   // CSCUIRemoveFolderFromCache
  13. #include "purge.h"
  14. #include "pathstr.h"
  15. #include "cscutils.h"
  16. #include "msgbox.h"
  17. #include "resource.h"
  18. #include "security.h"
  19. #include "util.h"     // Utils from "dll" directory.
  20. //
  21. // This is also defined in cscuidllpch.h
  22. // If you change it there you must change it here and vice versa.
  23. //
  24. #define FLAG_CSC_HINT_PIN_ADMIN  FLAG_CSC_HINT_PIN_SYSTEM
  25. //
  26. // Purge confirmation dialog.
  27. // The user can set which files are purged from the cache.
  28. //
  29. class CConfirmPurgeDialog
  30. {
  31.     public:
  32.         CConfirmPurgeDialog(void) throw()
  33.             : m_hInstance(NULL),
  34.               m_hwnd(NULL),
  35.               m_hwndLV(NULL),
  36.               m_pSel(NULL)
  37.               { }
  38.         ~CConfirmPurgeDialog(void) throw()
  39.             { if (NULL != m_hwnd) DestroyWindow(m_hwnd); }
  40.         int Run(HINSTANCE hInstance, HWND hwndParent, CCachePurgerSel *pSel);
  41.     private:
  42.         HINSTANCE        m_hInstance;
  43.         HWND             m_hwnd;
  44.         HWND             m_hwndLV;
  45.         CCachePurgerSel *m_pSel;                // Ptr to destination for selection info.
  46.         static INT_PTR CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  47.         void OnInitDialog(HWND hwnd);
  48.         void OnDestroy(void);
  49.         void OnOk(void);
  50.         void OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam);
  51.         LPTSTR MyStrDup(LPCTSTR psz);
  52.         BOOL LVSetItemData(HWND hwndLV, int i, LPARAM lParam);
  53.         LPARAM LVGetItemData(HWND hwndLV, int i);
  54.         int LVAddItem(HWND hwndLV, LPCTSTR pszItem);
  55. };
  56. inline ULONGLONG
  57. MakeULongLong(DWORD dwLow, DWORD dwHigh)
  58. {
  59.     return ((ULONGLONG)(((DWORD)(dwLow)) | ((LONGLONG)((DWORD)(dwHigh))) << 32));
  60. }
  61. inline bool
  62. IsDirty(const CscFindData& cfd)
  63. {
  64.     return 0 != (FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY & cfd.dwStatus);
  65. }
  66. inline bool
  67. IsSuperHidden(const CscFindData& cfd)
  68. {
  69.     const DWORD dwSuperHidden = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
  70.     return (cfd.fd.dwFileAttributes & dwSuperHidden) == dwSuperHidden;
  71. }
  72. inline bool
  73. OthersHaveAccess(const CscFindData& cfd)
  74. {
  75.     return CscAccessOther(cfd.dwStatus);
  76. }
  77. CCachePurger::CCachePurger(
  78.     const CCachePurgerSel& sel,
  79.     LPFNPURGECALLBACK pfnCbk, 
  80.     LPVOID pvCbkData
  81.     ) : m_ullBytesToScan(0),
  82.         m_ullBytesScanned(0),
  83.         m_ullBytesToDelete(0),
  84.         m_ullBytesDeleted(0),
  85.         m_ullFileBytes(0),
  86.         m_dwPhase(0),
  87.         m_cFilesToScan(0),
  88.         m_cFilesScanned(0),
  89.         m_cFilesToDelete(0),
  90.         m_cFilesDeleted(0),
  91.         m_iFile(0),
  92.         m_dwFileAttributes(0),
  93.         m_dwResult(0),
  94.         m_hmutexPurgeInProg(NULL),
  95.         m_pszFile(NULL),
  96.         m_pvCbkData(pvCbkData),
  97.         m_bWillDelete(FALSE),
  98.         m_pfnCbk(pfnCbk),
  99.         m_bIsValid(true),
  100.         m_sel(sel),
  101.         m_bDelPinned(0 != (PURGE_FLAG_PINNED & sel.Flags())),
  102.         m_bDelUnpinned(0 != (PURGE_FLAG_UNPINNED & sel.Flags())),
  103.         m_bIgnoreAccess(0 != (PURGE_IGNORE_ACCESS & sel.Flags())),
  104.         m_bUserIsAnAdmin(boolify(IsCurrentUserAnAdminMember()))
  105. {
  106.     //
  107.     // Let the world know we're purging files from the cache.
  108.     // In particular, our overlay handler in cscuidllshellex.cpp needs
  109.     // to disable it's auto-pin code whenever we're purging.  If we didn't
  110.     // do this AND the source folder is open during the purge, we get a 
  111.     // nasty race condition between our shell notifications for purging
  112.     // and the overlay handler's auto-pin code.  We'll delete a file
  113.     // and send a notification.  The shell updates the icon overlay.
  114.     // Our handler sees that the parent folder is pinned so it re-pins
  115.     // the purged file which restores the file in the folder. This ends
  116.     // up resulting in a very nasty infinite loop. Those interested
  117.     // should call IsPurgeInProgress() to determine if the purge is
  118.     // in progress (see cscuidllutil.cpp).  [brianau - 11/01/99]
  119.     //
  120.     m_hmutexPurgeInProg = CreateMutex(NULL, TRUE, c_szPurgeInProgMutex);
  121. }
  122. CCachePurger::~CCachePurger(
  123.     void
  124.     )
  125. {
  126.     if (NULL != m_hmutexPurgeInProg)
  127.     {
  128.         ReleaseMutex(m_hmutexPurgeInProg);
  129.         CloseHandle(m_hmutexPurgeInProg);
  130.     }
  131. }
  132.    
  133. //
  134. // Deletes a directory and all it's contents according to the PURGE_FLAG_XXXXX flag 
  135. // bits set by the caller of PurgeCache().
  136. // This function recursively traverses the cache file hierarchy in a post-order fashion.
  137. // Directory nodes are deleted after all children have been deleted.
  138. // If the caller of PurgeCache provided a callback function, it is called
  139. // after each deletion.   If the callback function returns FALSE, the traversal
  140. // operation is terminated.
  141. //
  142. // pstrPath - Address of CPath object containing the path of the directory to be
  143. //            deleted.  This object is used to contain the working path throughout
  144. //            the tree traversal.
  145. //
  146. // dwPhase -  PURGE_PHASE_SCAN    - Scanning for file totals.
  147. //            PURGE_PHASE_DELETE  - Deleting files.
  148. //
  149. // bShareIsOpen - There's an open connection to the share.  It's OK
  150. //                to issue a shell change notify.
  151. //
  152. // Returns:   true  = Continue traversal.
  153. //            false = User cancelled via callback.  Terminate traversal.
  154. //            
  155. //
  156. bool
  157. CCachePurger::ProcessDirectory(
  158.     CPath *pstrPath,
  159.     DWORD dwPhase,
  160.     bool bShareIsOpen
  161.     )
  162. {
  163.     bool bContinue = true;
  164.     bool bShellNotified = false;
  165.     CscFindData cfd;
  166.     CscFindHandle hFind(CacheFindFirst(*pstrPath, m_sel.UserSid(), &cfd));
  167.     if (hFind.IsValidHandle())
  168.     {
  169.         do
  170.         {
  171.             bool bIsDirectory = (0 != (FILE_ATTRIBUTE_DIRECTORY & cfd.fd.dwFileAttributes));
  172.             //
  173.             // Create full path to this file/folder.
  174.             //
  175.             pstrPath->Append(cfd.fd.cFileName);
  176.             if (bIsDirectory)
  177.             {
  178.                 //
  179.                 // It's a directory.  Recursively delete it's contents.
  180.                 //
  181.                 bContinue = ProcessDirectory(pstrPath, dwPhase, bShareIsOpen);
  182.             }
  183.             if (bContinue)
  184.             {
  185.                 bool bPinned = (0 != ((FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN) & cfd.dwHintFlags));
  186.                 //
  187.                 // The decision to delete a file has several criteria.  I've tried to break this
  188.                 // up using inlines and member variables to make it more understandable and minimize
  189.                 // maintenance bugs.
  190.                 // The logic for deletion is this:
  191.                 //
  192.                 //  bDelete = false;
  193.                 //  If (pinned AND deleting pinned) OR (not pinned and deleting unpinned) then
  194.                 //      If super_hidden then
  195.                 //          bDelete = true;
  196.                 //      else
  197.                 //          If (not locally dirty) then
  198.                 //              If (ignore access) then
  199.                 //                  bDelete = true;
  200.                 //              else
  201.                 //                  If (user is an admin) then
  202.                 //                      bDelete = true;
  203.                 //                  else
  204.                 //                      if (others have NO access) then
  205.                 //                          bDelete = true;
  206.                 //                      endif
  207.                 //                  endif
  208.                 //              endif
  209.                 //          endif
  210.                 //      endif
  211.                 //  endif
  212.                 bool bDelete = ((bPinned && m_bDelPinned) || (!bPinned && m_bDelUnpinned)) &&
  213.                                (IsSuperHidden(cfd) || 
  214.                                     (!IsDirty(cfd) && 
  215.                                         (m_bIgnoreAccess ||
  216.                                             (m_bUserIsAnAdmin || !OthersHaveAccess(cfd)))));
  217.                 m_pszFile          = pstrPath->Cstr();
  218.                 m_dwFileAttributes = cfd.fd.dwFileAttributes;
  219.                 m_ullFileBytes     = MakeULongLong(cfd.fd.nFileSizeLow, cfd.fd.nFileSizeHigh);
  220.                 m_bWillDelete      = bDelete;
  221.                 if (PURGE_PHASE_SCAN == dwPhase)
  222.                 {
  223.                     if (!bIsDirectory && m_pfnCbk)
  224.                     {
  225.                         //
  226.                         // Exclude directories from the file and byte counts.
  227.                         // 
  228.                         if (bDelete)
  229.                         {
  230.                             m_cFilesToDelete++;
  231.                             m_ullBytesToDelete += m_ullFileBytes;
  232.                         }
  233.                         m_cFilesScanned++;
  234.                         m_ullBytesScanned += m_ullFileBytes;
  235.                         m_dwResult = ERROR_SUCCESS;
  236.                         bContinue = boolify((*m_pfnCbk)(this));
  237.                         m_iFile++;
  238.                     }
  239.                 }
  240.                 else if (PURGE_PHASE_DELETE == dwPhase && bDelete)
  241.                 {
  242.                     m_dwResult = CscDelete(*pstrPath);
  243.                     if (ERROR_SUCCESS == m_dwResult)
  244.                     {
  245.                         if (!bIsDirectory)
  246.                         {
  247.                             m_cFilesDeleted++;
  248.                             m_ullBytesDeleted += m_ullFileBytes;
  249.                         }                            
  250.                         if (bShareIsOpen)
  251.                         {
  252.                             //
  253.                             // Only notify the shell if there's an open 
  254.                             // connection to the share.  If there's not an 
  255.                             // open connection and the share is not available 
  256.                             // via the net, this change notify will generate a 
  257.                             // "bad net name" error in the redir and cause the 
  258.                             // CSC agent to transition the share to offline mode.  
  259.                             // This results in a possible state change to the UI 
  260.                             // which (when deleting files from the cache) is a 
  261.                             // weird experience for the user.
  262.                             //
  263.                             ShellChangeNotify(*pstrPath);
  264.                             bShellNotified = true;
  265.                         }
  266.                     }
  267.                     else if (ERROR_ACCESS_DENIED == m_dwResult)
  268.                     {
  269.                         //
  270.                         // This is a little weird.  CscDelete
  271.                         // returns ERROR_ACCESS_DENIED if there's
  272.                         // a handle open on the file. Set the
  273.                         // code to ERROR_BUSY so we know to handle 
  274.                         // this as a special case.
  275.                         //
  276.                         m_dwResult = ERROR_BUSY;
  277.                     }
  278.                     if (!bIsDirectory && m_pfnCbk)
  279.                     {
  280.                         bContinue = boolify((*m_pfnCbk)(this));
  281.                         m_iFile++;
  282.                     }
  283.                 }
  284.             }
  285.             pstrPath->RemoveFileSpec();
  286.         }
  287.         while(bContinue && CacheFindNext(hFind, &cfd));
  288.     }
  289.     pstrPath->RemoveBackslash();
  290.     if (bShellNotified)
  291.     {
  292.         //
  293.         // Flush notifications to ensure all updates for this 
  294.         // directory are reflected in the shell.
  295.         //
  296.         ShellChangeNotify(*pstrPath, true, SHCNE_UPDATEDIR);
  297.     }
  298.     return bContinue;
  299. }
  300. //
  301. // Public function for purging cache contents.
  302. //
  303. HRESULT
  304. CCachePurger::Process(
  305.     DWORD dwPhase
  306.     )
  307. {
  308.     HRESULT hr = NOERROR;
  309.     if (!m_bIsValid)
  310.         return E_OUTOFMEMORY;  // Failed ctor.
  311.     m_dwPhase = dwPhase;
  312.     if (PURGE_PHASE_SCAN == dwPhase)
  313.     {
  314.         //
  315.         // At start of scanning phase, get the max bytes and file count 
  316.         // from the CSC database.  This will let us provide meaningful 
  317.         // progress data during the scanning phase.
  318.         //
  319.         ULARGE_INTEGER ulTotalBytes = {0, 0};
  320.         ULARGE_INTEGER ulUsedBytes  = {0, 0};
  321.         DWORD dwTotalFiles          = 0;
  322.         DWORD dwTotalDirs           = 0;
  323.         TCHAR szVolume[MAX_PATH];
  324.         CSCGetSpaceUsage(szVolume,
  325.                          ARRAYSIZE(szVolume),
  326.                          &ulTotalBytes.HighPart,
  327.                          &ulTotalBytes.LowPart,
  328.                          &ulUsedBytes.HighPart,
  329.                          &ulUsedBytes.LowPart,
  330.                          &dwTotalFiles,
  331.                          &dwTotalDirs);
  332.         m_cFilesToScan     = dwTotalFiles + dwTotalDirs;
  333.         m_ullBytesToScan   = ulTotalBytes.QuadPart;
  334.         m_ullBytesToDelete = 0;
  335.         m_ullBytesDeleted  = 0;
  336.         m_ullBytesScanned  = 0;
  337.         m_ullFileBytes     = 0;
  338.         m_cFilesToDelete   = 0;
  339.         m_cFilesDeleted    = 0;
  340.         m_cFilesScanned    = 0;
  341.         m_dwFileAttributes = 0;
  342.         m_dwResult         = 0;
  343.         m_pszFile          = NULL;
  344.         m_bWillDelete      = false;
  345.     }
  346.     m_iFile = 0; // Reset this for each phase.
  347.     bool bContinue = true;
  348.     try
  349.     {
  350.         CscFindData cfd;
  351.         CPath strPath;
  352.         if (0 < m_sel.ShareCount())
  353.         {
  354.             //
  355.             // Delete 1+ (but not all) shares.
  356.             //
  357.             for (int i = 0; i < m_sel.ShareCount(); i++)
  358.             {
  359.                 strPath = m_sel.ShareName(i);
  360.                 bContinue = ProcessDirectory(&strPath, 
  361.                                              dwPhase, 
  362.                                              S_OK == ::IsOpenConnectionPathUNC(m_sel.ShareName(i)));
  363.                                              
  364.                 if (PURGE_PHASE_DELETE == dwPhase)
  365.                 {
  366.                     CscDelete(strPath);
  367.                 }
  368.             }
  369.         }
  370.         else
  371.         {
  372.             //
  373.             // Delete all shares.
  374.             //
  375.             CscFindHandle hFind(CacheFindFirst(NULL, m_sel.UserSid(), &cfd));
  376.             if (hFind.IsValidHandle())
  377.             {
  378.                 do
  379.                 {
  380.                     strPath = cfd.fd.cFileName;
  381.                     bContinue = ProcessDirectory(&strPath, 
  382.                                                  dwPhase,
  383.                                                  S_OK == ::IsOpenConnectionShare(cfd.fd.cFileName));
  384.                 }
  385.                 while(bContinue && CacheFindNext(hFind, &cfd));
  386.             }
  387.         }
  388.         if (PURGE_PHASE_DELETE == dwPhase)
  389.         {
  390.             //
  391.             // On the DELETE phase always try to remove any empty
  392.             // share entries from the database.  CSCDelete will
  393.             // harmlessly fail if the share entry cannot be deleted.
  394.             //
  395.             CscFindHandle hFind(CacheFindFirst(NULL, m_sel.UserSid(), &cfd));
  396.             if (hFind.IsValidHandle())
  397.             {
  398.                 do
  399.                 {
  400.                     CscDelete(cfd.fd.cFileName);
  401.                 }
  402.                 while(CacheFindNext(hFind, &cfd));
  403.             }
  404.         }
  405.     }
  406.     catch(CException &e)
  407.     {
  408.         hr = e.dwError;
  409.     }
  410.    
  411.     return hr;
  412. }
  413. //
  414. // Displays a modal dialog to get cache purging confirmation from the 
  415. // user.  Let's user indicate if they want to purge only temp files
  416. // from the cache or both temp and pinned.
  417. //
  418. // Returns PURGE_FLAG_XXXX flags and a list of share names
  419. // in the CCachePurgerSel object.
  420. //
  421. void
  422. CCachePurger::AskUserWhatToPurge(
  423.     HWND hwndParent,
  424.     CCachePurgerSel *pSel
  425.     )
  426. {
  427.     CConfirmPurgeDialog dlg;
  428.     dlg.Run(GetModuleHandle(TEXT("cscui.dll")), hwndParent, pSel);
  429. }
  430. //
  431. // Returns:
  432. //      0 = User cancelled.
  433. //      1 = User pressed OK.
  434. //
  435. // Returns PURGE_FLAG_XXXX flags and a list of share names
  436. // in the CCachePurgerSel object.
  437. //
  438. int
  439. CConfirmPurgeDialog::Run(
  440.     HINSTANCE hInstance,
  441.     HWND hwndParent,
  442.     CCachePurgerSel *pSel        // We don't "own" this.  Merely a WRITE reference.
  443.     )
  444. {
  445.     DBGASSERT((NULL != hInstance));
  446.     DBGASSERT((NULL != hwndParent));
  447.     DBGASSERT((NULL != pSel));
  448.    
  449.     m_hInstance = hInstance;
  450.     m_pSel = pSel;
  451.     int iResult = (int)DialogBoxParam(hInstance, 
  452.                                       MAKEINTRESOURCE(IDD_CONFIRM_PURGE),
  453.                                       hwndParent,
  454.                                       DlgProc,
  455.                                       (LPARAM)this);
  456.     if (-1 == iResult)
  457.     {
  458.         DBGERROR((TEXT("Error %d creating delete confirmation dialog"), GetLastError()));
  459.     }
  460.     return iResult;
  461. }
  462. INT_PTR CALLBACK
  463. CConfirmPurgeDialog::DlgProc(
  464.     HWND hwnd,
  465.     UINT message,
  466.     WPARAM wParam,
  467.     LPARAM lParam
  468.     )
  469. {
  470.     CConfirmPurgeDialog *pThis = reinterpret_cast<CConfirmPurgeDialog *>(GetWindowLongPtr(hwnd, DWLP_USER));
  471.     try
  472.     {
  473.         switch(message)
  474.         {
  475.             case WM_INITDIALOG:
  476.                 SetWindowLongPtr(hwnd, DWLP_USER, (INT_PTR)lParam);
  477.                 pThis = reinterpret_cast<CConfirmPurgeDialog *>(lParam);
  478.                 DBGASSERT((NULL != pThis));
  479.                 pThis->OnInitDialog(hwnd);
  480.                 return TRUE;
  481.             case WM_ENDSESSION:
  482.                 EndDialog(hwnd, IDNO);
  483.                 return FALSE;
  484.             case WM_COMMAND:
  485.             {
  486.                 int iResult = 0; // Assume [Cancel]
  487.                 switch(LOWORD(wParam))
  488.                 {
  489.                     case IDOK:
  490.                         iResult = 1;
  491.                         pThis->OnOk();
  492.                         //
  493.                         // Fall through...
  494.                         //
  495.                     case IDCANCEL:
  496.                         EndDialog(hwnd, iResult);
  497.                         return FALSE;
  498.                 }
  499.             }
  500.             break;
  501.             case WM_SETTINGCHANGE:
  502.             case WM_SYSCOLORCHANGE:
  503.                 pThis->OnSettingChange(message, wParam, lParam);
  504.                 break;
  505.                 
  506.             case WM_DESTROY:
  507.                 pThis->OnDestroy();
  508.                 pThis->m_hwnd = NULL;
  509.                 return FALSE;
  510.             default:
  511.                 break;
  512.         }
  513.     }
  514.     catch(CException& e)
  515.     {
  516.         DBGERROR((TEXT("C++ exception %d caught in CConfirmPurgeDialog::DlgProc"), e.dwError));
  517.         CscWin32Message(NULL, e.dwError, CSCUI::SEV_ERROR);
  518.     }
  519.     return FALSE;
  520. }
  521. void
  522. CConfirmPurgeDialog::OnInitDialog(
  523.     HWND hwnd
  524.     )
  525. {
  526.     DBGTRACE((DM_VIEW, DL_LOW, TEXT("CConfirmPurgeDialog::OnInitDialog")));
  527.     DBGASSERT((NULL != hwnd));
  528.     RECT rc;
  529.     m_hwnd   = hwnd;
  530.     m_hwndLV = GetDlgItem(hwnd, IDC_LIST_PURGE);
  531.     CheckDlgButton(hwnd, IDC_RBN_CONFIRMPURGE_UNPINNED, BST_CHECKED);
  532.     CheckDlgButton(hwnd, IDC_RBN_CONFIRMPURGE_ALL,      BST_UNCHECKED);
  533.     //
  534.     // Turn on checkboxes in the listview.
  535.     //
  536.     ListView_SetExtendedListViewStyleEx(m_hwndLV, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
  537.     //
  538.     // Add the single column to the listview.
  539.     //
  540.     GetClientRect(m_hwndLV, &rc);
  541.     LV_COLUMN col = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM, 
  542.                       LVCFMT_LEFT, 
  543.                       rc.right - rc.left, 
  544.                       TEXT(""), 
  545.                       0, 
  546.                       0 };
  547.     ListView_InsertColumn(m_hwndLV, 0, &col);
  548.     //
  549.     // Create the image list for the listview.
  550.     //
  551.     HIMAGELIST hSmallImages = ImageList_Create(16, 16, ILC_MASK, 1, 0);
  552.     if (NULL != hSmallImages)
  553.     {
  554.         HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SHARE));
  555.         if (NULL != hIcon)
  556.         {
  557.             ImageList_AddIcon(hSmallImages, hIcon);
  558.         }
  559.         ListView_SetImageList(m_hwndLV, hSmallImages, LVSIL_SMALL);        
  560.     }
  561.     //
  562.     // Fill "shares" list with share names.
  563.     //
  564.     CscFindData cfd;
  565.     CscFindHandle hFind(CacheFindFirst(NULL, &cfd));
  566.     if (hFind.IsValidHandle())
  567.     {
  568.         LPITEMIDLIST pidl = NULL;
  569.         SHFILEINFO sfi;
  570.         CSCSHARESTATS ss;
  571.         CSCGETSTATSINFO si = { SSEF_NONE, SSUF_TOTAL, true, false };
  572.          
  573.         do
  574.         {
  575.             _GetShareStatisticsForUser(cfd.fd.cFileName, &si, &ss);
  576.             if (0 < ss.cTotal)
  577.             {
  578.                 ZeroMemory(&sfi, sizeof(sfi));
  579.                 if (SUCCEEDED(SHSimpleIDListFromFindData(cfd.fd.cFileName, &cfd.fd, &pidl)))
  580.                 {
  581.                     SHGetFileInfo((LPCTSTR)pidl,
  582.                                   0,
  583.                                   &sfi,
  584.                                   sizeof(sfi),
  585.                                   SHGFI_PIDL | SHGFI_DISPLAYNAME);
  586.                     SHFree(pidl);
  587.                 }
  588.                 int iItem = LVAddItem(m_hwndLV, sfi.szDisplayName);
  589.                 if (0 <= iItem)
  590.                 {
  591.                     //
  592.                     // All items are initially checked.
  593.                     //
  594.                     ListView_SetCheckState(m_hwndLV, iItem, TRUE);
  595.                     //
  596.                     // Each item's lParam contains a pointer to the
  597.                     // UNC path allocated on the heap.  Must be deleted
  598.                     // in OnDestroy().
  599.                     //
  600.                     LVSetItemData(m_hwndLV, iItem, (LPARAM)MyStrDup(cfd.fd.cFileName));
  601.                 }
  602.             }
  603.         }
  604.         while(CacheFindNext(hFind, &cfd));
  605.     }
  606.     if (0 == ListView_GetItemCount(m_hwndLV))
  607.     {
  608.         //
  609.         // No items are in the listview.
  610.         // Disable all of the controls, hide the "OK" button and 
  611.         // change the "Cancel" button to "Close".
  612.         //
  613.         const UINT rgidCtls[] = { IDC_TXT_CONFIRMPURGE3,
  614.                                   IDC_RBN_CONFIRMPURGE_UNPINNED,
  615.                                   IDC_RBN_CONFIRMPURGE_ALL,
  616.                                   IDC_LIST_PURGE,
  617.                                   IDOK};
  618.                                   
  619.         ShowWindow(GetDlgItem(m_hwnd, IDOK), SW_HIDE);
  620.         for (int i = 0; i < ARRAYSIZE(rgidCtls); i++)                                  
  621.         {
  622.             EnableWindow(GetDlgItem(m_hwnd, rgidCtls[i]), FALSE);
  623.         }
  624.         TCHAR szText[MAX_PATH];
  625.         LoadString(m_hInstance, IDS_BTN_TITLE_CLOSE, szText, ARRAYSIZE(szText));
  626.         SetWindowText(GetDlgItem(m_hwnd, IDCANCEL), szText);
  627.         //
  628.         // Replace the listview's caption with something like "There are
  629.         // no offline files to delete".
  630.         //
  631.         LoadString(m_hInstance, IDS_TXT_NO_FILES_TO_DELETE, szText, ARRAYSIZE(szText));
  632.         SetWindowText(GetDlgItem(m_hwnd, IDC_TXT_CONFIRMPURGE2), szText);
  633.         //
  634.         // Uncheck both radio buttons.
  635.         //
  636.         CheckDlgButton(m_hwnd, IDC_RBN_CONFIRMPURGE_UNPINNED, BST_UNCHECKED);
  637.         CheckDlgButton(m_hwnd, IDC_RBN_CONFIRMPURGE_ALL, BST_UNCHECKED);
  638.     }
  639. }
  640. LPARAM
  641. CConfirmPurgeDialog::LVGetItemData(
  642.     HWND hwndLV,
  643.     int i
  644.     )
  645. {
  646.     LVITEM item;
  647.     item.mask     = LVIF_PARAM;
  648.     item.iItem    = i;
  649.     item.iSubItem = 0;
  650.     if (ListView_GetItem(hwndLV, &item))
  651.     {
  652.         return item.lParam;
  653.     }
  654.     return 0;
  655. }
  656. BOOL
  657. CConfirmPurgeDialog::LVSetItemData(
  658.     HWND hwndLV,
  659.     int i,
  660.     LPARAM lParam
  661.     )
  662. {
  663.     LVITEM item;
  664.     item.mask     = LVIF_PARAM;
  665.     item.iItem    = i;
  666.     item.iSubItem = 0;
  667.     item.lParam   = lParam;
  668.     return ListView_SetItem(hwndLV, &item);
  669. }
  670. int
  671. CConfirmPurgeDialog::LVAddItem(
  672.     HWND hwndLV,
  673.     LPCTSTR pszItem
  674.     )
  675. {
  676.     LVITEM item;
  677.     item.mask     = LVIF_TEXT;
  678.     item.pszText  = (LPTSTR)pszItem;
  679.     item.iSubItem = 0;
  680.     item.iItem    = ListView_GetItemCount(hwndLV);
  681.     return ListView_InsertItem(hwndLV, &item);
  682. }
  683. void 
  684. CConfirmPurgeDialog::OnOk(
  685.     void
  686.     )
  687. {
  688.     const int cShares = ListView_GetItemCount(m_hwndLV);
  689.     for (int i = 0; i < cShares; i++)
  690.     {
  691.         if (0 != ListView_GetCheckState(m_hwndLV, i))
  692.         {
  693.             m_pSel->AddShareName((LPCTSTR)LVGetItemData(m_hwndLV, i));
  694.         }
  695.     }
  696.     
  697.     if (0 < m_pSel->ShareCount())
  698.     {
  699.         m_pSel->SetFlags((BST_CHECKED == IsDlgButtonChecked(m_hwnd, IDC_RBN_CONFIRMPURGE_UNPINNED)) ? 
  700.                           PURGE_FLAG_UNPINNED : PURGE_FLAG_ALL);
  701.     }
  702.     else
  703.     {
  704.         m_pSel->SetFlags(PURGE_FLAG_NONE);
  705.     }
  706. }
  707. void
  708. CConfirmPurgeDialog::OnDestroy(
  709.     void
  710.     )
  711. {
  712.     if (NULL != m_hwndLV)
  713.     {
  714.         const int cShares = ListView_GetItemCount(m_hwndLV);
  715.         for (int i = 0; i < cShares; i++)
  716.         {
  717.             delete[] (LPTSTR)LVGetItemData(m_hwndLV, i);        
  718.         }
  719.     }
  720. }
  721. void
  722. CConfirmPurgeDialog::OnSettingChange(
  723.     UINT uMsg,
  724.     WPARAM wParam,
  725.     LPARAM lParam
  726.     )
  727. {
  728.     if (NULL != m_hwndLV)
  729.         SendMessage(m_hwndLV, uMsg, wParam, lParam);
  730. }
  731. LPTSTR 
  732. CConfirmPurgeDialog::MyStrDup(
  733.     LPCTSTR psz
  734.     )
  735. {
  736.     LPTSTR pszCopy = new TCHAR[lstrlen(psz) + 1];
  737.     if (NULL != pszCopy)
  738.         lstrcpy(pszCopy, psz);
  739.     return pszCopy;
  740. }
  741. CCachePurgerSel::~CCachePurgerSel(
  742.     void
  743.     )
  744. {
  745.     if (NULL != m_hdpaShares)
  746.     {
  747.         const int cShares = DPA_GetPtrCount(m_hdpaShares);
  748.         for (int i = 0; i < cShares; i++)
  749.         {
  750.             delete[] DPA_GetPtr(m_hdpaShares, i);
  751.         }
  752.         DPA_Destroy(m_hdpaShares);
  753.     }
  754.     if (NULL != m_psidUser && IsValidSid(m_psidUser))
  755.     {
  756.         FreeSid(m_psidUser);
  757.     }
  758. }
  759. LPTSTR 
  760. CCachePurgerSel::MyStrDup(
  761.     LPCTSTR psz
  762.     )
  763. {
  764.     LPTSTR pszCopy = new TCHAR[lstrlen(psz) + 1];
  765.     if (NULL != pszCopy)
  766.         lstrcpy(pszCopy, psz);
  767.     return pszCopy;
  768. }
  769. BOOL
  770. CCachePurgerSel::SetUserSid(
  771.     PSID psid
  772.     )
  773. {
  774.     if (NULL != m_psidUser && IsValidSid(m_psidUser))
  775.     {
  776.         FreeSid(m_psidUser);
  777.         m_psidUser = NULL;
  778.     }
  779.     if (NULL != psid && IsValidSid(psid))
  780.     {
  781.         DWORD cbSid = GetLengthSid(psid);
  782.         PSID psidNew = (PSID)new BYTE[cbSid];
  783.         if (NULL != psidNew)
  784.         {
  785.             if (!CopySid(cbSid, psidNew, psid))
  786.             {
  787.                 delete[] psidNew;
  788.                 psidNew = NULL;
  789.             }
  790.             m_psidUser = psidNew;
  791.         }
  792.     }
  793.     return NULL != m_psidUser;
  794. }
  795. BOOL 
  796. CCachePurgerSel::AddShareName(
  797.     LPCTSTR pszShare
  798.     )
  799. {
  800.     //
  801.     // Be tolerant of a NULL pszShare pointer.
  802.     //
  803.     if (NULL != m_hdpaShares && NULL != pszShare)
  804.     {
  805.         LPTSTR pszCopy = MyStrDup(pszShare);
  806.         if (NULL != pszCopy)
  807.         {
  808.             if (-1 != DPA_AppendPtr(m_hdpaShares, pszCopy))
  809.             {
  810.                 return true;
  811.             }
  812.             delete[] pszCopy;
  813.         }
  814.     }
  815.     return false;
  816. }
  817. typedef struct _RemoveFolderCBData
  818. {
  819.     PFN_CSCUIRemoveFolderCallback pfnCB;
  820.     LPARAM lParam;
  821. } RemoveFolderCBData, *PRemoveFolderCBData;
  822. BOOL CALLBACK
  823. _RemoveFolderCallback(CCachePurger *pPurger)
  824. {
  825.     PRemoveFolderCBData pcbdata = (PRemoveFolderCBData)pPurger->CallbackData();
  826.     if (pcbdata->pfnCB)
  827.         return pcbdata->pfnCB(pPurger->FileName(), pcbdata->lParam);
  828.     return TRUE;
  829. }
  830. STDAPI
  831. CSCUIRemoveFolderFromCache(LPCWSTR pszFolder,
  832.                            DWORD /*dwReserved*/,    // can use for flags
  833.                            PFN_CSCUIRemoveFolderCallback pfnCB,
  834.                            LPARAM lParam)
  835. {
  836.     RemoveFolderCBData cbdata = { pfnCB, lParam };
  837.     CCachePurgerSel sel;
  838.     sel.SetFlags(PURGE_FLAG_ALL | PURGE_IGNORE_ACCESS);
  839.     sel.AddShareName(pszFolder);
  840.     CCachePurger purger(sel, _RemoveFolderCallback, &cbdata);
  841.     return purger.Delete();
  842. }