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

Windows Kernel

Development Platform:

Visual C++

  1. #include "init.h"
  2. #include <emptyvc.h>
  3. #include <regstr.h>
  4. #include "general.h"
  5. #include "dlg.h"
  6. #include "emptyvol.h"
  7. #include "parseinf.h"
  8. #define MAX_DRIVES                 26   // there are 26 letters only
  9. // {8369AB20-56C9-11d0-94E8-00AA0059CE02}
  10. const CLSID CLSID_EmptyControlVolumeCache = {
  11.                             0x8369ab20, 0x56c9, 0x11d0, 
  12.                             0x94, 0xe8, 0x0, 0xaa, 0x0,
  13.                             0x59, 0xce, 0x2};
  14. /******************************************************************************
  15.     class CEmptyControlVolumeCache
  16. ******************************************************************************/
  17. class CEmptyControlVolumeCache : public IEmptyVolumeCache
  18. {
  19. public:
  20.     // IUnknown Methods
  21.     STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
  22.     STDMETHODIMP_(ULONG) AddRef();
  23.     STDMETHODIMP_(ULONG) Release();
  24.     // IEmptyVolumeCache Methods
  25.     STDMETHODIMP Initialize(HKEY hRegKey, LPCWSTR pszVolume,
  26.         LPWSTR *ppszDisplayName, LPWSTR *ppszDescription, DWORD *pdwFlags);
  27.     STDMETHODIMP GetSpaceUsed(DWORDLONG *pdwSpaceUsed,
  28.         IEmptyVolumeCacheCallBack *picb);
  29.     STDMETHODIMP Purge(DWORDLONG dwSpaceToFree,
  30.         IEmptyVolumeCacheCallBack *picb);
  31.     STDMETHODIMP ShowProperties(HWND hwnd);
  32.     STDMETHODIMP Deactivate(DWORD *pdwFlags);
  33. // Attributes
  34. public:
  35.     static HRESULT IsControlExpired(HANDLE hControl, BOOL fUseCache = TRUE);
  36. // Implementation
  37. public:
  38.     // Constructor and destructor
  39.     CEmptyControlVolumeCache();
  40.     virtual ~CEmptyControlVolumeCache();
  41. protected:
  42. // implementation data helpers
  43.     // Note. Write operations are only perfomed by the private functions
  44.     //       prefixed cpl_XXX. Read access is not restricted.
  45.     LPCACHE_PATH_NODE m_pPathsHead,
  46.                       m_pPathsTail;
  47.     // Note. Write operations are only perfomed by the private functions
  48.     //       prefixed chl_XXX. Read access is not restricted.
  49.     LPCONTROL_HANDLE_NODE m_pControlsHead,
  50.                           m_pControlsTail;
  51.     WCHAR     m_szVol[4];
  52.     DWORDLONG m_dwTotalSize;
  53.     ULONG     m_cRef;
  54. // implementation helper routines
  55.     // cpl prefix stands for CachePathsList
  56.     HRESULT cpl_Add(LPCTSTR pszCachePath);
  57.     void    cpl_Remove();
  58.     HRESULT cpl_CreateForVolume(LPCWSTR pszVolume = NULL);
  59.     // chl prefix stands for ControlHandlesList
  60.     HRESULT chl_Find(HANDLE hControl,
  61.         LPCONTROL_HANDLE_NODE *rgp = NULL, UINT nSize = 1) const;
  62.     HRESULT chl_Add(HANDLE hControl);
  63.     void    chl_Remove(LPCONTROL_HANDLE_NODE rgp[2]);
  64.     HRESULT chl_Remove(HANDLE hControl = NULL);
  65.     HRESULT chl_CreateForPath(LPCTSTR pszCachePath,
  66.         DWORDLONG *pdwUsedInFolder = NULL);
  67.     friend HRESULT _stdcall EmptyControl_CreateInstance(IUnknown *pUnkOuter,
  68.         REFIID riid, LPVOID* ppv);
  69. //  friend BOOL CALLBACK EmptyControl_PropertiesDlgProc(HWND hDlg,
  70. //      UINT msg, WPARAM wp, LPARAM lp);
  71. };
  72. STDAPI EmptyControl_CreateInstance(IUnknown *pUnkOuter, REFIID riid, LPVOID* ppv)
  73. {
  74.     *ppv = NULL;
  75.     if (pUnkOuter != NULL)
  76.         return CLASS_E_NOAGGREGATION;
  77.     CEmptyControlVolumeCache *pCRC = new CEmptyControlVolumeCache;
  78.     if (pCRC == NULL)
  79.         return E_OUTOFMEMORY;
  80.     HRESULT hr = pCRC->QueryInterface(riid, ppv);
  81.     pCRC->Release();
  82.     return hr;
  83. }
  84. /////////////////////////////////////////////////////////////////////////////
  85. // CEmptyControlVolumeCache constructor and destructor
  86. CEmptyControlVolumeCache::CEmptyControlVolumeCache()
  87. {
  88.     DllAddRef();
  89.     m_pPathsHead = m_pPathsTail = NULL;
  90.     m_pControlsHead = m_pControlsTail = NULL;
  91.     m_szVol[0] = L'';
  92.     m_dwTotalSize = 0;
  93.     m_cRef = 1;
  94. }
  95. CEmptyControlVolumeCache::~CEmptyControlVolumeCache()
  96. {
  97.     ASSERT(m_cRef == 0);
  98.     cpl_Remove();
  99.     chl_Remove();
  100.     DllRelease();
  101. }
  102. /////////////////////////////////////////////////////////////////////////////
  103. // CEmptyControlVolumeCache attributes
  104. // CEmptyControlVolumeCache::IsControlExpired
  105. // Check if a control has not been accessed for more than N days. If there is
  106. // no registry entry, default is DEFAULT_DAYS_BEFORE_EXPIRE.
  107. //
  108. // Parameters: fUseCache can be used to not go to the registry for the value
  109. // of N above.
  110. //
  111. // Returns: either the Win32 error converted to HRESULT or
  112. //          S_OK if control is expired and S_FALSE if not;
  113. //
  114. // Used by: only by CEmptyControlVolumeCache::chl_CreateForPath
  115. //
  116. HRESULT CEmptyControlVolumeCache::IsControlExpired(HANDLE hControl,
  117.     BOOL fUseCache /*= TRUE*/)
  118. {
  119.     SYSTEMTIME    stNow;
  120.     FILETIME      ftNow;
  121.     FILETIME      ftLastAccess;
  122.     LARGE_INTEGER timeExpire;
  123.     HRESULT       hr = S_OK;
  124.     ASSERT(hControl != NULL && hControl != INVALID_HANDLE_VALUE);
  125.     // don't expire controls with uncertain access time.
  126.     if (FAILED(GetLastAccessTime(hControl, &ftLastAccess)))
  127.         return S_FALSE;
  128.  
  129.     //----- Time calculations (wierd looking) -----
  130.     // Add to last access date the length of time before a control expires
  131.     timeExpire.LowPart  = ftLastAccess.dwLowDateTime;
  132.     timeExpire.HighPart = ftLastAccess.dwHighDateTime;
  133.     timeExpire.QuadPart += (((CCacheItem*)hControl)->GetExpireDays() * 864000000000L); //24*3600*10^7
  134.     GetLocalTime(&stNow);
  135.     SystemTimeToFileTime(&stNow, &ftNow);
  136.     return CompareFileTime((FILETIME*)&timeExpire, &ftNow) <= 0 ?
  137.         S_OK : S_FALSE;
  138. }
  139. /////////////////////////////////////////////////////////////////////////////
  140. // CEmptyControlVolumeCache CachePathsList routines
  141. // CEmptyControlVolumeCache::cpl_Add
  142. // Check if a control has not been accessed for more than N days. If there is
  143. // no registry entry, default is DEFAULT_DAYS_BEFORE_EXPIRE.
  144. //
  145. // Parameters: a cache folder path to add.
  146. //
  147. // Returns: E_OUTOFMEMORY or
  148. //          S_FALSE if path is already in the list or S_OK if added.
  149. //
  150. // Used by: only by CEmptyControlVolumeCache::cpl_CreateForVolume
  151. //
  152. HRESULT CEmptyControlVolumeCache::cpl_Add(LPCTSTR pszCachePath)
  153. {
  154.     LPCACHE_PATH_NODE pNode;
  155.     ASSERT(pszCachePath != NULL);
  156.     for (pNode = m_pPathsHead; pNode != NULL; pNode = pNode->pNext)
  157.         if (lstrcmpi(pNode->szCachePath, pszCachePath) == 0)
  158.             break;
  159.     if (pNode != NULL)
  160.         return S_FALSE;
  161.     pNode = new CACHE_PATH_NODE;
  162.     if (pNode == NULL)
  163.         return E_OUTOFMEMORY;
  164.     lstrcpyn(pNode->szCachePath, pszCachePath, MAX_PATH);
  165.     pNode->pNext = NULL;
  166.     if (m_pPathsHead == NULL)
  167.         m_pPathsHead = pNode;
  168.     else
  169.         m_pPathsTail->pNext = pNode;
  170.     m_pPathsTail = pNode;
  171.     return S_OK;
  172. }
  173. // CEmptyControlVolumeCache::cpl_Remove
  174. // Remove all paths from the internal list.
  175. //
  176. // Parameters: none;
  177. //
  178. // Returns: void;
  179. //
  180. // Used by: several obvious places
  181. //
  182. void CEmptyControlVolumeCache::cpl_Remove()
  183. {
  184.     // remove cache path list
  185.     for (LPCACHE_PATH_NODE pCur = m_pPathsHead;
  186.          m_pPathsHead != NULL;
  187.          pCur = m_pPathsHead) {
  188.         m_pPathsHead = m_pPathsHead->pNext;
  189.         delete[] pCur;
  190.     }
  191.     m_pPathsTail = NULL;
  192. }
  193. // CEmptyControlVolumeCache::cpl_CreateForVolume
  194. // Build a list of paths to cache folders.
  195. //
  196. // Parameters: volume (or drive) where these folders are;
  197. //
  198. // Returns: S_OK or one out of the bunch of obvious errors;
  199. //
  200. // Used by: only by IEmptyVolumeCache::GetSpaceUsed
  201. //
  202. HRESULT CEmptyControlVolumeCache::cpl_CreateForVolume(LPCWSTR pszVolume)
  203. {
  204.     HKEY    hkey = NULL;
  205.     HRESULT hr   = E_FAIL;
  206.     int     iDriveNum;
  207.     ASSERT(pszVolume != NULL);
  208.     iDriveNum = PathGetDriveNumberW(pszVolume);
  209.     if (iDriveNum < 0)
  210.         return E_INVALIDARG;
  211.     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_ACTIVEX_CACHE, 0,
  212.             KEY_READ, &hkey) != ERROR_SUCCESS)
  213.         return E_FAIL;
  214.     TCHAR szCachePath[MAX_PATH],
  215.           szValue[MAX_PATH];
  216.     DWORD dwIndex    = 0,
  217.           dwValueLen = MAX_PATH, dwLen = MAX_PATH;
  218.     cpl_Remove();
  219.     while (RegEnumValue(hkey, dwIndex++, szValue, &dwValueLen, NULL, NULL,
  220.                (LPBYTE)szCachePath, &dwLen) == ERROR_SUCCESS) {
  221.         dwLen = dwValueLen = MAX_PATH;
  222.         if (PathGetDriveNumber(szCachePath) != iDriveNum)
  223.             continue;
  224.         // we must have added at least one successfully to get a success code..
  225.         hr = cpl_Add(szCachePath);
  226.         if (FAILED(hr))
  227.             break;
  228.     }
  229.     RegCloseKey(hkey);
  230.     if (FAILED(hr))
  231.         cpl_Remove();
  232.     return hr;
  233. }
  234. /////////////////////////////////////////////////////////////////////////////
  235. // CEmptyControlVolumeCache ControlHandlesList routines
  236. // CEmptyControlVolumeCache::chl_Find
  237. // Find and return a location for the specified handle in the internal list.
  238. // if (rgp == NULL), only result matters;
  239. // if (rgp != NULL),
  240. //     if (nSize == 1), *rgp is going to have found item (if it's there)
  241. //     if (nSize >= 2), *rgp[0] = prev to the found item, and *rgp[1] is the
  242. //                      item.
  243. //
  244. // Parameters: explained above;
  245. //
  246. // Returns: S_OK if the item is found, S_FALSE otherwise or
  247. //          one out of the bunch of obvious errors;
  248. //
  249. // Used by: CEmptyControlVolumeCache::chl_Add and
  250. //          CEmptyControlVolumeCache::chl_Remove
  251. //
  252. HRESULT CEmptyControlVolumeCache::chl_Find(HANDLE hControl,
  253.     LPCONTROL_HANDLE_NODE *rgp /*= NULL*/, UINT nSize /*= 1*/) const
  254. {
  255.     LPCONTROL_HANDLE_NODE pCur,
  256.                           pPrev = NULL;
  257.     ASSERT(hControl != NULL && hControl != INVALID_HANDLE_VALUE);
  258.     for (pCur = m_pControlsHead; pCur != NULL; pCur = pCur->pNext) {
  259.         if (pCur->hControl == hControl)
  260.             break;
  261.         pPrev = pCur;
  262.     }
  263.     if (pCur == NULL)
  264.         pPrev = NULL;                           // zero out possible return
  265.     if (rgp != NULL && nSize > 0)
  266.         if (nSize == 1)
  267.             *rgp = pCur;
  268.         else { /* if (nSize >= 2) */
  269.             rgp[0] = pPrev;
  270.             rgp[1] = pCur;
  271.         }
  272.     return (pCur != NULL) ? S_OK : E_FAIL;
  273. }
  274. HRESULT CEmptyControlVolumeCache::chl_Add(HANDLE hControl)
  275. {
  276.     LPCONTROL_HANDLE_NODE pNode;
  277.     DWORD                 dwSize;
  278.     // Note. Retail build assumes that handle is not in the list.
  279.     ASSERT(hControl != NULL && hControl != INVALID_HANDLE_VALUE);
  280.     ASSERT(FAILED(chl_Find(hControl)));
  281.     pNode = new CONTROL_HANDLE_NODE;
  282.     if (pNode == NULL)
  283.         return E_OUTOFMEMORY;
  284.     GetControlInfo(hControl, GCI_SIZESAVED, &dwSize, NULL, 0);
  285.     pNode->hControl = hControl;
  286.     pNode->pNext    = NULL;
  287.     if (m_pControlsHead == NULL)
  288.         m_pControlsHead = pNode;
  289.     else {
  290.         ASSERT(m_pControlsHead != NULL);
  291.         m_pControlsTail->pNext = pNode;
  292.     }
  293.     m_pControlsTail = pNode;
  294.     m_dwTotalSize += dwSize;
  295.     return S_OK;
  296. }
  297. void CEmptyControlVolumeCache::chl_Remove(LPCONTROL_HANDLE_NODE rgp[2])
  298. {
  299.     DWORD dwSize;
  300.     if (m_pControlsHead == NULL || (rgp[0] != NULL && rgp[1] == NULL))
  301.         return;
  302.     if (rgp[0] != NULL)
  303.         rgp[0]->pNext = rgp[1]->pNext;
  304.     else {
  305.         rgp[1] = m_pControlsHead;
  306.         m_pControlsHead = m_pControlsHead->pNext;
  307.     }
  308.     if (rgp[1] == m_pControlsTail)
  309.         m_pControlsTail = rgp[0];
  310.     GetControlInfo(rgp[1]->hControl, GCI_SIZESAVED, &dwSize, NULL, 0);
  311.     // Note. This code assumes that the size of a control didn't change since
  312.     //       it was added.
  313.     m_dwTotalSize -= dwSize;
  314.     ASSERT(m_dwTotalSize >= 0);
  315.     ReleaseControlHandle(rgp[1]->hControl);
  316.     delete rgp[1];
  317. }
  318. HRESULT CEmptyControlVolumeCache::chl_Remove(HANDLE hControl /*= NULL*/)
  319. {
  320.     LPCONTROL_HANDLE_NODE rgp[2] = { NULL, NULL };
  321.     HRESULT hr;
  322.     ASSERT(hControl != INVALID_HANDLE_VALUE);
  323.     if (hControl != NULL) {
  324.         hr = chl_Find(hControl, rgp, 2);
  325.         if (FAILED(hr))
  326.             return hr;
  327.         chl_Remove(rgp);
  328.         return S_OK;
  329.     }
  330.     while (m_pControlsHead != NULL)
  331.         chl_Remove(rgp);
  332.     ASSERT(m_pControlsHead == NULL && m_pControlsTail == NULL);
  333.     return S_OK;
  334. }
  335. // CEmptyControlVolumeCache::chl_CreateForPath
  336. // Calculate the size in bytes taken up by controls in the control cache
  337. // folder specified.
  338. //
  339. // Parameters: pszCachePath is a path to the controls cache folder;
  340. //             pdwSpaceUsed is the result
  341. //
  342. // Used by: only by IEmptyVolumeCache::GetSpaceUsed
  343. //
  344. HRESULT CEmptyControlVolumeCache::chl_CreateForPath(LPCTSTR pszCachePath,
  345.     DWORDLONG *pdwUsedInFolder /*= NULL*/)
  346. {
  347.     DWORDLONG dwCopy;
  348.     HANDLE    hFind    = NULL,
  349.               hControl = NULL;
  350.     LONG      lResult;
  351.     BOOL      fCache   = FALSE;
  352.     dwCopy = m_dwTotalSize;
  353.     for (lResult = FindFirstControl(hFind, hControl, pszCachePath);
  354.          lResult == ERROR_SUCCESS;
  355.          lResult = FindNextControl(hFind, hControl)) {
  356.         lResult = HRESULT_CODE(IsControlExpired(hControl, fCache));
  357.         fCache  = TRUE;
  358.         if (lResult != ERROR_SUCCESS)
  359.             continue;
  360.         lResult = HRESULT_CODE(chl_Add(hControl));
  361.         if (lResult != ERROR_SUCCESS)
  362.             break;
  363.     }
  364.     FindControlClose(hFind);
  365.     if (lResult == ERROR_NO_MORE_ITEMS)
  366.         lResult = ERROR_SUCCESS;
  367.     if (pdwUsedInFolder != NULL) {
  368.         *pdwUsedInFolder = m_dwTotalSize - dwCopy;
  369.         ASSERT(*pdwUsedInFolder >= 0);
  370.     }
  371.     return HRESULT_FROM_WIN32(lResult);
  372. }
  373. /******************************************************************************
  374.     IUnknown Methods
  375. ******************************************************************************/
  376. STDMETHODIMP CEmptyControlVolumeCache::QueryInterface(REFIID iid, void** ppv)
  377. {
  378.     if (ppv == NULL)
  379.         return E_POINTER;
  380.     *ppv = NULL;
  381.     if (iid != IID_IUnknown && iid != IID_IEmptyVolumeCache)
  382.         return E_NOINTERFACE;
  383.     *ppv = (void *)this;
  384.     AddRef();
  385.     return S_OK;
  386. }
  387. STDMETHODIMP_(ULONG) CEmptyControlVolumeCache::AddRef()
  388. {
  389.     return (++m_cRef);
  390. }
  391. STDMETHODIMP_(ULONG) CEmptyControlVolumeCache::Release()
  392. {
  393.     if (--m_cRef)
  394.         return m_cRef;
  395.     delete this;
  396.     return 0;   
  397. }
  398. /******************************************************************************
  399.     IEmptyVolumeCache Methods
  400. ******************************************************************************/
  401. STDMETHODIMP CEmptyControlVolumeCache::Initialize(HKEY hRegKey,
  402.     LPCWSTR pszVolume, LPWSTR *ppszDisplayName, LPWSTR *ppszDescription,
  403.     DWORD *pdwFlags)
  404. {
  405.     if (pszVolume == NULL)
  406.         return E_POINTER;
  407.     if (ppszDisplayName == NULL || ppszDescription == NULL)
  408.         return E_POINTER;
  409.     if (pdwFlags == NULL)
  410.         return E_POINTER;
  411.     StrCpyNW(m_szVol, pszVolume, ARRAYSIZE(m_szVol));
  412.     cpl_Remove();
  413.     chl_Remove();
  414.     
  415.     if (lstrlenW(m_szVol) == 0) {
  416.         return E_UNEXPECTED;
  417.     }
  418.     if (FAILED(cpl_CreateForVolume(m_szVol))) {
  419.         return E_FAIL;
  420.     }
  421.     *ppszDisplayName = *ppszDescription = NULL;
  422.     *pdwFlags = EVCF_HASSETTINGS | EVCF_ENABLEBYDEFAULT |
  423.         EVCF_ENABLEBYDEFAULT_AUTO;
  424.     return S_OK;
  425. }
  426. STDMETHODIMP CEmptyControlVolumeCache::GetSpaceUsed(DWORDLONG *pdwSpaceUsed,
  427.     IEmptyVolumeCacheCallBack *picb)
  428. {
  429.     LPCACHE_PATH_NODE pCur;
  430.     HRESULT hr = S_OK;
  431.     if (pdwSpaceUsed == NULL) {
  432.         hr = E_POINTER;
  433.         goto LastNotification;
  434.     }
  435.     *pdwSpaceUsed = 0;
  436.     if (lstrlenW(m_szVol) == 0) {
  437.         hr = E_UNEXPECTED;
  438.         goto LastNotification;
  439.     }
  440.     for (pCur = m_pPathsHead; pCur != NULL; pCur = pCur->pNext) {
  441.         DWORDLONG dwlThisItem = 0;
  442.         if (FAILED(chl_CreateForPath(pCur->szCachePath, &dwlThisItem)))
  443.             hr = S_FALSE;                       // at least one failed
  444.         m_dwTotalSize += dwlThisItem;
  445.         
  446.         if (picb != NULL)
  447.             picb->ScanProgress(m_dwTotalSize, 0, NULL);
  448.     }
  449. //  cpl_Remove();                               // because of ShowProperties
  450.     *pdwSpaceUsed = m_dwTotalSize;
  451. LastNotification:
  452.     if (picb != NULL)
  453.         picb->ScanProgress(m_dwTotalSize, EVCCBF_LASTNOTIFICATION, NULL);
  454.     return hr;
  455. }
  456. STDMETHODIMP CEmptyControlVolumeCache::Purge(DWORDLONG dwSpaceToFree,
  457.     IEmptyVolumeCacheCallBack *picb)
  458. {
  459.     LPCONTROL_HANDLE_NODE rgp[2] = { NULL, NULL };
  460.     DWORDLONG dwSpaceFreed;
  461.     HANDLE    hControl;
  462.     DWORD     dwSize;
  463.     HRESULT   hr;
  464.     if (m_pControlsHead == NULL) {
  465.         DWORDLONG dwSpaceUsed;
  466.         hr = GetSpaceUsed(&dwSpaceUsed, picb);
  467.         if (FAILED(hr) || m_pControlsHead == NULL)
  468.             hr = FAILED(hr) ? hr : STG_E_NOMOREFILES;
  469.         if (picb != NULL)
  470.             picb->PurgeProgress(0, dwSpaceToFree, EVCCBF_LASTNOTIFICATION,
  471.                 NULL);
  472.         return hr;
  473.     }
  474.     dwSpaceFreed = 0;
  475.     ASSERT(m_pControlsHead != NULL);
  476.     while (m_pControlsHead != NULL) {
  477.         hControl = m_pControlsHead->hControl;
  478.         ASSERT(hControl != NULL && hControl != INVALID_HANDLE_VALUE);
  479.         GetControlInfo(hControl, GCI_SIZESAVED, &dwSize, NULL, 0);
  480.         hr = RemoveControlByHandle2(hControl, FALSE, TRUE);
  481.         if (SUCCEEDED(hr)) {
  482.             dwSpaceFreed += dwSize;
  483.             if (picb != NULL)
  484.                 picb->PurgeProgress(dwSpaceFreed, dwSpaceToFree, 0, NULL);
  485.         }
  486.         chl_Remove(rgp);
  487.         if (dwSpaceFreed >= dwSpaceToFree)
  488.             break;
  489.     }
  490.     if (picb != NULL)
  491.         picb->PurgeProgress(dwSpaceFreed, dwSpaceToFree, 0, NULL);
  492.     return S_OK;
  493. }
  494. // Note. This function opens the last cache folder in the internal list.
  495. STDMETHODIMP CEmptyControlVolumeCache::ShowProperties(HWND hwnd)
  496. {
  497.     // Note. (According to SeanF) The codedownload engine will query
  498.     //       ActiveXCache key under HKLMSOFTWAREMicrosoftWindows
  499.     //       CurrentVersionInternet Settings. The value of this key should
  500.     //       be equal to the last item in the CachePathsList which is why
  501.     //       navigation below is done for the tail.
  502.     if (m_pPathsTail == NULL || m_pPathsTail->szCachePath == NULL)
  503.         return E_UNEXPECTED;
  504.     ShellExecute(hwnd, NULL, m_pPathsTail->szCachePath, NULL, NULL, SW_SHOW);
  505.     return S_OK;
  506. /*
  507.     int iDlgResult;
  508.     iDlgResult = MLDialogBoxWrap(MLGetHinst(), MAKEINTRESOURCE(IDD_PROP_EXPIRE), hwnd,
  509.         EmptyControl_PropertiesDlgProc);
  510.     return iDlgResult == IDOK ? S_OK : S_FALSE;
  511. */
  512. }
  513. STDMETHODIMP CEmptyControlVolumeCache::Deactivate(DWORD *pdwFlags)
  514. {
  515.     if (pdwFlags == NULL)
  516.         return E_INVALIDARG;
  517.     *pdwFlags = 0;
  518.     return S_OK;
  519. }
  520. /////////////////////////////////////////////////////////////////////////////
  521. // Implementation helpers routines (private)
  522. /*
  523. static void msg_OnInitDialog(HWND hDlg);
  524. static BOOL msg_OnCommand(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);
  525. static BOOL cmd_OnOK(HWND hDlg);
  526. INT_PTR CALLBACK EmptyControl_PropertiesDlgProc(HWND hDlg,
  527.     UINT msg, WPARAM wp, LPARAM lp)
  528. {
  529.     static MSD rgmsd[] = {
  530.         { WM_INITDIALOG, ms_vh,    (PFN)msg_OnInitDialog },
  531.         { WM_COMMAND,    ms_bwwwl, (PFN)msg_OnCommand    },
  532.         { WM_NULL,       ms_end,   (PFN)NULL             }
  533.     };
  534.     return Dlg_MsgProc(rgmsd, hDlg, msg, wp, lp);
  535. }
  536. void msg_OnInitDialog(HWND hDlg)
  537. {
  538.     UINT nDays;
  539.     CEmptyControlVolumeCache::GetDaysBeforeExpire(&nDays);
  540.     SetDlgItemInt(hDlg, IDC_EDIT_EXPIRE, nDays, FALSE);
  541. }
  542. BOOL msg_OnCommand(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
  543. {
  544.     static CMD rgcmd[] = {
  545.         { IDOK, 0, ms_bh,  (PFN)cmd_OnOK },
  546.         { 0,    0, ms_end, (PFN)NULL     }
  547.     };
  548.     return Msg_OnCmd(rgcmd, hDlg, msg, wp, lp);
  549. }
  550. BOOL cmd_OnOK(HWND hDlg)
  551. {
  552.     UINT nDays;
  553.     BOOL fWorked;
  554.     nDays = GetDlgItemInt(hDlg, IDC_EDIT_EXPIRE, &fWorked, FALSE);
  555.     if (!fWorked) {
  556.         MessageBeep(-1);
  557.         SetFocus(GetDlgItem(hDlg, IDC_EDIT_EXPIRE));
  558.         return FALSE;
  559.     }
  560.     CEmptyControlVolumeCache::SetDaysBeforeExpire(nDays);
  561.     return TRUE;
  562. }
  563. */