1330 lines
42 KiB
C++
1330 lines
42 KiB
C++
#include "ParseInf.h"
|
|
#include "general.h"
|
|
#include <shlwapi.h>
|
|
#include <wininet.h>
|
|
|
|
//#define USE_SHORT_PATH_NAME 1
|
|
|
|
#define REG_PATH_IE_CACHE_LIST TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ActiveX Cache")
|
|
|
|
#define cCachePathsMax 5 // maximum number of legacy caches + the current cache du jour
|
|
|
|
|
|
struct OCCFindData
|
|
{
|
|
LPCLSIDLIST_ITEM m_pcliHead;
|
|
LPCLSIDLIST_ITEM m_pcliTail;
|
|
struct {
|
|
TCHAR m_sz[MAX_PATH];
|
|
DWORD m_cch;
|
|
} m_aCachePath[cCachePathsMax];
|
|
|
|
OCCFindData();
|
|
~OCCFindData();
|
|
|
|
BOOL IsCachePath( LPCTSTR szPath );
|
|
|
|
// Control List operations
|
|
HRESULT AddListItem( LPCTSTR szFileName, LPCTSTR szCLSID, DWORD dwIsDistUnit );
|
|
LPCLSIDLIST_ITEM TakeFirstItem(void);
|
|
};
|
|
|
|
DWORD CCacheLegacyControl::s_dwType = 1;
|
|
DWORD CCacheDistUnit::s_dwType = 2;
|
|
|
|
HRESULT CCacheLegacyControl::Init( HKEY hkeyCLSID, LPCTSTR szFile, LPCTSTR szCLSID )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
lstrcpyn(m_szFile, szFile, ARRAYSIZE(m_szFile));
|
|
lstrcpyn(m_szCLSID, szCLSID, ARRAYSIZE(m_szCLSID));
|
|
|
|
// Get full user type name
|
|
m_szName[0] = '\0';
|
|
DWORD dw = sizeof(m_szName);
|
|
LRESULT lResult = RegQueryValue(hkeyCLSID, m_szCLSID, m_szName, (LONG*)&dw);
|
|
// if the fails, we should get a resource string (seanf 5/9/97 )
|
|
// Get type lib id
|
|
TCHAR szTypeLibValName[MAX_PATH];
|
|
CatPathStrN( szTypeLibValName, szCLSID, HKCR_TYPELIB, ARRAYSIZE(szTypeLibValName) );
|
|
|
|
dw = sizeof(m_szTypeLibID);
|
|
lResult = RegQueryValue( hkeyCLSID, szTypeLibValName, m_szTypeLibID, (LONG*)&dw);
|
|
if (lResult != ERROR_SUCCESS)
|
|
m_szTypeLibID[0] = TEXT('\0');
|
|
|
|
// Set Codebase
|
|
m_szCodeBase[0] = '\0';
|
|
m_szVersion[0] = '\0';
|
|
hr = DoParse( m_szFile, m_szCLSID );
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCacheDistUnit::Init( HKEY hkeyCLSID, LPCTSTR szFile, LPCTSTR szCLSID, HKEY hkeyDist, LPCTSTR szDU )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HKEY hkeyDU;
|
|
HKEY hkeyDLInfo; // DownloadInformation subkey
|
|
HKEY hkeyVers; // InstalledVersion subkey
|
|
HKEY hkeyCOM; // subkey of HKCR\CLSID, used if outside of cache dir
|
|
LRESULT lResult = ERROR_SUCCESS;
|
|
DWORD dw;
|
|
TCHAR szNameT[MAX_PATH];
|
|
UINT uiVerSize = 0;
|
|
DWORD dwVerSize = 0;
|
|
DWORD dwHandle = 0;
|
|
BYTE *pbBuffer = NULL;
|
|
HANDLE hFile;
|
|
FILETIME ftLastAccess;
|
|
BOOL bRunOnNT5 = FALSE;
|
|
OSVERSIONINFO osvi;
|
|
VS_FIXEDFILEINFO *lpVSInfo = NULL;
|
|
|
|
if ( szFile[0] == '\0' &&
|
|
RegOpenKeyEx( hkeyCLSID, szCLSID, 0, KEY_READ, &hkeyCOM ) == ERROR_SUCCESS )
|
|
{
|
|
LONG lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, INPROCSERVER, szNameT, &lcb );
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, INPROCSERVER32, szNameT, &lcb );
|
|
}
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, INPROCSERVERX86, szNameT, &lcb );
|
|
}
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, LOCALSERVER, szNameT, &lcb );
|
|
}
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, LOCALSERVER32, szNameT, &lcb );
|
|
}
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, LOCALSERVERX86, szNameT, &lcb );
|
|
}
|
|
|
|
RegCloseKey( hkeyCOM );
|
|
}
|
|
else
|
|
lstrcpyn( szNameT, szFile, ARRAYSIZE(szNameT));
|
|
|
|
if ( lResult != ERROR_SUCCESS ) // needed to find file path but couldn't
|
|
szNameT[0] = '\0';
|
|
|
|
hr = CCacheLegacyControl::Init( hkeyCLSID, szNameT, szCLSID );
|
|
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
|
|
lResult = RegOpenKeyEx(hkeyDist, szDU, 0, KEY_READ, &hkeyDU);
|
|
if (lResult != ERROR_SUCCESS)
|
|
return E_FAIL;
|
|
|
|
// Get CLSID
|
|
lstrcpyn(m_szCLSID, szDU, MAX_DIST_UNIT_NAME_LEN);
|
|
|
|
// Get full user type name - only override the control name if DU name is not empty
|
|
dw = sizeof(szNameT);
|
|
lResult = RegQueryValue(hkeyDU, NULL, szNameT, (LONG*)&dw);
|
|
if ( lResult == ERROR_SUCCESS && szNameT[0] != '\0' )
|
|
{
|
|
lstrcpyn( m_szName, szNameT, ARRAYSIZE(m_szName) );
|
|
}
|
|
else if ( *m_szName == '\0' ) // worst case, if we still don't have a name, a GUID will suffice
|
|
lstrcpyn( m_szName, szDU, ARRAYSIZE(m_szName) );
|
|
|
|
// Get type lib id
|
|
// Get type lib id
|
|
TCHAR szTypeLibValName[MAX_PATH];
|
|
CatPathStrN(szTypeLibValName, m_szCLSID, HKCR_TYPELIB, ARRAYSIZE(szTypeLibValName));
|
|
dw = sizeof(m_szTypeLibID);
|
|
lResult = RegQueryValue( hkeyCLSID, szTypeLibValName, m_szTypeLibID, (LONG*)&dw);
|
|
if (lResult != ERROR_SUCCESS)
|
|
(m_szTypeLibID)[0] = TEXT('\0');
|
|
|
|
m_szCodeBase[0] ='\0';
|
|
lResult = RegOpenKeyEx(hkeyDU, REGSTR_DOWNLOAD_INFORMATION, 0, KEY_READ, &hkeyDLInfo);
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
dw = sizeof(m_szCodeBase);
|
|
HRESULT hrErr = RegQueryValueEx(hkeyDLInfo, REGSTR_DLINFO_CODEBASE, NULL, NULL,
|
|
(unsigned char *)m_szCodeBase, &dw);
|
|
RegCloseKey( hkeyDLInfo );
|
|
}
|
|
|
|
// Get Version from DU branch
|
|
|
|
m_szVersion[0] ='\0';
|
|
lResult = RegOpenKeyEx(hkeyDU, REGSTR_INSTALLED_VERSION, 0,
|
|
KEY_READ, &hkeyVers);
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
dw = sizeof(m_szVersion);
|
|
RegQueryValueEx(hkeyVers, NULL, NULL, NULL, (LPBYTE)m_szVersion, &dw);
|
|
RegCloseKey(hkeyVers);
|
|
}
|
|
|
|
// The version specified in the COM branch is the definitive word on
|
|
// what the version is. If a key exists in the COM branch, use the version
|
|
// that is found inside the InProcServer/LocalServer.
|
|
|
|
if (RegOpenKeyEx( hkeyCLSID, szCLSID, 0, KEY_READ, &hkeyCOM ) == ERROR_SUCCESS)
|
|
{
|
|
LONG lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, INPROCSERVER32, szNameT, &lcb );
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, INPROCSERVER, szNameT, &lcb );
|
|
}
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, INPROCSERVERX86, szNameT, &lcb );
|
|
}
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, LOCALSERVER32, szNameT, &lcb );
|
|
}
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, LOCALSERVER, szNameT, &lcb );
|
|
}
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
lcb = sizeof(szNameT);
|
|
lResult = RegQueryValue( hkeyCOM, LOCALSERVERX86, szNameT, &lcb );
|
|
}
|
|
|
|
RegCloseKey( hkeyCOM );
|
|
|
|
// HACK! GetFileVersionInfoSize and GetFileVersionInfo modify
|
|
// the last access time of the file under NT5! This causes us
|
|
// to retrieve the wrong last access time when removing expired
|
|
// controls. This hack gets the last access time before the
|
|
// GetFileVersionInfo calls, and sets it back afterwards.
|
|
// See IE5 RAID #56927 for details. This code should be removed
|
|
// when NT5 fixes this bug.
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
GetVersionEx(&osvi);
|
|
|
|
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion == 5) {
|
|
bRunOnNT5 = TRUE;
|
|
}
|
|
|
|
if (bRunOnNT5) {
|
|
hFile = CreateFile(szNameT, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
GetFileTime(hFile, NULL, &ftLastAccess, NULL);
|
|
CloseHandle(hFile);
|
|
}
|
|
}
|
|
|
|
dwVerSize = GetFileVersionInfoSize((char *)szNameT, &dwHandle);
|
|
pbBuffer = new BYTE[dwVerSize];
|
|
if (!pbBuffer)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if (GetFileVersionInfo((char *)szNameT, 0, dwVerSize, pbBuffer))
|
|
{
|
|
if (VerQueryValue(pbBuffer, "\\", (void **)&lpVSInfo, &uiVerSize))
|
|
{
|
|
wsprintf(m_szVersion, "%d,%d,%d,%d", (lpVSInfo->dwFileVersionMS >> 16) & 0xFFFF
|
|
, lpVSInfo->dwFileVersionMS & 0xFFFF
|
|
, (lpVSInfo->dwFileVersionLS >> 16) & 0xFFFF
|
|
, lpVSInfo->dwFileVersionLS & 0xFFFF);
|
|
}
|
|
}
|
|
|
|
delete [] pbBuffer;
|
|
|
|
if (bRunOnNT5) {
|
|
hFile = CreateFile(szNameT, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
SetFileTime(hFile, NULL, &ftLastAccess, NULL);
|
|
CloseHandle(hFile);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
RegCloseKey( hkeyDU );
|
|
|
|
return DoParseDU( m_szFile, m_szCLSID);
|
|
}
|
|
|
|
HRESULT MakeCacheItemFromControlList( HKEY hkeyClass, // HKCR\CLSID
|
|
HKEY hkeyDist, // HKLM\SOFTWARE\MICROSOFT\Code Store Database\Distribution Units
|
|
LPCLSIDLIST_ITEM pcli,
|
|
CCacheItem **ppci )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
*ppci = NULL;
|
|
if ( pcli->bIsDistUnit )
|
|
{
|
|
CCacheDistUnit *pcdu = new CCacheDistUnit();
|
|
if ( pcdu != NULL &&
|
|
SUCCEEDED(hr = pcdu->Init( hkeyClass,
|
|
pcli->szFile,
|
|
pcli->szCLSID,
|
|
hkeyDist,
|
|
pcli->szCLSID)) )
|
|
*ppci = pcdu;
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
CCacheLegacyControl *pclc = new CCacheLegacyControl();
|
|
if ( pclc != NULL &&
|
|
SUCCEEDED(hr = pclc->Init( hkeyClass,
|
|
pcli->szFile,
|
|
pcli->szCLSID )) )
|
|
*ppci = pclc;
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
OCCFindData::OCCFindData() : m_pcliHead(NULL), m_pcliTail(NULL)
|
|
{
|
|
LONG lResult;
|
|
HKEY hkeyCacheList;
|
|
|
|
for ( int i = 0; i < cCachePathsMax; i++ )
|
|
{
|
|
m_aCachePath[i].m_cch = 0;
|
|
m_aCachePath[i].m_sz[0] = '\0';
|
|
}
|
|
|
|
// Unhook occache as a shell extension for the cache folders.
|
|
lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
REG_PATH_IE_CACHE_LIST,
|
|
0x0,
|
|
KEY_READ,
|
|
&hkeyCacheList );
|
|
|
|
if ( lResult == ERROR_SUCCESS ) {
|
|
DWORD dwIndex;
|
|
TCHAR szName[MAX_PATH];
|
|
DWORD cbName;
|
|
DWORD cbValue;
|
|
|
|
for ( dwIndex = 0, cbName = sizeof(szName), cbValue = MAX_PATH * sizeof(TCHAR);
|
|
dwIndex < cCachePathsMax;
|
|
dwIndex++, cbName = sizeof(szName), cbValue = MAX_PATH * sizeof(TCHAR) )
|
|
{
|
|
lResult = RegEnumValue( hkeyCacheList, dwIndex,
|
|
szName, &cbName,
|
|
NULL, NULL,
|
|
(LPBYTE)m_aCachePath[dwIndex].m_sz, &cbValue );
|
|
m_aCachePath[dwIndex].m_cch = lstrlen( m_aCachePath[dwIndex].m_sz );
|
|
}
|
|
// We leave this key in place because it is the only record we have of the
|
|
// cache folders and would be useful to future installations of IE
|
|
RegCloseKey( hkeyCacheList );
|
|
}
|
|
}
|
|
|
|
OCCFindData::~OCCFindData()
|
|
{
|
|
if ( m_pcliHead )
|
|
RemoveList(m_pcliHead);
|
|
}
|
|
|
|
BOOL OCCFindData::IsCachePath( LPCTSTR szPath )
|
|
{
|
|
BOOL fMatch = FALSE;
|
|
|
|
for ( int i = 0; i < cCachePathsMax && !fMatch; i++ )
|
|
fMatch = m_aCachePath[i].m_cch != 0 &&
|
|
LStrNICmp( szPath, m_aCachePath[i].m_sz, m_aCachePath[i].m_cch ) == 0;
|
|
return fMatch;
|
|
}
|
|
|
|
HRESULT OCCFindData::AddListItem( LPCTSTR szFile, LPCTSTR szCLSID, DWORD dwIsDistUnit )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if ( m_pcliTail == NULL )
|
|
{
|
|
m_pcliTail = new CLSIDLIST_ITEM;
|
|
if (m_pcliHead == NULL)
|
|
m_pcliHead = m_pcliTail;
|
|
}
|
|
else
|
|
{
|
|
m_pcliTail->pNext = new CLSIDLIST_ITEM;
|
|
m_pcliTail = m_pcliTail->pNext;
|
|
}
|
|
|
|
if ( m_pcliTail != NULL )
|
|
{
|
|
m_pcliTail->pNext = NULL;
|
|
lstrcpyn(m_pcliTail->szFile, szFile, MAX_PATH);
|
|
lstrcpyn(m_pcliTail->szCLSID, szCLSID, MAX_DIST_UNIT_NAME_LEN);
|
|
m_pcliTail->bIsDistUnit = dwIsDistUnit;
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
return hr;
|
|
}
|
|
|
|
LPCLSIDLIST_ITEM OCCFindData::TakeFirstItem(void)
|
|
{
|
|
LPCLSIDLIST_ITEM pcli = m_pcliHead;
|
|
|
|
if (m_pcliHead != NULL)
|
|
{
|
|
m_pcliHead = m_pcliHead;
|
|
m_pcliHead = m_pcliHead->pNext;
|
|
if ( m_pcliHead == NULL )
|
|
m_pcliTail = NULL;
|
|
}
|
|
|
|
return pcli;
|
|
}
|
|
|
|
BOOL IsDUDisplayable(HKEY hkeyDU)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
if (hkeyDU)
|
|
{
|
|
if (IsShowAllFilesEnabled())
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwType = 0, dwSystem = 0, dwSize = sizeof(dwSystem);
|
|
long lResult = RegQueryValueEx(hkeyDU, VALUE_SYSTEM, NULL, &dwType, (LPBYTE)&dwSystem, &dwSize);
|
|
bRet = (lResult == ERROR_SUCCESS && dwSystem == TRUE) ? (FALSE) : (TRUE);
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
BOOL IsShowAllFilesEnabled()
|
|
{
|
|
HKEY hkey = 0;
|
|
BOOL bRet = FALSE;
|
|
DWORD dwShowAll = 0;
|
|
|
|
DWORD lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_READ, &hkey);
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwType, dwSize = sizeof(dwShowAll);
|
|
lResult = RegQueryValueEx(hkey, REGSTR_SHOW_ALL_FILES, NULL, &dwType, (LPBYTE)&dwShowAll, &dwSize);
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
bRet = (dwShowAll != 0);
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
void ToggleShowAllFiles()
|
|
{
|
|
DWORD dwShowAll = !IsShowAllFilesEnabled();
|
|
HKEY hkey = 0;
|
|
DWORD lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_ALL_ACCESS, &hkey);
|
|
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
RegSetValueEx(hkey, REGSTR_SHOW_ALL_FILES, 0, REG_DWORD, (CONST BYTE *)&dwShowAll, sizeof(dwShowAll));
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
|
|
LONG WINAPI FindFirstControl(HANDLE& hFindHandle, HANDLE& hControlHandle, LPCTSTR pszCachePath)
|
|
{
|
|
LONG lResult = ERROR_SUCCESS;
|
|
HRESULT hr = S_OK;
|
|
DWORD dw = 0;
|
|
HKEY hKeyClass = NULL;
|
|
HKEY hKeyMod = NULL;
|
|
HKEY hKeyDist = NULL;
|
|
TCHAR szT[MAX_PATH]; // scratch buffer
|
|
int cEnum = 0;
|
|
CCacheItem *pci = NULL;
|
|
LPCLSIDLIST_ITEM pcli = NULL;
|
|
TCHAR szDUName[MAX_DIST_UNIT_NAME_LEN];
|
|
|
|
OCCFindData *poccfd = new OCCFindData();
|
|
if ( poccfd == NULL )
|
|
{
|
|
lResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto EXIT_FINDFIRSTCONTROL;
|
|
}
|
|
|
|
// Open up the HKCR\CLSID key.
|
|
lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, HKCR_CLSID, 0, KEY_READ, &hKeyClass);
|
|
if (ERROR_SUCCESS != lResult)
|
|
goto EXIT_FINDFIRSTCONTROL;
|
|
|
|
// Search for legacy controls found in the COM branch
|
|
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_MODULE_USAGE, 0, KEY_READ, &hKeyMod);
|
|
if (ERROR_SUCCESS != lResult)
|
|
goto EXIT_FINDFIRSTCONTROL;
|
|
|
|
// Enumerate the known modules and build up a list of the owners.
|
|
// This is a search for legacy controls.
|
|
while ((lResult = RegEnumKey(hKeyMod, cEnum++, szT, ARRAYSIZE(szT))) == ERROR_SUCCESS)
|
|
{
|
|
TCHAR szClient[MAX_CLIENT_LEN];
|
|
HKEY hKeyClsid = NULL;
|
|
HKEY hkeyMUEntry = NULL;
|
|
|
|
lResult = RegOpenKeyEx( hKeyMod, szT, 0, KEY_READ, &hkeyMUEntry );
|
|
if (ERROR_SUCCESS != lResult)
|
|
continue;
|
|
|
|
// Fetch the module owner.
|
|
// If the module owner is in the COM branch AND
|
|
// ( the owner lives in the cache OR it has an INF in the cache )
|
|
// Then add the _owner_ to our list of legacy controls.
|
|
// In the INF case, we may be looking at a control that was re-registered
|
|
// outside of the cache.
|
|
// If it doesn't have these properties, then it is either a DU module or
|
|
// was installed by something other than MSICD. In either case, we'll skip it
|
|
// at least for now.
|
|
dw = sizeof(szClient);
|
|
lResult = RegQueryValueEx(hkeyMUEntry, VALUE_OWNER, NULL, NULL, (LPBYTE)szClient, &dw);
|
|
if (ERROR_SUCCESS != lResult)
|
|
continue;
|
|
|
|
lResult = RegOpenKeyEx(hKeyClass, szClient, 0, KEY_READ, &hKeyClsid);
|
|
if (ERROR_SUCCESS == lResult)
|
|
{
|
|
TCHAR szCLocation[MAX_PATH]; // Canonical path of control
|
|
TCHAR szLocation[MAX_PATH]; // Location in COM CLSID reg tree.
|
|
|
|
// Look for InprocServer[32] or LocalServer[32] key
|
|
dw = sizeof(szLocation);
|
|
lResult = RegQueryValue(hKeyClsid, INPROCSERVER32, szLocation, (PLONG)&dw);
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
dw = sizeof(szLocation);
|
|
lResult = RegQueryValue(hKeyClsid, LOCALSERVER32, szLocation, (PLONG)&dw);
|
|
}
|
|
|
|
RegCloseKey(hKeyClsid);
|
|
hKeyClsid = NULL;
|
|
|
|
if ( lResult == ERROR_SUCCESS )
|
|
{
|
|
BOOL bAddOwner;
|
|
|
|
// see if we've already got an entry for this one.
|
|
for ( pcli = poccfd->m_pcliHead;
|
|
pcli != NULL && lstrcmp( szClient, pcli->szCLSID ) != 0;
|
|
pcli = pcli->pNext );
|
|
|
|
if ( pcli == NULL ) // not found - possibly add new item
|
|
{
|
|
// Canonicalize the path for use in comparisons with cache dirs
|
|
if ( OCCGetLongPathName(szCLocation, szLocation, MAX_PATH) == 0 )
|
|
lstrcpyn( szCLocation, szLocation, MAX_PATH );
|
|
|
|
// Is the owner in our cache?
|
|
bAddOwner = poccfd->IsCachePath( szCLocation );
|
|
|
|
if ( !bAddOwner )
|
|
{
|
|
// does it have an INF in our cache(s)?
|
|
// We'll appropriate szDCachePath
|
|
for ( int i = 0; i < cCachePathsMax && !bAddOwner; i++ )
|
|
{
|
|
if ( poccfd->m_aCachePath[i].m_sz != '\0' )
|
|
{
|
|
CatPathStrN( szT, poccfd->m_aCachePath[i].m_sz, PathFindFileName( szCLocation ), MAX_PATH);
|
|
|
|
// Note if another copy of the owner exists within the cache(s).
|
|
// This would be a case of re-registration.
|
|
if ( PathFileExists( szT ) )
|
|
{
|
|
// add our version of the control.
|
|
lstrcpyn( szCLocation, szT, MAX_PATH );
|
|
bAddOwner = TRUE;
|
|
}
|
|
else
|
|
bAddOwner = PathRenameExtension( szT, INF_EXTENSION ) &&
|
|
PathFileExists( szT );
|
|
} // if cache path
|
|
} // for each cache directory
|
|
} // if check for cached INF
|
|
|
|
if ( bAddOwner )
|
|
{
|
|
HKEY hkeyDUCheck = 0;
|
|
char achBuf[MAX_REGPATH_LEN];
|
|
|
|
wnsprintfA(achBuf, MAX_REGPATH_LEN, "%s\\%s", REGSTR_PATH_DIST_UNITS, szClient);
|
|
|
|
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, achBuf, 0, KEY_READ, &hkeyDUCheck);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
// This is a legacy control with no corresponding DU
|
|
poccfd->AddListItem( szCLocation, szClient, FALSE );
|
|
}
|
|
else
|
|
{
|
|
if (IsDUDisplayable(hkeyDUCheck))
|
|
{
|
|
// Legacy control w/ DU keys that is displayable
|
|
poccfd->AddListItem( szCLocation, szClient, FALSE );
|
|
}
|
|
RegCloseKey(hkeyDUCheck);
|
|
}
|
|
}
|
|
} // if owner we haven't seen before
|
|
} // if owner has local or inproc server
|
|
} // if owner has COM entry
|
|
RegCloseKey( hkeyMUEntry );
|
|
} // while enumerating Module Usage
|
|
|
|
// we're finished with module usage
|
|
RegCloseKey(hKeyMod);
|
|
|
|
// Now search distribution units
|
|
|
|
// Check for duplicates - distribution units for controls we detected above
|
|
|
|
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_READ, &hKeyDist);
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
cEnum = 0;
|
|
// Enumerate distribution units and queue them up in the list
|
|
while ((lResult = RegEnumKey(hKeyDist, cEnum++, szDUName, ARRAYSIZE(szDUName))) == ERROR_SUCCESS)
|
|
{
|
|
// We should only display DU's installed by code download.
|
|
HKEY hkeyDU;
|
|
DWORD dwType;
|
|
|
|
lResult = RegOpenKeyEx( hKeyDist, szDUName, 0, KEY_READ, &hkeyDU );
|
|
Assert( lResult == ERROR_SUCCESS );
|
|
|
|
if ((ERROR_SUCCESS != lResult) ||
|
|
!IsDUDisplayable(hkeyDU))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
szT[0] = '\0';
|
|
DWORD cb = sizeof(szT);
|
|
lResult = RegQueryValueEx( hkeyDU, DU_INSTALLER_VALUE, NULL, &dwType, (LPBYTE)szT, &cb );
|
|
|
|
Assert( lResult == ERROR_SUCCESS ); // properly-formed DU will have this
|
|
Assert( dwType == REG_SZ ); // properly-formed DU's have a string here
|
|
|
|
// Check for an installed version. We might just have a DU that has an AvailableVersion
|
|
// but hasn't been installed yet.
|
|
lResult = RegQueryValue( hkeyDU, REGSTR_INSTALLED_VERSION, NULL, NULL );
|
|
|
|
RegCloseKey( hkeyDU );
|
|
|
|
if ( lstrcmpi( szT, CDL_INSTALLER ) == 0 &&
|
|
lResult == ERROR_SUCCESS ) // from InstalledVersion RegQueryValue
|
|
{
|
|
// If we can convert the unique name to a GUID, then this DU
|
|
// may have already been added on the first pass through the
|
|
// COM branch.
|
|
CLSID clsidDummy = CLSID_NULL;
|
|
WORD szDummyStr[MAX_CTRL_NAME_SIZE];
|
|
BOOL bFoundDuplicate = FALSE;
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, szDUName, -1, szDummyStr, ARRAYSIZE(szDummyStr));
|
|
if ((CLSIDFromString(szDummyStr, &clsidDummy) == S_OK))
|
|
{
|
|
for (pcli = poccfd->m_pcliHead; pcli; pcli = pcli->pNext)
|
|
{
|
|
if (!lstrcmpi(szDUName, pcli->szCLSID))
|
|
{
|
|
// Duplicate found. Use dist unit information to
|
|
// fill in additional fields if it is the first
|
|
// entry in the list
|
|
bFoundDuplicate = TRUE;
|
|
pcli->bIsDistUnit = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bFoundDuplicate)
|
|
{
|
|
// Okay we're looking at some sort of Java scenario. We have a distribution unit, but
|
|
// no corresponding entry in the COM branch. This generally means we've got a DU that
|
|
// consists of java packages. It can also mean that we're dealing with a java/code download
|
|
// backdoor introduced in IE3. In this case, an Object tag gets a CAB downloaded that
|
|
// installs Java classes and sets of a CLSID that invokes MSJava.dll on the class ( ESPN's
|
|
// sportszone control/applet works this way ). In the first case, we get the name
|
|
// squared-away when we parse the DU. In the latter case, we need to try and pick the name
|
|
// up from the COM branch.
|
|
hr = poccfd->AddListItem( "", szDUName, TRUE );
|
|
if ( FAILED(hr) )
|
|
{
|
|
lResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto EXIT_FINDFIRSTCONTROL;
|
|
}
|
|
} // if no duplicate - add DU to the list
|
|
} // if installed by MSICD
|
|
} // while enumerating DU's
|
|
} // if we can open the DU key.
|
|
else
|
|
lResult = ERROR_NO_MORE_ITEMS; // if no DU's then make due with our legacy controls, if any
|
|
|
|
pcli = poccfd->TakeFirstItem();
|
|
if (pcli)
|
|
{
|
|
hr = MakeCacheItemFromControlList(hKeyClass, hKeyDist, pcli, &pci);
|
|
delete pcli;
|
|
if ( FAILED(hr) )
|
|
lResult = hr;
|
|
}
|
|
|
|
if (hKeyDist)
|
|
{
|
|
RegCloseKey(hKeyDist);
|
|
hKeyDist = 0;
|
|
}
|
|
|
|
|
|
// Clean up
|
|
|
|
if (lResult != ERROR_NO_MORE_ITEMS)
|
|
goto EXIT_FINDFIRSTCONTROL;
|
|
|
|
if (pci == NULL)
|
|
lResult = ERROR_NO_MORE_ITEMS;
|
|
else
|
|
{
|
|
lResult = ERROR_SUCCESS;
|
|
}
|
|
|
|
hFindHandle = (HANDLE)poccfd;
|
|
hControlHandle = (HANDLE)pci;
|
|
|
|
EXIT_FINDFIRSTCONTROL:
|
|
|
|
if (hKeyDist)
|
|
RegCloseKey(hKeyDist);
|
|
|
|
if (hKeyClass)
|
|
RegCloseKey(hKeyClass);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
if ( pci != NULL )
|
|
delete pci;
|
|
if ( poccfd != NULL )
|
|
delete poccfd;
|
|
hFindHandle = INVALID_HANDLE_VALUE;
|
|
hControlHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return lResult;
|
|
}
|
|
|
|
LONG WINAPI FindNextControl(HANDLE& hFindHandle, HANDLE& hControlHandle)
|
|
{
|
|
LONG lResult = ERROR_SUCCESS;
|
|
HRESULT hr = S_OK;
|
|
HKEY hKeyClass = NULL;
|
|
|
|
CCacheItem *pci = NULL;
|
|
OCCFindData *poccfd = (OCCFindData *)hFindHandle;
|
|
|
|
LPCLSIDLIST_ITEM pcli = poccfd->TakeFirstItem();
|
|
hControlHandle = INVALID_HANDLE_VALUE;
|
|
|
|
if (pcli == NULL)
|
|
{
|
|
lResult = ERROR_NO_MORE_ITEMS;
|
|
goto EXIT_FINDNEXTCONTROL;
|
|
}
|
|
|
|
if ((lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, HKCR_CLSID, 0, KEY_READ, &hKeyClass)) != ERROR_SUCCESS)
|
|
goto EXIT_FINDNEXTCONTROL;
|
|
|
|
if ( pcli->bIsDistUnit )
|
|
{
|
|
HKEY hKeyDist;
|
|
|
|
lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0,
|
|
KEY_READ, &hKeyDist);
|
|
|
|
if ( lResult == ERROR_SUCCESS )
|
|
{
|
|
hr = MakeCacheItemFromControlList( hKeyClass,
|
|
hKeyDist,
|
|
pcli,
|
|
&pci );
|
|
if ( FAILED(hr) )
|
|
lResult = hr;
|
|
|
|
RegCloseKey( hKeyDist );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is not a distribution unit. Fill in CCachItem information
|
|
// from the COM branch.
|
|
hr = MakeCacheItemFromControlList(hKeyClass, NULL, pcli, &pci );
|
|
if ( FAILED(hr) )
|
|
lResult = hr;
|
|
}
|
|
|
|
hControlHandle = (HANDLE)pci;
|
|
|
|
EXIT_FINDNEXTCONTROL:
|
|
|
|
if (hKeyClass)
|
|
RegCloseKey(hKeyClass);
|
|
|
|
if (pcli != NULL)
|
|
{
|
|
delete pcli;
|
|
}
|
|
|
|
return lResult;
|
|
}
|
|
|
|
void WINAPI FindControlClose(HANDLE hFindHandle)
|
|
{
|
|
if (hFindHandle == INVALID_HANDLE_VALUE ||
|
|
hFindHandle == (HANDLE)0)
|
|
return;
|
|
|
|
delete (OCCFindData*)hFindHandle;
|
|
}
|
|
|
|
void WINAPI ReleaseControlHandle(HANDLE hControlHandle)
|
|
{
|
|
if (hControlHandle == INVALID_HANDLE_VALUE ||
|
|
hControlHandle == (HANDLE)0)
|
|
return;
|
|
|
|
delete (CCacheItem *)hControlHandle;
|
|
}
|
|
|
|
HRESULT WINAPI RemoveControlByHandle(HANDLE hControlHandle, BOOL bForceRemove /* = FALSE */)
|
|
{
|
|
return RemoveControlByHandle2( hControlHandle, bForceRemove, FALSE );
|
|
}
|
|
|
|
|
|
HRESULT WINAPI RemoveControlByName(LPCTSTR lpszFile, LPCTSTR lpszCLSID, LPCTSTR lpszTypeLibID, BOOL bForceRemove, /* = FALSE */ DWORD dwIsDistUnit /* = FALSE */)
|
|
{
|
|
return RemoveControlByName2( lpszFile, lpszCLSID, lpszTypeLibID, bForceRemove, dwIsDistUnit, FALSE);
|
|
}
|
|
|
|
LONG WINAPI GetControlDependentFile(int iFile, HANDLE hControlHandle, LPTSTR lpszFile, LPDWORD lpdwSize, BOOL bToUpper /* = FALSE */)
|
|
{
|
|
CCacheItem *pci = (CCacheItem *)hControlHandle;
|
|
|
|
if (iFile < 0 || lpszFile == NULL || lpdwSize == NULL)
|
|
return ERROR_BAD_ARGUMENTS;
|
|
|
|
// loop through the list of files to find the one indicated
|
|
// by the given index.
|
|
// this way is dumb but since a control does not depend on
|
|
// too many files, it's ok
|
|
CFileNode *pFileNode = pci->GetFirstFile();
|
|
for (int i = 0; i < iFile && pFileNode != NULL; i++)
|
|
pFileNode = pci->GetNextFile();
|
|
|
|
if (pFileNode == NULL)
|
|
{
|
|
lpszFile[0] = TEXT('\0');
|
|
lpdwSize = 0;
|
|
return ERROR_NO_MORE_FILES;
|
|
}
|
|
|
|
// Make a fully qualified filename
|
|
if (pFileNode->GetPath() != NULL)
|
|
{
|
|
CatPathStrN( lpszFile, pFileNode->GetPath(), pFileNode->GetName(), MAX_PATH);
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(lpszFile, pFileNode->GetName());
|
|
}
|
|
|
|
if (FAILED(GetSizeOfFile(lpszFile, lpdwSize)))
|
|
*lpdwSize = 0;
|
|
|
|
// to upper case if required
|
|
if (bToUpper)
|
|
CharUpper(lpszFile);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// determine if a control or one of its associated files can be removed
|
|
// by reading its SharedDlls count
|
|
BOOL WINAPI IsModuleRemovable(LPCTSTR lpszFile)
|
|
{
|
|
TCHAR szFile[MAX_PATH];
|
|
TCHAR szT[MAX_PATH];
|
|
|
|
if (lpszFile == NULL)
|
|
return FALSE;
|
|
|
|
if ( OCCGetLongPathName(szFile, lpszFile, MAX_PATH) == 0 )
|
|
lstrcpyn( szFile, lpszFile, MAX_PATH );
|
|
|
|
// Don't ever pull something out of the system directory.
|
|
// This is a "safe" course of action because it is not reasonable
|
|
// to expect the user to judge whether yanking this file damage other
|
|
// software installations or the system itself.
|
|
GetSystemDirectory(szT, MAX_PATH);
|
|
if (StrStrI(szFile, szT))
|
|
return FALSE;
|
|
|
|
// check moduleusage if a control is safe to remove
|
|
if (LookUpModuleUsage(szFile, NULL, szT, MAX_PATH) != S_OK)
|
|
return FALSE;
|
|
|
|
// if we don't know who the owner of the module is, it's not
|
|
// safe to remove
|
|
if (lstrcmpi(szT, UNKNOWNOWNER) == 0)
|
|
return FALSE;
|
|
else
|
|
{
|
|
// check shareddlls if a control is safe to remove
|
|
LONG cRef;
|
|
|
|
HRESULT hr = SetSharedDllsCount( szFile, -1, &cRef );
|
|
|
|
return cRef == 1;
|
|
}
|
|
}
|
|
|
|
BOOL WINAPI GetControlInfo(HANDLE hControlHandle, UINT nFlag,
|
|
DWORD *pdwData, LPTSTR pszData, int nBufLen)
|
|
{
|
|
if (hControlHandle == 0 || hControlHandle == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
BOOL bResult = TRUE;
|
|
LPCTSTR pStr = NULL;
|
|
DWORD dw = 0;
|
|
|
|
switch (nFlag)
|
|
{
|
|
case GCI_NAME: // get friend name of control
|
|
pStr = ((CCacheItem *)hControlHandle)->m_szName;
|
|
break;
|
|
|
|
case GCI_FILE: // get filename of control (with full path)
|
|
pStr = ((CCacheItem *)hControlHandle)->m_szFile;
|
|
// if there is no file, but there is a package list, fake it
|
|
// with the path to the first package's ZIP file.
|
|
if ( *pStr == '\0' )
|
|
{
|
|
CPNode *ppn = ((CCacheItem *)hControlHandle)->GetFirstPackage();
|
|
if (ppn)
|
|
{
|
|
pStr = ppn->GetPath();
|
|
if (!pStr)
|
|
{
|
|
return FALSE; // this means hControlHandle is an invalid arg
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pStr && *pStr == TEXT('\0') )
|
|
{
|
|
CPNode *pfn = ((CCacheItem *)hControlHandle)->GetFirstFile();
|
|
if ( pfn != NULL )
|
|
pStr = pfn->GetPath();
|
|
}
|
|
break;
|
|
|
|
case GCI_DIST_UNIT_VERSION:
|
|
pStr = ((CCacheItem *)hControlHandle)->m_szVersion;
|
|
break;
|
|
|
|
case GCI_CLSID: // get CLSID of control
|
|
pStr = ((CCacheItem *)hControlHandle)->m_szCLSID;
|
|
break;
|
|
|
|
case GCI_TYPELIBID: // get TYPELIB id of control
|
|
pStr = ((CCacheItem *)hControlHandle)->m_szTypeLibID;
|
|
break;
|
|
|
|
case GCI_TOTALSIZE: // get total size in bytes
|
|
dw = ((CCacheItem *)hControlHandle)->GetTotalFileSize();
|
|
break;
|
|
|
|
case GCI_SIZESAVED: // get total size restored if control is removed
|
|
dw = ((CCacheItem *)hControlHandle)->GetTotalSizeSaved();
|
|
break;
|
|
|
|
case GCI_TOTALFILES: // get total number of files related to control
|
|
dw = (DWORD)(((CCacheItem *)hControlHandle)->GetTotalFiles());
|
|
break;
|
|
|
|
case GCI_CODEBASE: // get CodeBase for control
|
|
pStr = ((CCacheItem *)hControlHandle)->m_szCodeBase;
|
|
break;
|
|
|
|
case GCI_ISDISTUNIT:
|
|
dw = ((CCacheItem *)hControlHandle)->ItemType() == CCacheDistUnit::s_dwType;
|
|
break;
|
|
|
|
case GCI_STATUS:
|
|
dw = ((CCacheItem *)hControlHandle)->GetStatus();
|
|
break;
|
|
|
|
case GCI_HAS_ACTIVEX:
|
|
dw = ((CCacheItem *)hControlHandle)->GetHasActiveX();
|
|
break;
|
|
|
|
case GCI_HAS_JAVA:
|
|
dw = ((CCacheItem *)hControlHandle)->GetHasJava();
|
|
break;
|
|
}
|
|
|
|
if (nFlag == GCI_TOTALSIZE ||
|
|
nFlag == GCI_SIZESAVED ||
|
|
nFlag == GCI_TOTALFILES ||
|
|
nFlag == GCI_ISDISTUNIT ||
|
|
nFlag == GCI_STATUS ||
|
|
nFlag == GCI_HAS_ACTIVEX ||
|
|
nFlag == GCI_HAS_JAVA)
|
|
{
|
|
bResult = pdwData != NULL;
|
|
if (bResult)
|
|
*pdwData = dw;
|
|
}
|
|
else
|
|
{
|
|
bResult = pszData && pStr;
|
|
if (bResult)
|
|
lstrcpyn(pszData, pStr, nBufLen);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// API to be called by Advpack.dll
|
|
|
|
// Define list node to be used in a linked list of control
|
|
struct tagHANDLENODE;
|
|
typedef struct tagHANDLENODE HANDLENODE;
|
|
typedef HANDLENODE* LPHANDLENODE;
|
|
struct tagHANDLENODE
|
|
{
|
|
HANDLE hControl;
|
|
struct tagHANDLENODE* pNext;
|
|
};
|
|
|
|
// Given a handle to a control, get the control's last access time
|
|
// Result is stored in a FILETIME struct
|
|
HRESULT GetLastAccessTime(HANDLE hControl, FILETIME *pLastAccess)
|
|
{
|
|
Assert(hControl != NULL && hControl != INVALID_HANDLE_VALUE);
|
|
Assert(pLastAccess != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
WIN32_FIND_DATA fdata;
|
|
HANDLE h = INVALID_HANDLE_VALUE;
|
|
LPCTSTR lpszFile = NULL;
|
|
CCacheItem *pci = (CCacheItem *)hControl;
|
|
CPNode *ppn;
|
|
|
|
if (pci->m_szFile[0] != 0)
|
|
lpszFile = pci->m_szFile;
|
|
else if ( (ppn = pci->GetFirstPackage()) != NULL )
|
|
lpszFile = ppn->GetPath();
|
|
else if ( (ppn = pci->GetFirstFile()) != NULL )
|
|
lpszFile = ppn->GetPath();
|
|
|
|
if ( lpszFile )
|
|
h = FindFirstFile(lpszFile, &fdata);
|
|
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
{
|
|
SYSTEMTIME stNow;
|
|
GetLocalTime(&stNow);
|
|
SystemTimeToFileTime(&stNow, pLastAccess);
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
// Convert file time to local file time, then file time to
|
|
// system time. Set those fields to be ignored to 0, and
|
|
// set system time back to file time.
|
|
// FILETIME struct is used because API for time comparison
|
|
// only works on FILETIME.
|
|
|
|
// SYSTEMTIME sysTime;
|
|
|
|
FindClose(h);
|
|
FileTimeToLocalFileTime(&(fdata.ftLastAccessTime), pLastAccess);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI SweepControlsByLastAccessDate(
|
|
SYSTEMTIME *pLastAccessTime /* = NULL */,
|
|
PFNDOBEFOREREMOVAL pfnDoBefore /* = NULL */,
|
|
PFNDOAFTERREMOVAL pfnDoAfter /* = NULL */,
|
|
DWORD dwSizeLimit /* = 0 */
|
|
)
|
|
{
|
|
LONG lResult = ERROR_SUCCESS;
|
|
HRESULT hr = S_FALSE;
|
|
DWORD dwSize = 0, dwTotalSize = 0;
|
|
HANDLE hFind = NULL, hControl = NULL;
|
|
LPHANDLENODE pHead = NULL, pCur = NULL;
|
|
FILETIME timeLastAccess, timeRemovePrior;
|
|
UINT cCnt = 0;
|
|
TCHAR szFile[MAX_PATH];
|
|
|
|
// ignore all fields except wYear, wMonth and wDay
|
|
if (pLastAccessTime != NULL)
|
|
{
|
|
pLastAccessTime->wDayOfWeek = 0;
|
|
pLastAccessTime->wHour = 0;
|
|
pLastAccessTime->wMinute = 0;
|
|
pLastAccessTime->wSecond = 0;
|
|
pLastAccessTime->wMilliseconds = 0;
|
|
}
|
|
|
|
// loop through all controls and put in a list the
|
|
// ones that are accessed before the given date and
|
|
// are safe to uninstall
|
|
lResult = FindFirstControl(hFind, hControl);
|
|
for (;lResult == ERROR_SUCCESS;
|
|
lResult = FindNextControl(hFind, hControl))
|
|
{
|
|
// check last access time
|
|
if (pLastAccessTime != NULL)
|
|
{
|
|
GetLastAccessTime(hControl, &timeLastAccess);
|
|
SystemTimeToFileTime(pLastAccessTime, &timeRemovePrior);
|
|
if (CompareFileTime(&timeLastAccess, &timeRemovePrior) > 0)
|
|
{
|
|
ReleaseControlHandle(hControl);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// check if control is safe to remove
|
|
GetControlInfo(hControl, GCI_FILE, NULL, szFile, MAX_PATH);
|
|
if (!IsModuleRemovable(szFile))
|
|
{
|
|
ReleaseControlHandle(hControl);
|
|
continue;
|
|
}
|
|
|
|
// put control in a list
|
|
if (pHead == NULL)
|
|
{
|
|
pHead = new HANDLENODE;
|
|
pCur = pHead;
|
|
}
|
|
else
|
|
{
|
|
pCur->pNext = new HANDLENODE;
|
|
pCur = pCur->pNext;
|
|
}
|
|
|
|
if (pCur == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto EXIT_REMOVECONTROLBYLASTACCESSDATE;
|
|
}
|
|
|
|
pCur->pNext = NULL;
|
|
pCur->hControl = hControl;
|
|
cCnt += 1;
|
|
|
|
// calculate total size
|
|
GetControlInfo(pCur->hControl, GCI_SIZESAVED, &dwSize, NULL, NULL);
|
|
dwTotalSize += dwSize;
|
|
}
|
|
|
|
// quit if total size restored is less than the given amount
|
|
if (dwTotalSize < dwSizeLimit)
|
|
goto EXIT_REMOVECONTROLBYLASTACCESSDATE;
|
|
|
|
// traverse the list and remove each control
|
|
for (pCur = pHead; pCur != NULL; cCnt--)
|
|
{
|
|
hr = S_OK;
|
|
pHead = pHead->pNext;
|
|
|
|
// call callback function before removing a control
|
|
if (pfnDoBefore == NULL || SUCCEEDED(pfnDoBefore(pCur->hControl, cCnt)))
|
|
{
|
|
hr = RemoveControlByHandle(pCur->hControl);
|
|
|
|
// call callback function after removing a control, passing it the
|
|
// result of the removal
|
|
if (pfnDoAfter != NULL && FAILED(pfnDoAfter(hr, cCnt - 1)))
|
|
{
|
|
pHead = pCur; // set pHead back to head of list
|
|
goto EXIT_REMOVECONTROLBYLASTACCESSDATE;
|
|
}
|
|
}
|
|
|
|
// release memory used by the control handle
|
|
ReleaseControlHandle(pCur->hControl);
|
|
delete pCur;
|
|
pCur = pHead;
|
|
}
|
|
|
|
EXIT_REMOVECONTROLBYLASTACCESSDATE:
|
|
|
|
FindControlClose(hFind);
|
|
|
|
// release memory taken up by the list
|
|
for (pCur = pHead; pCur != NULL; pCur = pHead)
|
|
{
|
|
pHead = pHead->pNext;
|
|
ReleaseControlHandle(pCur->hControl);
|
|
delete pCur;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI RemoveExpiredControls(DWORD dwFlags, DWORD dwReserved)
|
|
{
|
|
LONG lResult = ERROR_SUCCESS;
|
|
HRESULT hr = S_FALSE;
|
|
HANDLE hFind = NULL, hControl = NULL;
|
|
LPHANDLENODE pHead = NULL, pCur = NULL;
|
|
FILETIME ftNow, ftMinLastAccess, ftLastAccess;
|
|
LARGE_INTEGER liMinLastAccess;
|
|
SYSTEMTIME stNow;
|
|
UINT cCnt = 0;
|
|
|
|
GetLocalTime( &stNow );
|
|
SystemTimeToFileTime(&stNow, &ftNow);
|
|
|
|
// loop through all controls and put in a list the
|
|
// ones that are accessed before the given date and
|
|
// are safe to uninstall
|
|
lResult = FindFirstControl(hFind, hControl);
|
|
for (;lResult == ERROR_SUCCESS;
|
|
lResult = FindNextControl(hFind, hControl))
|
|
{
|
|
CCacheItem *pci = (CCacheItem *)hControl;
|
|
|
|
// Controls must have a last access time of at least ftMinLastAccess or they will
|
|
// expire by default. If they have the Office Auto-expire set, then they may
|
|
// have to pass a higher bar.
|
|
|
|
liMinLastAccess.LowPart = ftNow.dwLowDateTime;
|
|
liMinLastAccess.HighPart = ftNow.dwHighDateTime;
|
|
// We add one to GetExpireDays to deal with bug 17151. The last access time
|
|
// returned by the file system is truncated down to 12AM, so we need to
|
|
// expand the expire interval to ensure that this truncation does not cause
|
|
// the control to expire prematurely.
|
|
liMinLastAccess.QuadPart -= ((pci->GetExpireDays()+1) * 864000000000L); //24*3600*10^7
|
|
ftMinLastAccess.dwLowDateTime = liMinLastAccess.LowPart;
|
|
ftMinLastAccess.dwHighDateTime = liMinLastAccess.HighPart;
|
|
|
|
GetLastAccessTime(hControl, &ftLastAccess); // ftLastAccess is a local file time
|
|
|
|
if (CompareFileTime(&ftLastAccess, &ftMinLastAccess) >= 0)
|
|
{
|
|
ReleaseControlHandle(hControl);
|
|
continue;
|
|
}
|
|
|
|
|
|
// put control in a list
|
|
if (pHead == NULL)
|
|
{
|
|
pHead = new HANDLENODE;
|
|
pCur = pHead;
|
|
}
|
|
else
|
|
{
|
|
pCur->pNext = new HANDLENODE;
|
|
pCur = pCur->pNext;
|
|
}
|
|
|
|
if (pCur == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
pCur->pNext = NULL;
|
|
pCur->hControl = hControl;
|
|
cCnt += 1;
|
|
}
|
|
|
|
// traverse the list and remove each control
|
|
for (pCur = pHead; pCur != NULL; cCnt--)
|
|
{
|
|
hr = S_OK;
|
|
pHead = pHead->pNext;
|
|
|
|
hr = RemoveControlByHandle2(pCur->hControl, FALSE, TRUE);
|
|
|
|
// release memory used by the control handle
|
|
ReleaseControlHandle(pCur->hControl);
|
|
delete pCur;
|
|
pCur = pHead;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
FindControlClose(hFind);
|
|
|
|
// release memory taken up by the list, if any left
|
|
for (pCur = pHead; pCur != NULL; pCur = pHead)
|
|
{
|
|
pHead = pHead->pNext;
|
|
ReleaseControlHandle(pCur->hControl);
|
|
delete pCur;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|