#include "ParseInf.h" #include "general.h" #include #include //#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; LRESULT lResult; DWORD dw; lstrcpyn(m_szFile, szFile, MAX_PATH); lstrcpyn(m_szCLSID, szCLSID, MAX_DIST_UNIT_NAME_LEN); // Get full user type name m_szName[0] = '\0'; dw = LENGTH_NAME; lResult = RegQueryValue(hkeyCLSID, m_szCLSID, m_szName, (LONG*)&dw); // BUGBUG - 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, MAX_PATH); dw = MAX_CLSID_LEN; 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 = MAX_PATH; lResult = RegQueryValue(hkeyCOM, INPROCSERVER, szNameT, &lcb); if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; lResult = RegQueryValue(hkeyCOM, INPROCSERVER32, szNameT, &lcb); } if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; lResult = RegQueryValue(hkeyCOM, INPROCSERVERX86, szNameT, &lcb); } if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; lResult = RegQueryValue(hkeyCOM, LOCALSERVER, szNameT, &lcb); } if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; lResult = RegQueryValue(hkeyCOM, LOCALSERVER32, szNameT, &lcb); } if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; lResult = RegQueryValue(hkeyCOM, LOCALSERVERX86, szNameT, &lcb); } RegCloseKey(hkeyCOM); } else lstrcpyn(szNameT, szFile, MAX_PATH); 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 = MAX_PATH; lResult = RegQueryValue(hkeyDU, NULL, szNameT, (LONG*)&dw); if (lResult == ERROR_SUCCESS && szNameT[0] != '\0') { lstrcpyn(m_szName, szNameT, LENGTH_NAME); } else if (*m_szName == '\0') // worst case, if we still don't have a name, a GUID will suffice lstrcpyn(m_szName, szDU, LENGTH_NAME); // Get type lib id // Get type lib id TCHAR szTypeLibValName[MAX_PATH]; CatPathStrN(szTypeLibValName, m_szCLSID, HKCR_TYPELIB, MAX_PATH); dw = MAX_CLSID_LEN; 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 = INTERNET_MAX_URL_LENGTH; 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 = VERSION_MAXSIZE; 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 = MAX_PATH; lResult = RegQueryValue(hkeyCOM, INPROCSERVER32, szNameT, &lcb); if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; lResult = RegQueryValue(hkeyCOM, INPROCSERVER, szNameT, &lcb); } if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; lResult = RegQueryValue(hkeyCOM, INPROCSERVERX86, szNameT, &lcb); } if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; lResult = RegQueryValue(hkeyCOM, LOCALSERVER32, szNameT, &lcb); } if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; lResult = RegQueryValue(hkeyCOM, LOCALSERVER, szNameT, &lcb); } if (lResult != ERROR_SUCCESS) { lcb = MAX_PATH; 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*)szFile, 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) { DWORD dwType = 0; DWORD dwSystem = 0; DWORD dwSize = 0; long lResult = 0; BOOL bRet; if (!hkeyDU) { bRet = FALSE; goto Exit; } if (IsShowAllFilesEnabled()) { bRet = TRUE; goto Exit; } dwSize = sizeof(DWORD); lResult = RegQueryValueEx(hkeyDU, VALUE_SYSTEM, NULL, &dwType, (LPBYTE)&dwSystem, &dwSize); bRet = (lResult == ERROR_SUCCESS && dwSystem == TRUE) ? (FALSE) : (TRUE); Exit: return bRet; } BOOL IsShowAllFilesEnabled() { HKEY hkey = 0; BOOL bRet = FALSE; DWORD lResult = 0; DWORD dwType = 0; DWORD dwSize = sizeof(DWORD); DWORD dwShowAll = 0; lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_READ, &hkey); if (lResult == ERROR_SUCCESS) { lResult = RegQueryValueEx(hkey, REGSTR_SHOW_ALL_FILES, NULL, &dwType, (LPBYTE)&dwShowAll, &dwSize); } if (lResult == ERROR_SUCCESS) { bRet = (dwShowAll != 0); } if (hkey) { RegCloseKey(hkey); } return bRet; } void ToggleShowAllFiles() { HKEY hkey = 0; DWORD lResult = 0; DWORD dwShowAll = !IsShowAllFilesEnabled(); 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(DWORD)); } if (hkey) { RegCloseKey(hkey); } } LONG WINAPI FindFirstControl( HANDLE& hFindHandle, HANDLE& hControlHandle, LPCTSTR lpszCachePath /* = NULL */ ) { LONG lResult = ERROR_SUCCESS; HRESULT hr = S_OK; DWORD dw = 0; HKEY hKeyClass = NULL; HKEY hKeyClsid = NULL; HKEY hKeyMod = NULL; HKEY hkeyMUEntry = NULL; HKEY hKeyDist = NULL; TCHAR szT[MAX_PATH]; // scratch buffer int cEnum = 0; CCacheItem* pci = NULL; OCCFindData* poccfd = new OCCFindData(); LPCLSIDLIST_ITEM pcli = NULL; TCHAR szDUName[MAX_DIST_UNIT_NAME_LEN]; 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 (lResult != ERROR_SUCCESS) goto EXIT_FINDFIRSTCONTROL; // Search for legacy controls found in the COM branch if ((lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_MODULE_USAGE, 0, KEY_READ, &hKeyMod)) != ERROR_SUCCESS) 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, MAX_PATH)) == ERROR_SUCCESS) { TCHAR szClient[MAX_CLIENT_LEN]; lResult = RegOpenKeyEx(hKeyMod, szT, 0, KEY_READ, &hkeyMUEntry); Assert(lResult == ERROR_SUCCESS); // Hey, we just got the key name DWORD dwType; dw = MAX_CLIENT_LEN * sizeof(TCHAR); // Fetch the module owner. lResult = RegQueryValueEx(hkeyMUEntry, VALUE_OWNER, NULL, &dwType, (LPBYTE)szClient, &dw); Assert(lResult == ERROR_SUCCESS); // assert that mu entry is properly formed. // 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. lResult = RegOpenKeyEx(hKeyClass, szClient, 0, KEY_READ, &hKeyClsid); if (lResult == ERROR_SUCCESS) { 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 = MAX_PATH; lResult = RegQueryValue(hKeyClsid, INPROCSERVER32, szLocation, (PLONG)&dw); if (lResult != ERROR_SUCCESS) { dw = MAX_PATH; lResult = RegQueryValue(hKeyClsid, LOCALSERVER32, szLocation, (PLONG)&dw); } RegCloseKey(hKeyClsid); 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, MAX_DIST_UNIT_NAME_LEN)) == ERROR_SUCCESS) { // We should only display DU's installed by code download. HKEY hkeyDU; DWORD dwType; DWORD cb = MAX_PATH * sizeof(TCHAR); lResult = RegOpenKeyEx(hKeyDist, szDUName, 0, KEY_READ, &hkeyDU); Assert(lResult == ERROR_SUCCESS); if (!IsDUDisplayable(hkeyDU)) { continue; } szT[0] = '\0'; 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; LPOLESTR szDummyStr[MAX_CTRL_NAME_SIZE]; BOOL bFoundDuplicate = FALSE; MultiByteToWideChar(CP_ACP, 0, szDUName, -1, (LPOLESTR)szDummyStr, MAX_CTRL_NAME_SIZE); if ((CLSIDFromString((LPOLESTR)szDummyStr, &clsidDummy) == S_OK)) { for (pcli = poccfd->m_pcliHead; pcli != NULL; 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 != NULL) { 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, LPDWORD lpdwData, LPTSTR lpszData, int nBufLen) { if (hControlHandle == 0 || hControlHandle == INVALID_HANDLE_VALUE) return FALSE; BOOL bResult = TRUE; LPCTSTR lpStr = NULL; DWORD dw = 0; switch (nFlag) { case GCI_NAME: // get friend name of control lpStr = ((CCacheItem*)hControlHandle)->m_szName; break; case GCI_FILE: // get filename of control (with full path) lpStr = ((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 (*lpStr == '\0') { CPNode* ppn = ((CCacheItem*)hControlHandle)->GetFirstPackage(); if (ppn != NULL) lpStr = ppn->GetPath(); } if (*lpStr == '\0') { CPNode* pfn = ((CCacheItem*)hControlHandle)->GetFirstFile(); if (pfn != NULL) lpStr = pfn->GetPath(); } break; case GCI_DIST_UNIT_VERSION: lpStr = ((CCacheItem*)hControlHandle)->m_szVersion; break; case GCI_CLSID: // get CLSID of control lpStr = ((CCacheItem*)hControlHandle)->m_szCLSID; break; case GCI_TYPELIBID: // get TYPELIB id of control lpStr = ((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 lpStr = ((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 = (lpdwData != NULL); if (bResult) *lpdwData = dw; } else { bResult = (lpszData != NULL && nBufLen > lstrlen(lpStr)); if (bResult) lstrcpy(lpszData, lpStr); } 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; }