#include "init.h" #include #include #include "general.h" #include "dlg.h" #include "emptyvol.h" #include "parseinf.h" #define MAX_DRIVES 26 // there are 26 letters only // {8369AB20-56C9-11d0-94E8-00AA0059CE02} const CLSID CLSID_EmptyControlVolumeCache = { 0x8369ab20, 0x56c9, 0x11d0, 0x94, 0xe8, 0x0, 0xaa, 0x0, 0x59, 0xce, 0x2}; /****************************************************************************** class CEmptyControlVolumeCache ******************************************************************************/ class CEmptyControlVolumeCache : public IEmptyVolumeCache { public: // IUnknown Methods STDMETHODIMP QueryInterface(REFIID iid, void** ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IEmptyVolumeCache Methods STDMETHODIMP Initialize(HKEY hRegKey, LPCWSTR pszVolume, LPWSTR *ppszDisplayName, LPWSTR *ppszDescription, DWORD *pdwFlags); STDMETHODIMP GetSpaceUsed(DWORDLONG *pdwSpaceUsed, IEmptyVolumeCacheCallBack *picb); STDMETHODIMP Purge(DWORDLONG dwSpaceToFree, IEmptyVolumeCacheCallBack *picb); STDMETHODIMP ShowProperties(HWND hwnd); STDMETHODIMP Deactivate(DWORD *pdwFlags); // Attributes public: static HRESULT IsControlExpired(HANDLE hControl, BOOL fUseCache = TRUE); // Implementation public: // Constructor and destructor CEmptyControlVolumeCache(); virtual ~CEmptyControlVolumeCache(); protected: // implementation data helpers // Note. Write operations are only perfomed by the private functions // prefixed cpl_XXX. Read access is not restricted. LPCACHE_PATH_NODE m_pPathsHead, m_pPathsTail; // Note. Write operations are only perfomed by the private functions // prefixed chl_XXX. Read access is not restricted. LPCONTROL_HANDLE_NODE m_pControlsHead, m_pControlsTail; WCHAR m_szVol[4]; DWORDLONG m_dwTotalSize; ULONG m_cRef; // implementation helper routines // cpl prefix stands for CachePathsList HRESULT cpl_Add(LPCTSTR pszCachePath); void cpl_Remove(); HRESULT cpl_CreateForVolume(LPCWSTR pszVolume = NULL); // chl prefix stands for ControlHandlesList HRESULT chl_Find(HANDLE hControl, LPCONTROL_HANDLE_NODE *rgp = NULL, UINT nSize = 1) const; HRESULT chl_Add(HANDLE hControl); void chl_Remove(LPCONTROL_HANDLE_NODE rgp[2]); HRESULT chl_Remove(HANDLE hControl = NULL); HRESULT chl_CreateForPath(LPCTSTR pszCachePath, DWORDLONG *pdwUsedInFolder = NULL); friend HRESULT _stdcall EmptyControl_CreateInstance(IUnknown *pUnkOuter, REFIID riid, LPVOID* ppv); // friend BOOL CALLBACK EmptyControl_PropertiesDlgProc(HWND hDlg, // UINT msg, WPARAM wp, LPARAM lp); }; STDAPI EmptyControl_CreateInstance(IUnknown *pUnkOuter, REFIID riid, LPVOID* ppv) { *ppv = NULL; if (pUnkOuter != NULL) return CLASS_E_NOAGGREGATION; CEmptyControlVolumeCache *pCRC = new CEmptyControlVolumeCache; if (pCRC == NULL) return E_OUTOFMEMORY; HRESULT hr = pCRC->QueryInterface(riid, ppv); pCRC->Release(); return hr; } ///////////////////////////////////////////////////////////////////////////// // CEmptyControlVolumeCache constructor and destructor CEmptyControlVolumeCache::CEmptyControlVolumeCache() { DllAddRef(); m_pPathsHead = m_pPathsTail = NULL; m_pControlsHead = m_pControlsTail = NULL; m_szVol[0] = L'\0'; m_dwTotalSize = 0; m_cRef = 1; } CEmptyControlVolumeCache::~CEmptyControlVolumeCache() { ASSERT(m_cRef == 0); cpl_Remove(); chl_Remove(); DllRelease(); } ///////////////////////////////////////////////////////////////////////////// // CEmptyControlVolumeCache attributes // CEmptyControlVolumeCache::IsControlExpired // Check if a control has not been accessed for more than N days. If there is // no registry entry, default is DEFAULT_DAYS_BEFORE_EXPIRE. // // Parameters: fUseCache can be used to not go to the registry for the value // of N above. // // Returns: either the Win32 error converted to HRESULT or // S_OK if control is expired and S_FALSE if not; // // Used by: only by CEmptyControlVolumeCache::chl_CreateForPath // HRESULT CEmptyControlVolumeCache::IsControlExpired(HANDLE hControl, BOOL fUseCache /*= TRUE*/) { SYSTEMTIME stNow; FILETIME ftNow; FILETIME ftLastAccess; LARGE_INTEGER timeExpire; HRESULT hr = S_OK; ASSERT(hControl != NULL && hControl != INVALID_HANDLE_VALUE); // don't expire controls with uncertain access time. if (FAILED(GetLastAccessTime(hControl, &ftLastAccess))) return S_FALSE; //----- Time calculations (wierd looking) ----- // Add to last access date the length of time before a control expires timeExpire.LowPart = ftLastAccess.dwLowDateTime; timeExpire.HighPart = ftLastAccess.dwHighDateTime; timeExpire.QuadPart += (((CCacheItem*)hControl)->GetExpireDays() * 864000000000L); //24*3600*10^7 GetLocalTime(&stNow); SystemTimeToFileTime(&stNow, &ftNow); return CompareFileTime((FILETIME*)&timeExpire, &ftNow) <= 0 ? S_OK : S_FALSE; } ///////////////////////////////////////////////////////////////////////////// // CEmptyControlVolumeCache CachePathsList routines // CEmptyControlVolumeCache::cpl_Add // Check if a control has not been accessed for more than N days. If there is // no registry entry, default is DEFAULT_DAYS_BEFORE_EXPIRE. // // Parameters: a cache folder path to add. // // Returns: E_OUTOFMEMORY or // S_FALSE if path is already in the list or S_OK if added. // // Used by: only by CEmptyControlVolumeCache::cpl_CreateForVolume // HRESULT CEmptyControlVolumeCache::cpl_Add(LPCTSTR pszCachePath) { LPCACHE_PATH_NODE pNode; ASSERT(pszCachePath != NULL); for (pNode = m_pPathsHead; pNode != NULL; pNode = pNode->pNext) if (lstrcmpi(pNode->szCachePath, pszCachePath) == 0) break; if (pNode != NULL) return S_FALSE; pNode = new CACHE_PATH_NODE; if (pNode == NULL) return E_OUTOFMEMORY; lstrcpyn(pNode->szCachePath, pszCachePath, MAX_PATH); pNode->pNext = NULL; if (m_pPathsHead == NULL) m_pPathsHead = pNode; else m_pPathsTail->pNext = pNode; m_pPathsTail = pNode; return S_OK; } // CEmptyControlVolumeCache::cpl_Remove // Remove all paths from the internal list. // // Parameters: none; // // Returns: void; // // Used by: several obvious places // void CEmptyControlVolumeCache::cpl_Remove() { // remove cache path list for (LPCACHE_PATH_NODE pCur = m_pPathsHead; m_pPathsHead != NULL; pCur = m_pPathsHead) { m_pPathsHead = m_pPathsHead->pNext; delete[] pCur; } m_pPathsTail = NULL; } // CEmptyControlVolumeCache::cpl_CreateForVolume // Build a list of paths to cache folders. // // Parameters: volume (or drive) where these folders are; // // Returns: S_OK or one out of the bunch of obvious errors; // // Used by: only by IEmptyVolumeCache::GetSpaceUsed // HRESULT CEmptyControlVolumeCache::cpl_CreateForVolume(LPCWSTR pszVolume) { HKEY hkey = NULL; HRESULT hr = E_FAIL; int iDriveNum; ASSERT(pszVolume != NULL); iDriveNum = PathGetDriveNumberW(pszVolume); if (iDriveNum < 0) return E_INVALIDARG; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_ACTIVEX_CACHE, 0, KEY_READ, &hkey) != ERROR_SUCCESS) return E_FAIL; TCHAR szCachePath[MAX_PATH], szValue[MAX_PATH]; DWORD dwIndex = 0, dwValueLen = MAX_PATH, dwLen = MAX_PATH; cpl_Remove(); while (RegEnumValue(hkey, dwIndex++, szValue, &dwValueLen, NULL, NULL, (LPBYTE)szCachePath, &dwLen) == ERROR_SUCCESS) { dwLen = dwValueLen = MAX_PATH; if (PathGetDriveNumber(szCachePath) != iDriveNum) continue; // we must have added at least one successfully to get a success code.. hr = cpl_Add(szCachePath); if (FAILED(hr)) break; } RegCloseKey(hkey); if (FAILED(hr)) cpl_Remove(); return hr; } ///////////////////////////////////////////////////////////////////////////// // CEmptyControlVolumeCache ControlHandlesList routines // CEmptyControlVolumeCache::chl_Find // Find and return a location for the specified handle in the internal list. // if (rgp == NULL), only result matters; // if (rgp != NULL), // if (nSize == 1), *rgp is going to have found item (if it's there) // if (nSize >= 2), *rgp[0] = prev to the found item, and *rgp[1] is the // item. // // Parameters: explained above; // // Returns: S_OK if the item is found, S_FALSE otherwise or // one out of the bunch of obvious errors; // // Used by: CEmptyControlVolumeCache::chl_Add and // CEmptyControlVolumeCache::chl_Remove // HRESULT CEmptyControlVolumeCache::chl_Find(HANDLE hControl, LPCONTROL_HANDLE_NODE *rgp /*= NULL*/, UINT nSize /*= 1*/) const { LPCONTROL_HANDLE_NODE pCur, pPrev = NULL; ASSERT(hControl != NULL && hControl != INVALID_HANDLE_VALUE); for (pCur = m_pControlsHead; pCur != NULL; pCur = pCur->pNext) { if (pCur->hControl == hControl) break; pPrev = pCur; } if (pCur == NULL) pPrev = NULL; // zero out possible return if (rgp != NULL && nSize > 0) if (nSize == 1) *rgp = pCur; else { /* if (nSize >= 2) */ rgp[0] = pPrev; rgp[1] = pCur; } return (pCur != NULL) ? S_OK : E_FAIL; } HRESULT CEmptyControlVolumeCache::chl_Add(HANDLE hControl) { LPCONTROL_HANDLE_NODE pNode; DWORD dwSize; // Note. Retail build assumes that handle is not in the list. ASSERT(hControl != NULL && hControl != INVALID_HANDLE_VALUE); ASSERT(FAILED(chl_Find(hControl))); pNode = new CONTROL_HANDLE_NODE; if (pNode == NULL) return E_OUTOFMEMORY; GetControlInfo(hControl, GCI_SIZESAVED, &dwSize, NULL, 0); pNode->hControl = hControl; pNode->pNext = NULL; if (m_pControlsHead == NULL) m_pControlsHead = pNode; else { ASSERT(m_pControlsHead != NULL); m_pControlsTail->pNext = pNode; } m_pControlsTail = pNode; m_dwTotalSize += dwSize; return S_OK; } void CEmptyControlVolumeCache::chl_Remove(LPCONTROL_HANDLE_NODE rgp[2]) { DWORD dwSize; if (m_pControlsHead == NULL || (rgp[0] != NULL && rgp[1] == NULL)) return; if (rgp[0] != NULL) rgp[0]->pNext = rgp[1]->pNext; else { rgp[1] = m_pControlsHead; m_pControlsHead = m_pControlsHead->pNext; } if (rgp[1] == m_pControlsTail) m_pControlsTail = rgp[0]; if (GetControlInfo(rgp[1]->hControl, GCI_SIZESAVED, &dwSize, NULL, 0)) { // only proceeed if GetControlInfo succeeds // Note. This code assumes that the size of a control didn't change since // it was added. m_dwTotalSize -= dwSize; } ReleaseControlHandle(rgp[1]->hControl); delete rgp[1]; } HRESULT CEmptyControlVolumeCache::chl_Remove(HANDLE hControl /*= NULL*/) { LPCONTROL_HANDLE_NODE rgp[2] = { NULL, NULL }; HRESULT hr; ASSERT(hControl != INVALID_HANDLE_VALUE); if (hControl != NULL) { hr = chl_Find(hControl, rgp, 2); if (FAILED(hr)) return hr; chl_Remove(rgp); return S_OK; } while (m_pControlsHead != NULL) chl_Remove(rgp); ASSERT(m_pControlsHead == NULL && m_pControlsTail == NULL); return S_OK; } // CEmptyControlVolumeCache::chl_CreateForPath // Calculate the size in bytes taken up by controls in the control cache // folder specified. // // Parameters: pszCachePath is a path to the controls cache folder; // pdwSpaceUsed is the result // // Used by: only by IEmptyVolumeCache::GetSpaceUsed // HRESULT CEmptyControlVolumeCache::chl_CreateForPath(LPCTSTR pszCachePath, DWORDLONG *pdwUsedInFolder /*= NULL*/) { DWORDLONG dwCopy; HANDLE hFind = NULL, hControl = NULL; LONG lResult; BOOL fCache = FALSE; dwCopy = m_dwTotalSize; for (lResult = FindFirstControl(hFind, hControl, pszCachePath); lResult == ERROR_SUCCESS; lResult = FindNextControl(hFind, hControl)) { lResult = HRESULT_CODE(IsControlExpired(hControl, fCache)); fCache = TRUE; if (lResult != ERROR_SUCCESS) continue; lResult = HRESULT_CODE(chl_Add(hControl)); if (lResult != ERROR_SUCCESS) break; } FindControlClose(hFind); if (lResult == ERROR_NO_MORE_ITEMS) lResult = ERROR_SUCCESS; if (pdwUsedInFolder != NULL) { *pdwUsedInFolder = m_dwTotalSize - dwCopy; } return HRESULT_FROM_WIN32(lResult); } /****************************************************************************** IUnknown Methods ******************************************************************************/ STDMETHODIMP CEmptyControlVolumeCache::QueryInterface(REFIID iid, void** ppv) { if (ppv == NULL) return E_POINTER; *ppv = NULL; if (iid != IID_IUnknown && iid != IID_IEmptyVolumeCache) return E_NOINTERFACE; *ppv = (void *)this; AddRef(); return S_OK; } STDMETHODIMP_(ULONG) CEmptyControlVolumeCache::AddRef() { return (++m_cRef); } STDMETHODIMP_(ULONG) CEmptyControlVolumeCache::Release() { if (--m_cRef) return m_cRef; delete this; return 0; } /****************************************************************************** IEmptyVolumeCache Methods ******************************************************************************/ STDMETHODIMP CEmptyControlVolumeCache::Initialize(HKEY hRegKey, LPCWSTR pszVolume, LPWSTR *ppszDisplayName, LPWSTR *ppszDescription, DWORD *pdwFlags) { if (pszVolume == NULL) return E_POINTER; if (ppszDisplayName == NULL || ppszDescription == NULL) return E_POINTER; if (pdwFlags == NULL) return E_POINTER; StrCpyNW(m_szVol, pszVolume, ARRAYSIZE(m_szVol)); cpl_Remove(); chl_Remove(); if (lstrlenW(m_szVol) == 0) { return E_UNEXPECTED; } if (FAILED(cpl_CreateForVolume(m_szVol))) { return E_FAIL; } *ppszDisplayName = *ppszDescription = NULL; *pdwFlags = EVCF_HASSETTINGS | EVCF_ENABLEBYDEFAULT | EVCF_ENABLEBYDEFAULT_AUTO; return S_OK; } STDMETHODIMP CEmptyControlVolumeCache::GetSpaceUsed(DWORDLONG *pdwSpaceUsed, IEmptyVolumeCacheCallBack *picb) { LPCACHE_PATH_NODE pCur; HRESULT hr = S_OK; if (pdwSpaceUsed == NULL) { hr = E_POINTER; goto LastNotification; } *pdwSpaceUsed = 0; if (lstrlenW(m_szVol) == 0) { hr = E_UNEXPECTED; goto LastNotification; } for (pCur = m_pPathsHead; pCur != NULL; pCur = pCur->pNext) { DWORDLONG dwlThisItem = 0; if (FAILED(chl_CreateForPath(pCur->szCachePath, &dwlThisItem))) hr = S_FALSE; // at least one failed m_dwTotalSize += dwlThisItem; if (picb != NULL) picb->ScanProgress(m_dwTotalSize, 0, NULL); } // cpl_Remove(); // because of ShowProperties *pdwSpaceUsed = m_dwTotalSize; LastNotification: if (picb != NULL) picb->ScanProgress(m_dwTotalSize, EVCCBF_LASTNOTIFICATION, NULL); return hr; } STDMETHODIMP CEmptyControlVolumeCache::Purge(DWORDLONG dwSpaceToFree, IEmptyVolumeCacheCallBack *picb) { LPCONTROL_HANDLE_NODE rgp[2] = { NULL, NULL }; DWORDLONG dwSpaceFreed; HANDLE hControl; DWORD dwSize; HRESULT hr; if (m_pControlsHead == NULL) { DWORDLONG dwSpaceUsed; hr = GetSpaceUsed(&dwSpaceUsed, picb); if (FAILED(hr) || m_pControlsHead == NULL) hr = FAILED(hr) ? hr : STG_E_NOMOREFILES; if (picb != NULL) picb->PurgeProgress(0, dwSpaceToFree, EVCCBF_LASTNOTIFICATION, NULL); return hr; } dwSpaceFreed = 0; ASSERT(m_pControlsHead != NULL); while (m_pControlsHead != NULL) { hControl = m_pControlsHead->hControl; ASSERT(hControl != NULL && hControl != INVALID_HANDLE_VALUE); GetControlInfo(hControl, GCI_SIZESAVED, &dwSize, NULL, 0); hr = RemoveControlByHandle2(hControl, FALSE, TRUE); if (SUCCEEDED(hr)) { dwSpaceFreed += dwSize; if (picb != NULL) picb->PurgeProgress(dwSpaceFreed, dwSpaceToFree, 0, NULL); } chl_Remove(rgp); if (dwSpaceFreed >= dwSpaceToFree) break; } if (picb != NULL) picb->PurgeProgress(dwSpaceFreed, dwSpaceToFree, 0, NULL); return S_OK; } // Note. This function opens the last cache folder in the internal list. STDMETHODIMP CEmptyControlVolumeCache::ShowProperties(HWND hwnd) { // Note. (According to SeanF) The codedownload engine will query // ActiveXCache key under HKLM\SOFTWARE\Microsoft\Windows\ // CurrentVersion\Internet Settings. The value of this key should // be equal to the last item in the CachePathsList which is why // navigation below is done for the tail. if (m_pPathsTail == NULL || m_pPathsTail->szCachePath == NULL) return E_UNEXPECTED; ShellExecute(hwnd, NULL, m_pPathsTail->szCachePath, NULL, NULL, SW_SHOW); return S_OK; /* int iDlgResult; iDlgResult = MLDialogBoxWrap(MLGetHinst(), MAKEINTRESOURCE(IDD_PROP_EXPIRE), hwnd, EmptyControl_PropertiesDlgProc); return iDlgResult == IDOK ? S_OK : S_FALSE; */ } STDMETHODIMP CEmptyControlVolumeCache::Deactivate(DWORD *pdwFlags) { if (pdwFlags == NULL) return E_INVALIDARG; *pdwFlags = 0; return S_OK; } ///////////////////////////////////////////////////////////////////////////// // Implementation helpers routines (private) /* static void msg_OnInitDialog(HWND hDlg); static BOOL msg_OnCommand(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp); static BOOL cmd_OnOK(HWND hDlg); INT_PTR CALLBACK EmptyControl_PropertiesDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static MSD rgmsd[] = { { WM_INITDIALOG, ms_vh, (PFN)msg_OnInitDialog }, { WM_COMMAND, ms_bwwwl, (PFN)msg_OnCommand }, { WM_NULL, ms_end, (PFN)NULL } }; return Dlg_MsgProc(rgmsd, hDlg, msg, wp, lp); } void msg_OnInitDialog(HWND hDlg) { UINT nDays; CEmptyControlVolumeCache::GetDaysBeforeExpire(&nDays); SetDlgItemInt(hDlg, IDC_EDIT_EXPIRE, nDays, FALSE); } BOOL msg_OnCommand(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static CMD rgcmd[] = { { IDOK, 0, ms_bh, (PFN)cmd_OnOK }, { 0, 0, ms_end, (PFN)NULL } }; return Msg_OnCmd(rgcmd, hDlg, msg, wp, lp); } BOOL cmd_OnOK(HWND hDlg) { UINT nDays; BOOL fWorked; nDays = GetDlgItemInt(hDlg, IDC_EDIT_EXPIRE, &fWorked, FALSE); if (!fWorked) { MessageBeep(-1); SetFocus(GetDlgItem(hDlg, IDC_EDIT_EXPIRE)); return FALSE; } CEmptyControlVolumeCache::SetDaysBeforeExpire(nDays); return TRUE; } */