Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1329 lines
43 KiB

  1. #include "ParseInf.h"
  2. #include "general.h"
  3. #include <shlwapi.h>
  4. #include <wininet.h>
  5. //#define USE_SHORT_PATH_NAME 1
  6. #define REG_PATH_IE_CACHE_LIST TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ActiveX Cache")
  7. #define cCachePathsMax 5 // maximum number of legacy caches + the current cache du jour
  8. struct OCCFindData
  9. {
  10. LPCLSIDLIST_ITEM m_pcliHead;
  11. LPCLSIDLIST_ITEM m_pcliTail;
  12. struct {
  13. TCHAR m_sz[MAX_PATH];
  14. DWORD m_cch;
  15. } m_aCachePath[cCachePathsMax];
  16. OCCFindData();
  17. ~OCCFindData();
  18. BOOL IsCachePath( LPCTSTR szPath );
  19. // Control List operations
  20. HRESULT AddListItem( LPCTSTR szFileName, LPCTSTR szCLSID, DWORD dwIsDistUnit );
  21. LPCLSIDLIST_ITEM TakeFirstItem(void);
  22. };
  23. DWORD CCacheLegacyControl::s_dwType = 1;
  24. DWORD CCacheDistUnit::s_dwType = 2;
  25. HRESULT CCacheLegacyControl::Init( HKEY hkeyCLSID, LPCTSTR szFile, LPCTSTR szCLSID )
  26. {
  27. HRESULT hr = S_OK;
  28. lstrcpyn(m_szFile, szFile, ARRAYSIZE(m_szFile));
  29. lstrcpyn(m_szCLSID, szCLSID, ARRAYSIZE(m_szCLSID));
  30. // Get full user type name
  31. m_szName[0] = '\0';
  32. DWORD dw = sizeof(m_szName);
  33. LRESULT lResult = RegQueryValue(hkeyCLSID, m_szCLSID, m_szName, (LONG*)&dw);
  34. // if the fails, we should get a resource string (seanf 5/9/97 )
  35. // Get type lib id
  36. TCHAR szTypeLibValName[MAX_PATH];
  37. CatPathStrN( szTypeLibValName, szCLSID, HKCR_TYPELIB, ARRAYSIZE(szTypeLibValName) );
  38. dw = sizeof(m_szTypeLibID);
  39. lResult = RegQueryValue( hkeyCLSID, szTypeLibValName, m_szTypeLibID, (LONG*)&dw);
  40. if (lResult != ERROR_SUCCESS)
  41. m_szTypeLibID[0] = TEXT('\0');
  42. // Set Codebase
  43. m_szCodeBase[0] = '\0';
  44. m_szVersion[0] = '\0';
  45. hr = DoParse( m_szFile, m_szCLSID );
  46. return hr;
  47. }
  48. HRESULT CCacheDistUnit::Init( HKEY hkeyCLSID, LPCTSTR szFile, LPCTSTR szCLSID, HKEY hkeyDist, LPCTSTR szDU )
  49. {
  50. HRESULT hr = S_OK;
  51. HKEY hkeyDU;
  52. HKEY hkeyDLInfo; // DownloadInformation subkey
  53. HKEY hkeyVers; // InstalledVersion subkey
  54. HKEY hkeyCOM; // subkey of HKCR\CLSID, used if outside of cache dir
  55. LRESULT lResult = ERROR_SUCCESS;
  56. DWORD dw;
  57. TCHAR szNameT[MAX_PATH];
  58. UINT uiVerSize = 0;
  59. DWORD dwVerSize = 0;
  60. DWORD dwHandle = 0;
  61. BYTE *pbBuffer = NULL;
  62. HANDLE hFile;
  63. FILETIME ftLastAccess;
  64. BOOL bRunOnNT5 = FALSE;
  65. OSVERSIONINFO osvi;
  66. VS_FIXEDFILEINFO *lpVSInfo = NULL;
  67. if ( szFile[0] == '\0' &&
  68. RegOpenKeyEx( hkeyCLSID, szCLSID, 0, KEY_READ, &hkeyCOM ) == ERROR_SUCCESS )
  69. {
  70. LONG lcb = sizeof(szNameT);
  71. lResult = RegQueryValue( hkeyCOM, INPROCSERVER, szNameT, &lcb );
  72. if ( lResult != ERROR_SUCCESS )
  73. {
  74. lcb = sizeof(szNameT);
  75. lResult = RegQueryValue( hkeyCOM, INPROCSERVER32, szNameT, &lcb );
  76. }
  77. if ( lResult != ERROR_SUCCESS )
  78. {
  79. lcb = sizeof(szNameT);
  80. lResult = RegQueryValue( hkeyCOM, INPROCSERVERX86, szNameT, &lcb );
  81. }
  82. if ( lResult != ERROR_SUCCESS )
  83. {
  84. lcb = sizeof(szNameT);
  85. lResult = RegQueryValue( hkeyCOM, LOCALSERVER, szNameT, &lcb );
  86. }
  87. if ( lResult != ERROR_SUCCESS )
  88. {
  89. lcb = sizeof(szNameT);
  90. lResult = RegQueryValue( hkeyCOM, LOCALSERVER32, szNameT, &lcb );
  91. }
  92. if ( lResult != ERROR_SUCCESS )
  93. {
  94. lcb = sizeof(szNameT);
  95. lResult = RegQueryValue( hkeyCOM, LOCALSERVERX86, szNameT, &lcb );
  96. }
  97. RegCloseKey( hkeyCOM );
  98. }
  99. else
  100. lstrcpyn( szNameT, szFile, ARRAYSIZE(szNameT));
  101. if ( lResult != ERROR_SUCCESS ) // needed to find file path but couldn't
  102. szNameT[0] = '\0';
  103. hr = CCacheLegacyControl::Init( hkeyCLSID, szNameT, szCLSID );
  104. if ( FAILED(hr) )
  105. return hr;
  106. lResult = RegOpenKeyEx(hkeyDist, szDU, 0, KEY_READ, &hkeyDU);
  107. if (lResult != ERROR_SUCCESS)
  108. return E_FAIL;
  109. // Get CLSID
  110. lstrcpyn(m_szCLSID, szDU, MAX_DIST_UNIT_NAME_LEN);
  111. // Get full user type name - only override the control name if DU name is not empty
  112. dw = sizeof(szNameT);
  113. lResult = RegQueryValue(hkeyDU, NULL, szNameT, (LONG*)&dw);
  114. if ( lResult == ERROR_SUCCESS && szNameT[0] != '\0' )
  115. {
  116. lstrcpyn( m_szName, szNameT, ARRAYSIZE(m_szName) );
  117. }
  118. else if ( *m_szName == '\0' ) // worst case, if we still don't have a name, a GUID will suffice
  119. lstrcpyn( m_szName, szDU, ARRAYSIZE(m_szName) );
  120. // Get type lib id
  121. // Get type lib id
  122. TCHAR szTypeLibValName[MAX_PATH];
  123. CatPathStrN(szTypeLibValName, m_szCLSID, HKCR_TYPELIB, ARRAYSIZE(szTypeLibValName));
  124. dw = sizeof(m_szTypeLibID);
  125. lResult = RegQueryValue( hkeyCLSID, szTypeLibValName, m_szTypeLibID, (LONG*)&dw);
  126. if (lResult != ERROR_SUCCESS)
  127. (m_szTypeLibID)[0] = TEXT('\0');
  128. m_szCodeBase[0] ='\0';
  129. lResult = RegOpenKeyEx(hkeyDU, REGSTR_DOWNLOAD_INFORMATION, 0, KEY_READ, &hkeyDLInfo);
  130. if (lResult == ERROR_SUCCESS)
  131. {
  132. dw = sizeof(m_szCodeBase);
  133. HRESULT hrErr = RegQueryValueEx(hkeyDLInfo, REGSTR_DLINFO_CODEBASE, NULL, NULL,
  134. (unsigned char *)m_szCodeBase, &dw);
  135. RegCloseKey( hkeyDLInfo );
  136. }
  137. // Get Version from DU branch
  138. m_szVersion[0] ='\0';
  139. lResult = RegOpenKeyEx(hkeyDU, REGSTR_INSTALLED_VERSION, 0,
  140. KEY_READ, &hkeyVers);
  141. if (lResult == ERROR_SUCCESS)
  142. {
  143. dw = sizeof(m_szVersion);
  144. RegQueryValueEx(hkeyVers, NULL, NULL, NULL, (LPBYTE)m_szVersion, &dw);
  145. RegCloseKey(hkeyVers);
  146. }
  147. // The version specified in the COM branch is the definitive word on
  148. // what the version is. If a key exists in the COM branch, use the version
  149. // that is found inside the InProcServer/LocalServer.
  150. if (RegOpenKeyEx( hkeyCLSID, szCLSID, 0, KEY_READ, &hkeyCOM ) == ERROR_SUCCESS)
  151. {
  152. LONG lcb = sizeof(szNameT);
  153. lResult = RegQueryValue( hkeyCOM, INPROCSERVER32, szNameT, &lcb );
  154. if ( lResult != ERROR_SUCCESS )
  155. {
  156. lcb = sizeof(szNameT);
  157. lResult = RegQueryValue( hkeyCOM, INPROCSERVER, szNameT, &lcb );
  158. }
  159. if ( lResult != ERROR_SUCCESS )
  160. {
  161. lcb = sizeof(szNameT);
  162. lResult = RegQueryValue( hkeyCOM, INPROCSERVERX86, szNameT, &lcb );
  163. }
  164. if ( lResult != ERROR_SUCCESS )
  165. {
  166. lcb = sizeof(szNameT);
  167. lResult = RegQueryValue( hkeyCOM, LOCALSERVER32, szNameT, &lcb );
  168. }
  169. if ( lResult != ERROR_SUCCESS )
  170. {
  171. lcb = sizeof(szNameT);
  172. lResult = RegQueryValue( hkeyCOM, LOCALSERVER, szNameT, &lcb );
  173. }
  174. if ( lResult != ERROR_SUCCESS )
  175. {
  176. lcb = sizeof(szNameT);
  177. lResult = RegQueryValue( hkeyCOM, LOCALSERVERX86, szNameT, &lcb );
  178. }
  179. RegCloseKey( hkeyCOM );
  180. // HACK! GetFileVersionInfoSize and GetFileVersionInfo modify
  181. // the last access time of the file under NT5! This causes us
  182. // to retrieve the wrong last access time when removing expired
  183. // controls. This hack gets the last access time before the
  184. // GetFileVersionInfo calls, and sets it back afterwards.
  185. // See IE5 RAID #56927 for details. This code should be removed
  186. // when NT5 fixes this bug.
  187. osvi.dwOSVersionInfoSize = sizeof(osvi);
  188. GetVersionEx(&osvi);
  189. if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion == 5) {
  190. bRunOnNT5 = TRUE;
  191. }
  192. if (bRunOnNT5) {
  193. hFile = CreateFile(szNameT, GENERIC_READ, FILE_SHARE_READ, NULL,
  194. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  195. if (hFile != INVALID_HANDLE_VALUE) {
  196. GetFileTime(hFile, NULL, &ftLastAccess, NULL);
  197. CloseHandle(hFile);
  198. }
  199. }
  200. dwVerSize = GetFileVersionInfoSize((char *)szNameT, &dwHandle);
  201. pbBuffer = new BYTE[dwVerSize];
  202. if (!pbBuffer)
  203. {
  204. return E_OUTOFMEMORY;
  205. }
  206. if (GetFileVersionInfo((char *)szNameT, 0, dwVerSize, pbBuffer))
  207. {
  208. if (VerQueryValue(pbBuffer, "\\", (void **)&lpVSInfo, &uiVerSize))
  209. {
  210. wsprintf(m_szVersion, "%d,%d,%d,%d", (lpVSInfo->dwFileVersionMS >> 16) & 0xFFFF
  211. , lpVSInfo->dwFileVersionMS & 0xFFFF
  212. , (lpVSInfo->dwFileVersionLS >> 16) & 0xFFFF
  213. , lpVSInfo->dwFileVersionLS & 0xFFFF);
  214. }
  215. }
  216. delete [] pbBuffer;
  217. if (bRunOnNT5) {
  218. hFile = CreateFile(szNameT, GENERIC_WRITE, FILE_SHARE_READ, NULL,
  219. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  220. if (hFile != INVALID_HANDLE_VALUE) {
  221. SetFileTime(hFile, NULL, &ftLastAccess, NULL);
  222. CloseHandle(hFile);
  223. }
  224. }
  225. }
  226. RegCloseKey( hkeyDU );
  227. return DoParseDU( m_szFile, m_szCLSID);
  228. }
  229. HRESULT MakeCacheItemFromControlList( HKEY hkeyClass, // HKCR\CLSID
  230. HKEY hkeyDist, // HKLM\SOFTWARE\MICROSOFT\Code Store Database\Distribution Units
  231. LPCLSIDLIST_ITEM pcli,
  232. CCacheItem **ppci )
  233. {
  234. HRESULT hr = E_FAIL;
  235. *ppci = NULL;
  236. if ( pcli->bIsDistUnit )
  237. {
  238. CCacheDistUnit *pcdu = new CCacheDistUnit();
  239. if ( pcdu != NULL &&
  240. SUCCEEDED(hr = pcdu->Init( hkeyClass,
  241. pcli->szFile,
  242. pcli->szCLSID,
  243. hkeyDist,
  244. pcli->szCLSID)) )
  245. *ppci = pcdu;
  246. else
  247. hr = E_OUTOFMEMORY;
  248. }
  249. else
  250. {
  251. CCacheLegacyControl *pclc = new CCacheLegacyControl();
  252. if ( pclc != NULL &&
  253. SUCCEEDED(hr = pclc->Init( hkeyClass,
  254. pcli->szFile,
  255. pcli->szCLSID )) )
  256. *ppci = pclc;
  257. else
  258. hr = E_OUTOFMEMORY;
  259. }
  260. return hr;
  261. }
  262. OCCFindData::OCCFindData() : m_pcliHead(NULL), m_pcliTail(NULL)
  263. {
  264. LONG lResult;
  265. HKEY hkeyCacheList;
  266. for ( int i = 0; i < cCachePathsMax; i++ )
  267. {
  268. m_aCachePath[i].m_cch = 0;
  269. m_aCachePath[i].m_sz[0] = '\0';
  270. }
  271. // Unhook occache as a shell extension for the cache folders.
  272. lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  273. REG_PATH_IE_CACHE_LIST,
  274. 0x0,
  275. KEY_READ,
  276. &hkeyCacheList );
  277. if ( lResult == ERROR_SUCCESS ) {
  278. DWORD dwIndex;
  279. TCHAR szName[MAX_PATH];
  280. DWORD cbName;
  281. DWORD cbValue;
  282. for ( dwIndex = 0, cbName = sizeof(szName), cbValue = MAX_PATH * sizeof(TCHAR);
  283. dwIndex < cCachePathsMax;
  284. dwIndex++, cbName = sizeof(szName), cbValue = MAX_PATH * sizeof(TCHAR) )
  285. {
  286. lResult = RegEnumValue( hkeyCacheList, dwIndex,
  287. szName, &cbName,
  288. NULL, NULL,
  289. (LPBYTE)m_aCachePath[dwIndex].m_sz, &cbValue );
  290. m_aCachePath[dwIndex].m_cch = lstrlen( m_aCachePath[dwIndex].m_sz );
  291. }
  292. // We leave this key in place because it is the only record we have of the
  293. // cache folders and would be useful to future installations of IE
  294. RegCloseKey( hkeyCacheList );
  295. }
  296. }
  297. OCCFindData::~OCCFindData()
  298. {
  299. if ( m_pcliHead )
  300. RemoveList(m_pcliHead);
  301. }
  302. BOOL OCCFindData::IsCachePath( LPCTSTR szPath )
  303. {
  304. BOOL fMatch = FALSE;
  305. for ( int i = 0; i < cCachePathsMax && !fMatch; i++ )
  306. fMatch = m_aCachePath[i].m_cch != 0 &&
  307. LStrNICmp( szPath, m_aCachePath[i].m_sz, m_aCachePath[i].m_cch ) == 0;
  308. return fMatch;
  309. }
  310. HRESULT OCCFindData::AddListItem( LPCTSTR szFile, LPCTSTR szCLSID, DWORD dwIsDistUnit )
  311. {
  312. HRESULT hr = S_OK;
  313. if ( m_pcliTail == NULL )
  314. {
  315. m_pcliTail = new CLSIDLIST_ITEM;
  316. if (m_pcliHead == NULL)
  317. m_pcliHead = m_pcliTail;
  318. }
  319. else
  320. {
  321. m_pcliTail->pNext = new CLSIDLIST_ITEM;
  322. m_pcliTail = m_pcliTail->pNext;
  323. }
  324. if ( m_pcliTail != NULL )
  325. {
  326. m_pcliTail->pNext = NULL;
  327. lstrcpyn(m_pcliTail->szFile, szFile, MAX_PATH);
  328. lstrcpyn(m_pcliTail->szCLSID, szCLSID, MAX_DIST_UNIT_NAME_LEN);
  329. m_pcliTail->bIsDistUnit = dwIsDistUnit;
  330. }
  331. else
  332. hr = E_OUTOFMEMORY;
  333. return hr;
  334. }
  335. LPCLSIDLIST_ITEM OCCFindData::TakeFirstItem(void)
  336. {
  337. LPCLSIDLIST_ITEM pcli = m_pcliHead;
  338. if (m_pcliHead != NULL)
  339. {
  340. m_pcliHead = m_pcliHead;
  341. m_pcliHead = m_pcliHead->pNext;
  342. if ( m_pcliHead == NULL )
  343. m_pcliTail = NULL;
  344. }
  345. return pcli;
  346. }
  347. BOOL IsDUDisplayable(HKEY hkeyDU)
  348. {
  349. BOOL bRet = FALSE;
  350. if (hkeyDU)
  351. {
  352. if (IsShowAllFilesEnabled())
  353. {
  354. bRet = TRUE;
  355. }
  356. else
  357. {
  358. DWORD dwType = 0, dwSystem = 0, dwSize = sizeof(dwSystem);
  359. long lResult = RegQueryValueEx(hkeyDU, VALUE_SYSTEM, NULL, &dwType, (LPBYTE)&dwSystem, &dwSize);
  360. bRet = (lResult == ERROR_SUCCESS && dwSystem == TRUE) ? (FALSE) : (TRUE);
  361. }
  362. }
  363. return bRet;
  364. }
  365. BOOL IsShowAllFilesEnabled()
  366. {
  367. HKEY hkey = 0;
  368. BOOL bRet = FALSE;
  369. DWORD dwShowAll = 0;
  370. DWORD lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_READ, &hkey);
  371. if (lResult == ERROR_SUCCESS)
  372. {
  373. DWORD dwType, dwSize = sizeof(dwShowAll);
  374. lResult = RegQueryValueEx(hkey, REGSTR_SHOW_ALL_FILES, NULL, &dwType, (LPBYTE)&dwShowAll, &dwSize);
  375. if (lResult == ERROR_SUCCESS)
  376. {
  377. bRet = (dwShowAll != 0);
  378. }
  379. RegCloseKey(hkey);
  380. }
  381. return bRet;
  382. }
  383. void ToggleShowAllFiles()
  384. {
  385. DWORD dwShowAll = !IsShowAllFilesEnabled();
  386. HKEY hkey = 0;
  387. DWORD lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_ALL_ACCESS, &hkey);
  388. if (lResult == ERROR_SUCCESS)
  389. {
  390. RegSetValueEx(hkey, REGSTR_SHOW_ALL_FILES, 0, REG_DWORD, (CONST BYTE *)&dwShowAll, sizeof(dwShowAll));
  391. RegCloseKey(hkey);
  392. }
  393. }
  394. LONG WINAPI FindFirstControl(HANDLE& hFindHandle, HANDLE& hControlHandle, LPCTSTR pszCachePath)
  395. {
  396. LONG lResult = ERROR_SUCCESS;
  397. HRESULT hr = S_OK;
  398. DWORD dw = 0;
  399. HKEY hKeyClass = NULL;
  400. HKEY hKeyMod = NULL;
  401. HKEY hKeyDist = NULL;
  402. TCHAR szT[MAX_PATH]; // scratch buffer
  403. int cEnum = 0;
  404. CCacheItem *pci = NULL;
  405. LPCLSIDLIST_ITEM pcli = NULL;
  406. TCHAR szDUName[MAX_DIST_UNIT_NAME_LEN];
  407. OCCFindData *poccfd = new OCCFindData();
  408. if ( poccfd == NULL )
  409. {
  410. lResult = ERROR_NOT_ENOUGH_MEMORY;
  411. goto EXIT_FINDFIRSTCONTROL;
  412. }
  413. // Open up the HKCR\CLSID key.
  414. lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, HKCR_CLSID, 0, KEY_READ, &hKeyClass);
  415. if (ERROR_SUCCESS != lResult)
  416. goto EXIT_FINDFIRSTCONTROL;
  417. // Search for legacy controls found in the COM branch
  418. lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_MODULE_USAGE, 0, KEY_READ, &hKeyMod);
  419. if (ERROR_SUCCESS != lResult)
  420. goto EXIT_FINDFIRSTCONTROL;
  421. // Enumerate the known modules and build up a list of the owners.
  422. // This is a search for legacy controls.
  423. while ((lResult = RegEnumKey(hKeyMod, cEnum++, szT, ARRAYSIZE(szT))) == ERROR_SUCCESS)
  424. {
  425. TCHAR szClient[MAX_CLIENT_LEN];
  426. HKEY hKeyClsid = NULL;
  427. HKEY hkeyMUEntry = NULL;
  428. lResult = RegOpenKeyEx( hKeyMod, szT, 0, KEY_READ, &hkeyMUEntry );
  429. if (ERROR_SUCCESS != lResult)
  430. continue;
  431. // Fetch the module owner.
  432. // If the module owner is in the COM branch AND
  433. // ( the owner lives in the cache OR it has an INF in the cache )
  434. // Then add the _owner_ to our list of legacy controls.
  435. // In the INF case, we may be looking at a control that was re-registered
  436. // outside of the cache.
  437. // If it doesn't have these properties, then it is either a DU module or
  438. // was installed by something other than MSICD. In either case, we'll skip it
  439. // at least for now.
  440. dw = sizeof(szClient);
  441. lResult = RegQueryValueEx(hkeyMUEntry, VALUE_OWNER, NULL, NULL, (LPBYTE)szClient, &dw);
  442. if (ERROR_SUCCESS != lResult)
  443. continue;
  444. lResult = RegOpenKeyEx(hKeyClass, szClient, 0, KEY_READ, &hKeyClsid);
  445. if (ERROR_SUCCESS == lResult)
  446. {
  447. TCHAR szCLocation[MAX_PATH]; // Canonical path of control
  448. TCHAR szLocation[MAX_PATH]; // Location in COM CLSID reg tree.
  449. // Look for InprocServer[32] or LocalServer[32] key
  450. dw = sizeof(szLocation);
  451. lResult = RegQueryValue(hKeyClsid, INPROCSERVER32, szLocation, (PLONG)&dw);
  452. if (lResult != ERROR_SUCCESS)
  453. {
  454. dw = sizeof(szLocation);
  455. lResult = RegQueryValue(hKeyClsid, LOCALSERVER32, szLocation, (PLONG)&dw);
  456. }
  457. RegCloseKey(hKeyClsid);
  458. hKeyClsid = NULL;
  459. if ( lResult == ERROR_SUCCESS )
  460. {
  461. BOOL bAddOwner;
  462. // see if we've already got an entry for this one.
  463. for ( pcli = poccfd->m_pcliHead;
  464. pcli != NULL && lstrcmp( szClient, pcli->szCLSID ) != 0;
  465. pcli = pcli->pNext );
  466. if ( pcli == NULL ) // not found - possibly add new item
  467. {
  468. // Canonicalize the path for use in comparisons with cache dirs
  469. if ( OCCGetLongPathName(szCLocation, szLocation, MAX_PATH) == 0 )
  470. lstrcpyn( szCLocation, szLocation, MAX_PATH );
  471. // Is the owner in our cache?
  472. bAddOwner = poccfd->IsCachePath( szCLocation );
  473. if ( !bAddOwner )
  474. {
  475. // does it have an INF in our cache(s)?
  476. // We'll appropriate szDCachePath
  477. for ( int i = 0; i < cCachePathsMax && !bAddOwner; i++ )
  478. {
  479. if ( poccfd->m_aCachePath[i].m_sz != '\0' )
  480. {
  481. CatPathStrN( szT, poccfd->m_aCachePath[i].m_sz, PathFindFileName( szCLocation ), MAX_PATH);
  482. // Note if another copy of the owner exists within the cache(s).
  483. // This would be a case of re-registration.
  484. if ( PathFileExists( szT ) )
  485. {
  486. // add our version of the control.
  487. lstrcpyn( szCLocation, szT, MAX_PATH );
  488. bAddOwner = TRUE;
  489. }
  490. else
  491. bAddOwner = PathRenameExtension( szT, INF_EXTENSION ) &&
  492. PathFileExists( szT );
  493. } // if cache path
  494. } // for each cache directory
  495. } // if check for cached INF
  496. if ( bAddOwner )
  497. {
  498. HKEY hkeyDUCheck = 0;
  499. char achBuf[MAX_REGPATH_LEN];
  500. wnsprintfA(achBuf, MAX_REGPATH_LEN, "%s\\%s", REGSTR_PATH_DIST_UNITS, szClient);
  501. lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, achBuf, 0, KEY_READ, &hkeyDUCheck);
  502. if (lResult != ERROR_SUCCESS)
  503. {
  504. // This is a legacy control with no corresponding DU
  505. poccfd->AddListItem( szCLocation, szClient, FALSE );
  506. }
  507. else
  508. {
  509. if (IsDUDisplayable(hkeyDUCheck))
  510. {
  511. // Legacy control w/ DU keys that is displayable
  512. poccfd->AddListItem( szCLocation, szClient, FALSE );
  513. }
  514. RegCloseKey(hkeyDUCheck);
  515. }
  516. }
  517. } // if owner we haven't seen before
  518. } // if owner has local or inproc server
  519. } // if owner has COM entry
  520. RegCloseKey( hkeyMUEntry );
  521. } // while enumerating Module Usage
  522. // we're finished with module usage
  523. RegCloseKey(hKeyMod);
  524. // Now search distribution units
  525. // Check for duplicates - distribution units for controls we detected above
  526. lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_READ, &hKeyDist);
  527. if (lResult == ERROR_SUCCESS)
  528. {
  529. cEnum = 0;
  530. // Enumerate distribution units and queue them up in the list
  531. while ((lResult = RegEnumKey(hKeyDist, cEnum++, szDUName, ARRAYSIZE(szDUName))) == ERROR_SUCCESS)
  532. {
  533. // We should only display DU's installed by code download.
  534. HKEY hkeyDU;
  535. DWORD dwType;
  536. lResult = RegOpenKeyEx( hKeyDist, szDUName, 0, KEY_READ, &hkeyDU );
  537. Assert( lResult == ERROR_SUCCESS );
  538. if ((ERROR_SUCCESS != lResult) ||
  539. !IsDUDisplayable(hkeyDU))
  540. {
  541. continue;
  542. }
  543. szT[0] = '\0';
  544. DWORD cb = sizeof(szT);
  545. lResult = RegQueryValueEx( hkeyDU, DU_INSTALLER_VALUE, NULL, &dwType, (LPBYTE)szT, &cb );
  546. Assert( lResult == ERROR_SUCCESS ); // properly-formed DU will have this
  547. Assert( dwType == REG_SZ ); // properly-formed DU's have a string here
  548. // Check for an installed version. We might just have a DU that has an AvailableVersion
  549. // but hasn't been installed yet.
  550. lResult = RegQueryValue( hkeyDU, REGSTR_INSTALLED_VERSION, NULL, NULL );
  551. RegCloseKey( hkeyDU );
  552. if ( lstrcmpi( szT, CDL_INSTALLER ) == 0 &&
  553. lResult == ERROR_SUCCESS ) // from InstalledVersion RegQueryValue
  554. {
  555. // If we can convert the unique name to a GUID, then this DU
  556. // may have already been added on the first pass through the
  557. // COM branch.
  558. CLSID clsidDummy = CLSID_NULL;
  559. WORD szDummyStr[MAX_CTRL_NAME_SIZE];
  560. BOOL bFoundDuplicate = FALSE;
  561. MultiByteToWideChar(CP_ACP, 0, szDUName, -1, szDummyStr, ARRAYSIZE(szDummyStr));
  562. if ((CLSIDFromString(szDummyStr, &clsidDummy) == S_OK))
  563. {
  564. for (pcli = poccfd->m_pcliHead; pcli; pcli = pcli->pNext)
  565. {
  566. if (!lstrcmpi(szDUName, pcli->szCLSID))
  567. {
  568. // Duplicate found. Use dist unit information to
  569. // fill in additional fields if it is the first
  570. // entry in the list
  571. bFoundDuplicate = TRUE;
  572. pcli->bIsDistUnit = TRUE;
  573. break;
  574. }
  575. }
  576. }
  577. if (!bFoundDuplicate)
  578. {
  579. // Okay we're looking at some sort of Java scenario. We have a distribution unit, but
  580. // no corresponding entry in the COM branch. This generally means we've got a DU that
  581. // consists of java packages. It can also mean that we're dealing with a java/code download
  582. // backdoor introduced in IE3. In this case, an Object tag gets a CAB downloaded that
  583. // installs Java classes and sets of a CLSID that invokes MSJava.dll on the class ( ESPN's
  584. // sportszone control/applet works this way ). In the first case, we get the name
  585. // squared-away when we parse the DU. In the latter case, we need to try and pick the name
  586. // up from the COM branch.
  587. hr = poccfd->AddListItem( "", szDUName, TRUE );
  588. if ( FAILED(hr) )
  589. {
  590. lResult = ERROR_NOT_ENOUGH_MEMORY;
  591. goto EXIT_FINDFIRSTCONTROL;
  592. }
  593. } // if no duplicate - add DU to the list
  594. } // if installed by MSICD
  595. } // while enumerating DU's
  596. } // if we can open the DU key.
  597. else
  598. lResult = ERROR_NO_MORE_ITEMS; // if no DU's then make due with our legacy controls, if any
  599. pcli = poccfd->TakeFirstItem();
  600. if (pcli)
  601. {
  602. hr = MakeCacheItemFromControlList(hKeyClass, hKeyDist, pcli, &pci);
  603. delete pcli;
  604. if ( FAILED(hr) )
  605. lResult = hr;
  606. }
  607. if (hKeyDist)
  608. {
  609. RegCloseKey(hKeyDist);
  610. hKeyDist = 0;
  611. }
  612. // Clean up
  613. if (lResult != ERROR_NO_MORE_ITEMS)
  614. goto EXIT_FINDFIRSTCONTROL;
  615. if (pci == NULL)
  616. lResult = ERROR_NO_MORE_ITEMS;
  617. else
  618. {
  619. lResult = ERROR_SUCCESS;
  620. }
  621. hFindHandle = (HANDLE)poccfd;
  622. hControlHandle = (HANDLE)pci;
  623. EXIT_FINDFIRSTCONTROL:
  624. if (hKeyDist)
  625. RegCloseKey(hKeyDist);
  626. if (hKeyClass)
  627. RegCloseKey(hKeyClass);
  628. if (lResult != ERROR_SUCCESS)
  629. {
  630. if ( pci != NULL )
  631. delete pci;
  632. if ( poccfd != NULL )
  633. delete poccfd;
  634. hFindHandle = INVALID_HANDLE_VALUE;
  635. hControlHandle = INVALID_HANDLE_VALUE;
  636. }
  637. return lResult;
  638. }
  639. LONG WINAPI FindNextControl(HANDLE& hFindHandle, HANDLE& hControlHandle)
  640. {
  641. LONG lResult = ERROR_SUCCESS;
  642. HRESULT hr = S_OK;
  643. HKEY hKeyClass = NULL;
  644. CCacheItem *pci = NULL;
  645. OCCFindData *poccfd = (OCCFindData *)hFindHandle;
  646. LPCLSIDLIST_ITEM pcli = poccfd->TakeFirstItem();
  647. hControlHandle = INVALID_HANDLE_VALUE;
  648. if (pcli == NULL)
  649. {
  650. lResult = ERROR_NO_MORE_ITEMS;
  651. goto EXIT_FINDNEXTCONTROL;
  652. }
  653. if ((lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, HKCR_CLSID, 0, KEY_READ, &hKeyClass)) != ERROR_SUCCESS)
  654. goto EXIT_FINDNEXTCONTROL;
  655. if ( pcli->bIsDistUnit )
  656. {
  657. HKEY hKeyDist;
  658. lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0,
  659. KEY_READ, &hKeyDist);
  660. if ( lResult == ERROR_SUCCESS )
  661. {
  662. hr = MakeCacheItemFromControlList( hKeyClass,
  663. hKeyDist,
  664. pcli,
  665. &pci );
  666. if ( FAILED(hr) )
  667. lResult = hr;
  668. RegCloseKey( hKeyDist );
  669. }
  670. }
  671. else
  672. {
  673. // This is not a distribution unit. Fill in CCachItem information
  674. // from the COM branch.
  675. hr = MakeCacheItemFromControlList(hKeyClass, NULL, pcli, &pci );
  676. if ( FAILED(hr) )
  677. lResult = hr;
  678. }
  679. hControlHandle = (HANDLE)pci;
  680. EXIT_FINDNEXTCONTROL:
  681. if (hKeyClass)
  682. RegCloseKey(hKeyClass);
  683. if (pcli != NULL)
  684. {
  685. delete pcli;
  686. }
  687. return lResult;
  688. }
  689. void WINAPI FindControlClose(HANDLE hFindHandle)
  690. {
  691. if (hFindHandle == INVALID_HANDLE_VALUE ||
  692. hFindHandle == (HANDLE)0)
  693. return;
  694. delete (OCCFindData*)hFindHandle;
  695. }
  696. void WINAPI ReleaseControlHandle(HANDLE hControlHandle)
  697. {
  698. if (hControlHandle == INVALID_HANDLE_VALUE ||
  699. hControlHandle == (HANDLE)0)
  700. return;
  701. delete (CCacheItem *)hControlHandle;
  702. }
  703. HRESULT WINAPI RemoveControlByHandle(HANDLE hControlHandle, BOOL bForceRemove /* = FALSE */)
  704. {
  705. return RemoveControlByHandle2( hControlHandle, bForceRemove, FALSE );
  706. }
  707. HRESULT WINAPI RemoveControlByName(LPCTSTR lpszFile, LPCTSTR lpszCLSID, LPCTSTR lpszTypeLibID, BOOL bForceRemove, /* = FALSE */ DWORD dwIsDistUnit /* = FALSE */)
  708. {
  709. return RemoveControlByName2( lpszFile, lpszCLSID, lpszTypeLibID, bForceRemove, dwIsDistUnit, FALSE);
  710. }
  711. LONG WINAPI GetControlDependentFile(int iFile, HANDLE hControlHandle, LPTSTR lpszFile, LPDWORD lpdwSize, BOOL bToUpper /* = FALSE */)
  712. {
  713. CCacheItem *pci = (CCacheItem *)hControlHandle;
  714. if (iFile < 0 || lpszFile == NULL || lpdwSize == NULL)
  715. return ERROR_BAD_ARGUMENTS;
  716. // loop through the list of files to find the one indicated
  717. // by the given index.
  718. // this way is dumb but since a control does not depend on
  719. // too many files, it's ok
  720. CFileNode *pFileNode = pci->GetFirstFile();
  721. for (int i = 0; i < iFile && pFileNode != NULL; i++)
  722. pFileNode = pci->GetNextFile();
  723. if (pFileNode == NULL)
  724. {
  725. lpszFile[0] = TEXT('\0');
  726. lpdwSize = 0;
  727. return ERROR_NO_MORE_FILES;
  728. }
  729. // Make a fully qualified filename
  730. if (pFileNode->GetPath() != NULL)
  731. {
  732. CatPathStrN( lpszFile, pFileNode->GetPath(), pFileNode->GetName(), MAX_PATH);
  733. }
  734. else
  735. {
  736. lstrcpy(lpszFile, pFileNode->GetName());
  737. }
  738. if (FAILED(GetSizeOfFile(lpszFile, lpdwSize)))
  739. *lpdwSize = 0;
  740. // to upper case if required
  741. if (bToUpper)
  742. CharUpper(lpszFile);
  743. return ERROR_SUCCESS;
  744. }
  745. // determine if a control or one of its associated files can be removed
  746. // by reading its SharedDlls count
  747. BOOL WINAPI IsModuleRemovable(LPCTSTR lpszFile)
  748. {
  749. TCHAR szFile[MAX_PATH];
  750. TCHAR szT[MAX_PATH];
  751. if (lpszFile == NULL)
  752. return FALSE;
  753. if ( OCCGetLongPathName(szFile, lpszFile, MAX_PATH) == 0 )
  754. lstrcpyn( szFile, lpszFile, MAX_PATH );
  755. // Don't ever pull something out of the system directory.
  756. // This is a "safe" course of action because it is not reasonable
  757. // to expect the user to judge whether yanking this file damage other
  758. // software installations or the system itself.
  759. GetSystemDirectory(szT, MAX_PATH);
  760. if (StrStrI(szFile, szT))
  761. return FALSE;
  762. // check moduleusage if a control is safe to remove
  763. if (LookUpModuleUsage(szFile, NULL, szT, MAX_PATH) != S_OK)
  764. return FALSE;
  765. // if we don't know who the owner of the module is, it's not
  766. // safe to remove
  767. if (lstrcmpi(szT, UNKNOWNOWNER) == 0)
  768. return FALSE;
  769. else
  770. {
  771. // check shareddlls if a control is safe to remove
  772. LONG cRef;
  773. HRESULT hr = SetSharedDllsCount( szFile, -1, &cRef );
  774. return cRef == 1;
  775. }
  776. }
  777. BOOL WINAPI GetControlInfo(HANDLE hControlHandle, UINT nFlag,
  778. DWORD *pdwData, LPTSTR pszData, int nBufLen)
  779. {
  780. if (hControlHandle == 0 || hControlHandle == INVALID_HANDLE_VALUE)
  781. return FALSE;
  782. BOOL bResult = TRUE;
  783. LPCTSTR pStr = NULL;
  784. DWORD dw = 0;
  785. switch (nFlag)
  786. {
  787. case GCI_NAME: // get friend name of control
  788. pStr = ((CCacheItem *)hControlHandle)->m_szName;
  789. break;
  790. case GCI_FILE: // get filename of control (with full path)
  791. pStr = ((CCacheItem *)hControlHandle)->m_szFile;
  792. // if there is no file, but there is a package list, fake it
  793. // with the path to the first package's ZIP file.
  794. if ( *pStr == '\0' )
  795. {
  796. CPNode *ppn = ((CCacheItem *)hControlHandle)->GetFirstPackage();
  797. if (ppn)
  798. {
  799. pStr = ppn->GetPath();
  800. if (!pStr)
  801. {
  802. return FALSE; // this means hControlHandle is an invalid arg
  803. }
  804. }
  805. }
  806. if ( pStr && *pStr == TEXT('\0') )
  807. {
  808. CPNode *pfn = ((CCacheItem *)hControlHandle)->GetFirstFile();
  809. if ( pfn != NULL )
  810. pStr = pfn->GetPath();
  811. }
  812. break;
  813. case GCI_DIST_UNIT_VERSION:
  814. pStr = ((CCacheItem *)hControlHandle)->m_szVersion;
  815. break;
  816. case GCI_CLSID: // get CLSID of control
  817. pStr = ((CCacheItem *)hControlHandle)->m_szCLSID;
  818. break;
  819. case GCI_TYPELIBID: // get TYPELIB id of control
  820. pStr = ((CCacheItem *)hControlHandle)->m_szTypeLibID;
  821. break;
  822. case GCI_TOTALSIZE: // get total size in bytes
  823. dw = ((CCacheItem *)hControlHandle)->GetTotalFileSize();
  824. break;
  825. case GCI_SIZESAVED: // get total size restored if control is removed
  826. dw = ((CCacheItem *)hControlHandle)->GetTotalSizeSaved();
  827. break;
  828. case GCI_TOTALFILES: // get total number of files related to control
  829. dw = (DWORD)(((CCacheItem *)hControlHandle)->GetTotalFiles());
  830. break;
  831. case GCI_CODEBASE: // get CodeBase for control
  832. pStr = ((CCacheItem *)hControlHandle)->m_szCodeBase;
  833. break;
  834. case GCI_ISDISTUNIT:
  835. dw = ((CCacheItem *)hControlHandle)->ItemType() == CCacheDistUnit::s_dwType;
  836. break;
  837. case GCI_STATUS:
  838. dw = ((CCacheItem *)hControlHandle)->GetStatus();
  839. break;
  840. case GCI_HAS_ACTIVEX:
  841. dw = ((CCacheItem *)hControlHandle)->GetHasActiveX();
  842. break;
  843. case GCI_HAS_JAVA:
  844. dw = ((CCacheItem *)hControlHandle)->GetHasJava();
  845. break;
  846. }
  847. if (nFlag == GCI_TOTALSIZE ||
  848. nFlag == GCI_SIZESAVED ||
  849. nFlag == GCI_TOTALFILES ||
  850. nFlag == GCI_ISDISTUNIT ||
  851. nFlag == GCI_STATUS ||
  852. nFlag == GCI_HAS_ACTIVEX ||
  853. nFlag == GCI_HAS_JAVA)
  854. {
  855. bResult = pdwData != NULL;
  856. if (bResult)
  857. *pdwData = dw;
  858. }
  859. else
  860. {
  861. bResult = pszData && pStr;
  862. if (bResult)
  863. lstrcpyn(pszData, pStr, nBufLen);
  864. }
  865. return bResult;
  866. }
  867. ///////////////////////////////////////////////////////////////////////////////
  868. // API to be called by Advpack.dll
  869. // Define list node to be used in a linked list of control
  870. struct tagHANDLENODE;
  871. typedef struct tagHANDLENODE HANDLENODE;
  872. typedef HANDLENODE* LPHANDLENODE;
  873. struct tagHANDLENODE
  874. {
  875. HANDLE hControl;
  876. struct tagHANDLENODE* pNext;
  877. };
  878. // Given a handle to a control, get the control's last access time
  879. // Result is stored in a FILETIME struct
  880. HRESULT GetLastAccessTime(HANDLE hControl, FILETIME *pLastAccess)
  881. {
  882. Assert(hControl != NULL && hControl != INVALID_HANDLE_VALUE);
  883. Assert(pLastAccess != NULL);
  884. HRESULT hr = S_OK;
  885. WIN32_FIND_DATA fdata;
  886. HANDLE h = INVALID_HANDLE_VALUE;
  887. LPCTSTR lpszFile = NULL;
  888. CCacheItem *pci = (CCacheItem *)hControl;
  889. CPNode *ppn;
  890. if (pci->m_szFile[0] != 0)
  891. lpszFile = pci->m_szFile;
  892. else if ( (ppn = pci->GetFirstPackage()) != NULL )
  893. lpszFile = ppn->GetPath();
  894. else if ( (ppn = pci->GetFirstFile()) != NULL )
  895. lpszFile = ppn->GetPath();
  896. if ( lpszFile )
  897. h = FindFirstFile(lpszFile, &fdata);
  898. if (h == INVALID_HANDLE_VALUE)
  899. {
  900. SYSTEMTIME stNow;
  901. GetLocalTime(&stNow);
  902. SystemTimeToFileTime(&stNow, pLastAccess);
  903. hr = HRESULT_FROM_WIN32(GetLastError());
  904. }
  905. else
  906. {
  907. // Convert file time to local file time, then file time to
  908. // system time. Set those fields to be ignored to 0, and
  909. // set system time back to file time.
  910. // FILETIME struct is used because API for time comparison
  911. // only works on FILETIME.
  912. // SYSTEMTIME sysTime;
  913. FindClose(h);
  914. FileTimeToLocalFileTime(&(fdata.ftLastAccessTime), pLastAccess);
  915. }
  916. return hr;
  917. }
  918. HRESULT WINAPI SweepControlsByLastAccessDate(
  919. SYSTEMTIME *pLastAccessTime /* = NULL */,
  920. PFNDOBEFOREREMOVAL pfnDoBefore /* = NULL */,
  921. PFNDOAFTERREMOVAL pfnDoAfter /* = NULL */,
  922. DWORD dwSizeLimit /* = 0 */
  923. )
  924. {
  925. LONG lResult = ERROR_SUCCESS;
  926. HRESULT hr = S_FALSE;
  927. DWORD dwSize = 0, dwTotalSize = 0;
  928. HANDLE hFind = NULL, hControl = NULL;
  929. LPHANDLENODE pHead = NULL, pCur = NULL;
  930. FILETIME timeLastAccess, timeRemovePrior;
  931. UINT cCnt = 0;
  932. TCHAR szFile[MAX_PATH];
  933. // ignore all fields except wYear, wMonth and wDay
  934. if (pLastAccessTime != NULL)
  935. {
  936. pLastAccessTime->wDayOfWeek = 0;
  937. pLastAccessTime->wHour = 0;
  938. pLastAccessTime->wMinute = 0;
  939. pLastAccessTime->wSecond = 0;
  940. pLastAccessTime->wMilliseconds = 0;
  941. }
  942. // loop through all controls and put in a list the
  943. // ones that are accessed before the given date and
  944. // are safe to uninstall
  945. lResult = FindFirstControl(hFind, hControl);
  946. for (;lResult == ERROR_SUCCESS;
  947. lResult = FindNextControl(hFind, hControl))
  948. {
  949. // check last access time
  950. if (pLastAccessTime != NULL)
  951. {
  952. GetLastAccessTime(hControl, &timeLastAccess);
  953. SystemTimeToFileTime(pLastAccessTime, &timeRemovePrior);
  954. if (CompareFileTime(&timeLastAccess, &timeRemovePrior) > 0)
  955. {
  956. ReleaseControlHandle(hControl);
  957. continue;
  958. }
  959. }
  960. // check if control is safe to remove
  961. GetControlInfo(hControl, GCI_FILE, NULL, szFile, MAX_PATH);
  962. if (!IsModuleRemovable(szFile))
  963. {
  964. ReleaseControlHandle(hControl);
  965. continue;
  966. }
  967. // put control in a list
  968. if (pHead == NULL)
  969. {
  970. pHead = new HANDLENODE;
  971. pCur = pHead;
  972. }
  973. else
  974. {
  975. pCur->pNext = new HANDLENODE;
  976. pCur = pCur->pNext;
  977. }
  978. if (pCur == NULL)
  979. {
  980. hr = E_OUTOFMEMORY;
  981. goto EXIT_REMOVECONTROLBYLASTACCESSDATE;
  982. }
  983. pCur->pNext = NULL;
  984. pCur->hControl = hControl;
  985. cCnt += 1;
  986. // calculate total size
  987. GetControlInfo(pCur->hControl, GCI_SIZESAVED, &dwSize, NULL, NULL);
  988. dwTotalSize += dwSize;
  989. }
  990. // quit if total size restored is less than the given amount
  991. if (dwTotalSize < dwSizeLimit)
  992. goto EXIT_REMOVECONTROLBYLASTACCESSDATE;
  993. // traverse the list and remove each control
  994. for (pCur = pHead; pCur != NULL; cCnt--)
  995. {
  996. hr = S_OK;
  997. pHead = pHead->pNext;
  998. // call callback function before removing a control
  999. if (pfnDoBefore == NULL || SUCCEEDED(pfnDoBefore(pCur->hControl, cCnt)))
  1000. {
  1001. hr = RemoveControlByHandle(pCur->hControl);
  1002. // call callback function after removing a control, passing it the
  1003. // result of the removal
  1004. if (pfnDoAfter != NULL && FAILED(pfnDoAfter(hr, cCnt - 1)))
  1005. {
  1006. pHead = pCur; // set pHead back to head of list
  1007. goto EXIT_REMOVECONTROLBYLASTACCESSDATE;
  1008. }
  1009. }
  1010. // release memory used by the control handle
  1011. ReleaseControlHandle(pCur->hControl);
  1012. delete pCur;
  1013. pCur = pHead;
  1014. }
  1015. EXIT_REMOVECONTROLBYLASTACCESSDATE:
  1016. FindControlClose(hFind);
  1017. // release memory taken up by the list
  1018. for (pCur = pHead; pCur != NULL; pCur = pHead)
  1019. {
  1020. pHead = pHead->pNext;
  1021. ReleaseControlHandle(pCur->hControl);
  1022. delete pCur;
  1023. }
  1024. return hr;
  1025. }
  1026. HRESULT WINAPI RemoveExpiredControls(DWORD dwFlags, DWORD dwReserved)
  1027. {
  1028. LONG lResult = ERROR_SUCCESS;
  1029. HRESULT hr = S_FALSE;
  1030. HANDLE hFind = NULL, hControl = NULL;
  1031. LPHANDLENODE pHead = NULL, pCur = NULL;
  1032. FILETIME ftNow, ftMinLastAccess, ftLastAccess;
  1033. LARGE_INTEGER liMinLastAccess;
  1034. SYSTEMTIME stNow;
  1035. UINT cCnt = 0;
  1036. GetLocalTime( &stNow );
  1037. SystemTimeToFileTime(&stNow, &ftNow);
  1038. // loop through all controls and put in a list the
  1039. // ones that are accessed before the given date and
  1040. // are safe to uninstall
  1041. lResult = FindFirstControl(hFind, hControl);
  1042. for (;lResult == ERROR_SUCCESS;
  1043. lResult = FindNextControl(hFind, hControl))
  1044. {
  1045. CCacheItem *pci = (CCacheItem *)hControl;
  1046. // Controls must have a last access time of at least ftMinLastAccess or they will
  1047. // expire by default. If they have the Office Auto-expire set, then they may
  1048. // have to pass a higher bar.
  1049. liMinLastAccess.LowPart = ftNow.dwLowDateTime;
  1050. liMinLastAccess.HighPart = ftNow.dwHighDateTime;
  1051. // We add one to GetExpireDays to deal with bug 17151. The last access time
  1052. // returned by the file system is truncated down to 12AM, so we need to
  1053. // expand the expire interval to ensure that this truncation does not cause
  1054. // the control to expire prematurely.
  1055. liMinLastAccess.QuadPart -= ((pci->GetExpireDays()+1) * 864000000000L); //24*3600*10^7
  1056. ftMinLastAccess.dwLowDateTime = liMinLastAccess.LowPart;
  1057. ftMinLastAccess.dwHighDateTime = liMinLastAccess.HighPart;
  1058. GetLastAccessTime(hControl, &ftLastAccess); // ftLastAccess is a local file time
  1059. if (CompareFileTime(&ftLastAccess, &ftMinLastAccess) >= 0)
  1060. {
  1061. ReleaseControlHandle(hControl);
  1062. continue;
  1063. }
  1064. // put control in a list
  1065. if (pHead == NULL)
  1066. {
  1067. pHead = new HANDLENODE;
  1068. pCur = pHead;
  1069. }
  1070. else
  1071. {
  1072. pCur->pNext = new HANDLENODE;
  1073. pCur = pCur->pNext;
  1074. }
  1075. if (pCur == NULL)
  1076. {
  1077. hr = E_OUTOFMEMORY;
  1078. goto cleanup;
  1079. }
  1080. pCur->pNext = NULL;
  1081. pCur->hControl = hControl;
  1082. cCnt += 1;
  1083. }
  1084. // traverse the list and remove each control
  1085. for (pCur = pHead; pCur != NULL; cCnt--)
  1086. {
  1087. hr = S_OK;
  1088. pHead = pHead->pNext;
  1089. hr = RemoveControlByHandle2(pCur->hControl, FALSE, TRUE);
  1090. // release memory used by the control handle
  1091. ReleaseControlHandle(pCur->hControl);
  1092. delete pCur;
  1093. pCur = pHead;
  1094. }
  1095. cleanup:
  1096. FindControlClose(hFind);
  1097. // release memory taken up by the list, if any left
  1098. for (pCur = pHead; pCur != NULL; pCur = pHead)
  1099. {
  1100. pHead = pHead->pNext;
  1101. ReleaseControlHandle(pCur->hControl);
  1102. delete pCur;
  1103. }
  1104. return hr;
  1105. }