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.

1958 lines
62 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Implementation of class CParseInf
  3. //
  4. // CParseInf is created to deal with parsing of an INF file.
  5. #include <ole2.h>
  6. #include "ParseInf.h"
  7. #include "resource.h"
  8. #include "init.h"
  9. #include "global.h"
  10. #include <shlwapi.h>
  11. #include <initguid.h>
  12. #include <pkgguid.h>
  13. #include <cleanoc.h> // for STATUS_CTRL values
  14. #include <mluisupp.h>
  15. #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
  16. static BOOL FGetCLSIDFile( LPTSTR szFile, LPCTSTR szCLSID )
  17. {
  18. BOOL fGotIt = FALSE;
  19. HKEY hkeyClsid;
  20. TCHAR szT[MAX_PATH];
  21. TCHAR *szPath = CatPathStrN( szT, HKCR_CLSID, szCLSID, MAX_PATH );
  22. if ( RegOpenKeyEx( HKEY_CLASSES_ROOT, szPath, 0, KEY_READ, &hkeyClsid ) == ERROR_SUCCESS )
  23. {
  24. DWORD dw;
  25. LRESULT lResult;
  26. // Look for InprocServer[32] or LocalServer[32] key
  27. dw = MAX_PATH;
  28. lResult = RegQueryValue(hkeyClsid, INPROCSERVER32, szT, (PLONG)&dw);
  29. if (lResult != ERROR_SUCCESS)
  30. {
  31. dw = MAX_PATH;
  32. lResult = RegQueryValue(hkeyClsid, LOCALSERVER32, szT, (PLONG)&dw);
  33. }
  34. if (lResult != ERROR_SUCCESS)
  35. {
  36. dw = MAX_PATH;
  37. lResult = RegQueryValue(hkeyClsid, INPROCSERVERX86, szT, (PLONG)&dw);
  38. }
  39. if (lResult != ERROR_SUCCESS)
  40. {
  41. dw = MAX_PATH;
  42. lResult = RegQueryValue(hkeyClsid, LOCALSERVERX86, szT, (PLONG)&dw);
  43. }
  44. if ( lResult == ERROR_SUCCESS )
  45. {
  46. if ( OCCGetLongPathName( szFile, szT, MAX_PATH ) == 0 )
  47. lstrcpy( szFile, szT );
  48. fGotIt = TRUE;
  49. }
  50. RegCloseKey( hkeyClsid );
  51. }
  52. return fGotIt;
  53. }
  54. // constructor
  55. CParseInf::CParseInf()
  56. {
  57. m_pHeadFileList = NULL;
  58. m_pCurFileNode = NULL;
  59. m_pFileRetrievalPtr = NULL;
  60. m_pHeadPackageList = NULL;
  61. m_pCurPackageNode = NULL;
  62. m_pPackageRetrievalPtr = NULL;
  63. m_bIsDistUnit = FALSE;
  64. m_bHasActiveX = FALSE;
  65. m_bHasJava = FALSE;
  66. m_pijpm = NULL;
  67. m_bCoInit = FALSE;
  68. m_dwStatus = STATUS_CTRL_UNKNOWN;
  69. GetDaysBeforeExpireGeneral( &m_cExpireDays );
  70. }
  71. // destructor
  72. CParseInf::~CParseInf()
  73. {
  74. DestroyFileList();
  75. DestroyPackageList();
  76. if ( m_pijpm != NULL )
  77. m_pijpm->Release();
  78. if ( m_bCoInit )
  79. CoUninitialize();
  80. }
  81. // initialization
  82. void CParseInf::Init()
  83. {
  84. m_dwFileSizeSaved = 0;
  85. m_dwTotalFileSize = 0;
  86. m_nTotalFiles = 0;
  87. m_pHeadFileList = m_pCurFileNode = NULL;
  88. m_pHeadPackageList = m_pCurPackageNode = NULL;
  89. lstrcpyn(m_szInf, m_szFileName, ARRAYSIZE(m_szInf));
  90. TCHAR *pCh = ReverseStrchr(m_szInf, '.');
  91. if (pCh != NULL)
  92. *pCh = '\0';
  93. if ( lstrlen(m_szInf) + lstrlen(INF_EXTENSION) < ARRAYSIZE(m_szInf))
  94. lstrcat(m_szInf, INF_EXTENSION);
  95. else
  96. m_szInf[0] = 0; // if can't hold it, we can't hold it.
  97. }
  98. // release memory used by a linked list of files
  99. void CParseInf::DestroyFileList()
  100. {
  101. if (m_pHeadFileList != NULL)
  102. delete m_pHeadFileList;
  103. m_pHeadFileList = m_pCurFileNode = NULL;
  104. }
  105. void CParseInf::DestroyPackageList()
  106. {
  107. if (m_pHeadPackageList != NULL)
  108. delete m_pHeadPackageList;
  109. m_pHeadPackageList = m_pCurPackageNode = NULL;
  110. }
  111. // find inf from cache directory if one with the
  112. // same name as the OCX is not found
  113. HRESULT CParseInf::FindInf(LPTSTR szInf)
  114. {
  115. HRESULT hr = S_OK;
  116. WIN32_FIND_DATA dataFile;
  117. HANDLE h = INVALID_HANDLE_VALUE;
  118. DWORD dwLen = 0;
  119. TCHAR szValueBuf[MAX_PATH];
  120. TCHAR *szOcxFileName = ReverseStrchr(m_szFileName, '\\');
  121. int nCachePathLength = 0, i = 0;
  122. Assert(szOcxFileName != NULL);
  123. szOcxFileName += 1;
  124. Assert (szInf != NULL);
  125. if (szInf == NULL)
  126. goto ExitFindInf;
  127. // search for inf file in two directories. First the dir where the
  128. // OCX is, then the OC cache dir.
  129. for (i = 0; dwLen == 0 && i < 2; i++)
  130. {
  131. if (i == 0)
  132. hr = GetDirectory(GD_EXTRACTDIR, szInf, ARRAYSIZE(szInf), m_szFileName);
  133. else
  134. {
  135. TCHAR szTemp[MAX_PATH];
  136. hr = GetDirectory(GD_CACHEDIR, szTemp, ARRAYSIZE(szTemp));
  137. if (lstrcmpi(szTemp, szInf) == 0)
  138. continue;
  139. lstrcpy(szInf, szTemp);
  140. }
  141. if (FAILED(hr))
  142. goto ExitFindInf;
  143. lstrcat(szInf, TEXT("\\"));
  144. nCachePathLength = lstrlen(szInf);
  145. lstrcat(szInf, TEXT("*"));
  146. lstrcat(szInf, INF_EXTENSION);
  147. h = FindFirstFile(szInf, &dataFile);
  148. if (h == INVALID_HANDLE_VALUE)
  149. {
  150. goto ExitFindInf;
  151. }
  152. // find an inf file with a section in [Add.Code] dedicated
  153. // to the OCX file in question
  154. do {
  155. szInf[nCachePathLength] = '\0';
  156. lstrcat(szInf, (LPCTSTR)dataFile.cFileName);
  157. dwLen = GetPrivateProfileString(
  158. KEY_ADDCODE,
  159. szOcxFileName,
  160. DEFAULT_VALUE,
  161. szValueBuf,
  162. MAX_PATH,
  163. szInf);
  164. } while(dwLen == 0 && FindNextFile(h, &dataFile));
  165. }
  166. hr = (dwLen != 0 ? hr : S_FALSE);
  167. ExitFindInf:
  168. if (h != INVALID_HANDLE_VALUE)
  169. FindClose(h);
  170. if (hr != S_OK)
  171. szInf[0] = '\0';
  172. return hr;
  173. }
  174. // initiate parsing of INF file
  175. // szCLSID -- address to a buffer storing CLSID of control
  176. // szOCXFileName -- full path and name (ie. long file name) of OCX file
  177. HRESULT CParseInf::DoParse(
  178. LPCTSTR szOCXFileName,
  179. LPCTSTR szCLSID)
  180. {
  181. Assert(szOCXFileName != NULL);
  182. Assert(szCLSID != NULL);
  183. HRESULT hr = S_OK;
  184. const TCHAR *pszPath = NULL;
  185. TCHAR szFileName[MAX_PATH];
  186. DWORD dwFileSize = 0;
  187. if ( FGetCLSIDFile( szFileName, szCLSID ) &&
  188. lstrcmpi( szFileName, szOCXFileName ) != 0 )
  189. m_dwStatus = STATUS_CTRL_UNPLUGGED;
  190. // If DoParse was called, we are assumed to be a legacy control and not
  191. // a distribution unit (subsequent call to DoParseDU will change the
  192. // status). This information is required for control removal purposes.
  193. m_bIsDistUnit = FALSE;
  194. m_bHasActiveX = TRUE; // all legacy controls are ActiveX
  195. // initialization
  196. if ( OCCGetLongPathName(m_szFileName, szOCXFileName, MAX_PATH) == 0 )
  197. lstrcpyn( m_szFileName, szOCXFileName, MAX_PATH );
  198. lstrcpyn(m_szCLSID, szCLSID, MAX_CLSID_LEN);
  199. DestroyFileList();
  200. Init();
  201. BOOL bOCXRemovable = IsModuleRemovable(m_szFileName);
  202. // test INF file existance, if not, try to find one in OC cache dir.
  203. if (!FileExist(m_szInf))
  204. {
  205. if (!ReadInfFileNameFromRegistry(m_szCLSID, m_szInf, MAX_PATH))
  206. {
  207. FindInf(m_szInf);
  208. // record inf file name into the registry
  209. WriteInfFileNameToRegistry(
  210. m_szCLSID,
  211. (m_szInf[0] == '\0' ? NULL : m_szInf));
  212. }
  213. }
  214. // enumerate files assocated with a particular OCX
  215. if (FAILED(hr = EnumSections()))
  216. goto ExitDoParse;
  217. // S_FALSE is returned when an ocx has no inf file
  218. if (hr == S_FALSE)
  219. {
  220. m_nTotalFiles = 1;
  221. if (FAILED(GetSizeOfFile(m_szFileName, &m_dwFileSizeSaved)))
  222. {
  223. m_dwFileSizeSaved = 0;
  224. m_dwTotalFileSize = 0;
  225. }
  226. else
  227. {
  228. m_dwTotalFileSize = m_dwFileSizeSaved;
  229. }
  230. hr = S_OK;
  231. if ( !PathFileExists( m_szFileName ) )
  232. m_dwStatus = STATUS_CTRL_DAMAGED;
  233. else
  234. m_dwStatus = STATUS_CTRL_INSTALLED;
  235. goto ExitDoParse;
  236. }
  237. // OCX has an corresponding INF file.
  238. // Loop through the list of assocated files to dig out info for each
  239. // from their corresponding section in the INF file
  240. for (m_pCurFileNode = m_pHeadFileList;
  241. m_pCurFileNode != NULL;
  242. m_pCurFileNode = m_pCurFileNode->GetNextFileNode(), hr = S_OK)
  243. {
  244. // if m_pCurFileNode->GetNextFileNode() == NULL => it's the inf file itself,
  245. // which does not need to be processed.
  246. if (m_pCurFileNode->GetNextFileNode() != NULL)
  247. {
  248. pszPath = m_pCurFileNode->GetPath();
  249. Assert(pszPath != NULL);
  250. if (pszPath == NULL)
  251. {
  252. hr = E_UNEXPECTED;
  253. goto ExitDoParse;
  254. }
  255. CatPathStrN( szFileName, pszPath, m_pCurFileNode->GetName(), ARRAYSIZE(szFileName));
  256. }
  257. else
  258. {
  259. lstrcpyn(szFileName, m_szInf, ARRAYSIZE(szFileName));
  260. pszPath = NULL;
  261. }
  262. // hr might either be S_OK or S_FALSE
  263. // S_OK means file can be removed as it has a SharedDlls count of 1
  264. // S_FALSE if the count is greater than 1
  265. // calculate total num of files and their sizes
  266. if (SUCCEEDED(hr = GetSizeOfFile(szFileName, &dwFileSize)))
  267. {
  268. if (pszPath == NULL ||
  269. IsModuleRemovable(szFileName) ||
  270. lstrcmpi(szFileName, m_szFileName) == 0)
  271. {
  272. m_dwFileSizeSaved += dwFileSize;
  273. }
  274. m_dwTotalFileSize += dwFileSize;
  275. } else
  276. m_dwStatus = STATUS_CTRL_DAMAGED; // failure to get size indicative of missing file.
  277. m_nTotalFiles += 1;
  278. }
  279. // if we didn't detect a problem, flag the control as installed.
  280. if ( m_dwStatus == STATUS_CTRL_UNKNOWN )
  281. m_dwStatus = STATUS_CTRL_INSTALLED;
  282. ExitDoParse:
  283. return hr;
  284. }
  285. HRESULT CParseInf::BuildDUFileList( HKEY hKeyDU )
  286. {
  287. HRESULT hr = S_OK;
  288. LRESULT lResult;
  289. HKEY hkeyFiles;
  290. TCHAR szDUFileName[MAX_PATH + 1];
  291. DWORD dwStrSize = MAX_PATH;
  292. int cFilesEnum = 0;
  293. lResult = RegOpenKeyEx(hKeyDU, REGSTR_DU_CONTAINS_FILES, 0,
  294. KEY_READ, &hkeyFiles);
  295. if ( lResult != ERROR_SUCCESS ) // if no files, maybe there's Java
  296. return hr;
  297. while ((lResult = RegEnumValue(hkeyFiles, cFilesEnum++, szDUFileName,
  298. &dwStrSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
  299. {
  300. TCHAR szPath[MAX_PATH + 1];
  301. CFileNode *pFileNode;
  302. lstrcpyn(szPath, szDUFileName, MAX_PATH);
  303. TCHAR *szFName = ReverseStrchr(szPath, '\\');
  304. Assert(szFName != NULL);
  305. // long ago and far away, in the IE4, PP1-2 timeframe, there was a horrible
  306. // bug that corrupted these entries on Memphis and NT5. We suspect that GetLongPathName
  307. // was doing something wrong for code download, but repro scenarios were not
  308. // to be found. Anywho, the damaged registries are out there, so we need to
  309. // cope with them more gracefully than faulting at the *szFName = NULL;
  310. if ( szFName == NULL )
  311. continue;
  312. *szFName = NULL;
  313. szFName++;
  314. pFileNode = new CFileNode(szFName, "", szPath);
  315. if (pFileNode == NULL)
  316. {
  317. hr = E_OUTOFMEMORY;
  318. break;
  319. }
  320. // create and add node to list
  321. if (m_pHeadFileList == NULL)
  322. {
  323. m_pHeadFileList = pFileNode;
  324. m_pCurFileNode = m_pHeadFileList;
  325. }
  326. else
  327. {
  328. hr = m_pCurFileNode->Insert(pFileNode);
  329. m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
  330. }
  331. dwStrSize = MAX_PATH;
  332. }
  333. RegCloseKey( hkeyFiles );
  334. return hr;
  335. }
  336. HRESULT CParseInf::BuildDUPackageList( HKEY hKeyDU )
  337. {
  338. HRESULT hr = S_OK;
  339. LRESULT lResult;
  340. HKEY hkeyJava;
  341. ICreateJavaPackageMgr *picjpm;
  342. DestroyPackageList();
  343. lResult = RegOpenKeyEx(hKeyDU, REGSTR_DU_CONTAINS_JAVA, 0,
  344. KEY_READ, &hkeyJava);
  345. if ( lResult != ERROR_SUCCESS ) // it's OK if there's no Java
  346. return hr;
  347. if ( !m_bCoInit )
  348. m_bCoInit = SUCCEEDED(hr = CoInitialize(NULL));
  349. if ( m_bCoInit )
  350. {
  351. hr=CoCreateInstance(CLSID_JavaPackageManager,NULL,CLSCTX_INPROC_SERVER,
  352. IID_ICreateJavaPackageMgr,(LPVOID *) &picjpm);
  353. if (SUCCEEDED(hr))
  354. {
  355. hr = picjpm->GetPackageManager(&m_pijpm);
  356. picjpm->Release();
  357. }
  358. }
  359. if (FAILED(hr))
  360. return S_OK; // hr; // quietly fail until we're sure the JavaVM with package manager support is in the build.
  361. // list the packages under Contains/Java - these are in the gobal namespace
  362. hr = BuildNamespacePackageList(hkeyJava, "");
  363. // add packages for each namespace key under Contains\Java
  364. if ( SUCCEEDED(hr) )
  365. {
  366. DWORD dwIndex;
  367. TCHAR szNamespace[MAX_PATH + 1]; //
  368. DWORD dwStrSize;
  369. for ( dwIndex = 0, dwStrSize = MAX_PATH;
  370. RegEnumKey( hkeyJava, dwIndex, szNamespace, dwStrSize ) == ERROR_SUCCESS &&
  371. SUCCEEDED(hr);
  372. dwIndex++, dwStrSize = MAX_PATH )
  373. {
  374. HKEY hkeyNamespace;
  375. lResult = RegOpenKeyEx(hkeyJava, szNamespace, 0, KEY_READ, &hkeyNamespace);
  376. if ( lResult == ERROR_SUCCESS )
  377. {
  378. hr = BuildNamespacePackageList(hkeyNamespace, szNamespace );
  379. RegCloseKey( hkeyNamespace );
  380. }
  381. else
  382. {
  383. hr = HRESULT_FROM_WIN32(lResult);
  384. break;
  385. }
  386. }
  387. }
  388. RegCloseKey( hkeyJava );
  389. m_bHasJava = m_pHeadPackageList != NULL;
  390. return hr;
  391. }
  392. HRESULT CParseInf::BuildNamespacePackageList( HKEY hKeyNS, LPCTSTR szNamespace )
  393. {
  394. HRESULT hr = S_OK;
  395. LRESULT lResult;
  396. int cPackagesEnum = 0;
  397. TCHAR szDUPackageName[MAX_PATH + 1];
  398. DWORD dwStrSize = MAX_PATH;
  399. BOOL fIsSystemClass = FALSE;
  400. while ((lResult = RegEnumValue(hKeyNS, cPackagesEnum++, szDUPackageName,
  401. &dwStrSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
  402. {
  403. IJavaPackage *pijp;
  404. #ifndef UNICODE
  405. MAKE_WIDEPTR_FROMANSI(swzPackage, szDUPackageName );
  406. MAKE_WIDEPTR_FROMANSI(swzNamespace, szNamespace );
  407. #else
  408. OLESTR swzPackage = szDUPackageName;
  409. OLESTR swzNamespace = szNamespace;
  410. #endif
  411. hr = m_pijpm->GetPackage( swzPackage,
  412. ((*szNamespace == '\0')? NULL : swzNamespace),
  413. &pijp );
  414. if ( SUCCEEDED(hr) )
  415. {
  416. BSTR bstrPath;
  417. hr = pijp->GetFilePath( &bstrPath );
  418. if ( SUCCEEDED(hr) ) {
  419. CPackageNode *pPackageNode;
  420. pPackageNode = new CPackageNode(szDUPackageName, szNamespace);
  421. if (pPackageNode == NULL)
  422. {
  423. hr = E_OUTOFMEMORY;
  424. pijp->Release();
  425. break;
  426. }
  427. #ifndef UNICODE
  428. MAKE_ANSIPTR_FROMWIDE(szPath, bstrPath );
  429. #else
  430. TCHAR *szPath = bstrPath;
  431. #endif
  432. pPackageNode->SetPath( szPath );
  433. pijp->IsSystemClass(&fIsSystemClass);
  434. pPackageNode->SetIsSystemClass(fIsSystemClass);
  435. if (m_pHeadPackageList == NULL)
  436. {
  437. m_pHeadPackageList = pPackageNode;
  438. m_pCurPackageNode = m_pHeadPackageList;
  439. }
  440. else
  441. {
  442. hr = m_pCurPackageNode->Insert(pPackageNode);
  443. m_pCurPackageNode = m_pCurPackageNode->GetNextPackageNode();
  444. }
  445. SysFreeString( bstrPath );
  446. pijp->Release(); // we're done with the package
  447. }
  448. }
  449. else
  450. {
  451. m_dwStatus = STATUS_CTRL_DAMAGED;
  452. hr = S_OK; // don't barf if this doesn't work, some villain might have uninstalled it
  453. }
  454. dwStrSize = MAX_PATH;
  455. }
  456. return hr;
  457. }
  458. HRESULT CParseInf::DoParseDU(LPCTSTR szOCXFileName, LPCTSTR szCLSID)
  459. {
  460. HRESULT hr = S_OK;
  461. TCHAR szFileName[MAX_PATH];
  462. TCHAR szDUSvrName[MAX_PATH];
  463. const TCHAR *pszSvrFile = NULL;
  464. DWORD dwFileSize = 0;
  465. HKEY hKeyFiles = 0;
  466. HKEY hKeyDU = 0;
  467. HKEY hKeyDLInfo = 0;
  468. TCHAR szDistUnit[MAX_REGPATH_LEN];
  469. HRESULT lResult;
  470. CFileNode *pFileNode = NULL;
  471. DWORD dwExpire;
  472. DWORD dw;
  473. Assert(szCLSID != NULL);
  474. // Since this function was called, we must be a distribution unit.
  475. // Set a member flag so that all other member functions realize that
  476. // we are really part of a DU now.
  477. m_bIsDistUnit = TRUE;
  478. // initialization
  479. if ( szOCXFileName != NULL )
  480. lstrcpyn(m_szFileName, szOCXFileName, ARRAYSIZE(m_szFileName));
  481. lstrcpyn(m_szCLSID, szCLSID, ARRAYSIZE(m_szCLSID));
  482. Init();
  483. // Add files from ...\Distribution Units\{Name}\Contains\Files
  484. CatPathStrN( szDistUnit, REGSTR_PATH_DIST_UNITS, szCLSID, ARRAYSIZE(szDistUnit));
  485. lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szDistUnit, 0, KEY_READ,
  486. &hKeyDU);
  487. if (lResult != ERROR_SUCCESS)
  488. {
  489. hr = E_FAIL;
  490. goto ExitDoParseDU;
  491. }
  492. hr = BuildDUFileList( hKeyDU );
  493. if (FAILED(hr))
  494. {
  495. goto ExitDoParseDU;
  496. }
  497. hr = BuildDUPackageList( hKeyDU );
  498. if (FAILED(hr))
  499. {
  500. goto ExitDoParseDU;
  501. }
  502. // Now add the OSD and INF files
  503. lResult = RegOpenKeyEx(hKeyDU, REGSTR_DOWNLOAD_INFORMATION, 0,
  504. KEY_READ, &hKeyDLInfo);
  505. if (lResult == ERROR_SUCCESS)
  506. {
  507. TCHAR *pFileName = NULL;
  508. TCHAR szBuffer[MAX_PATH + 1];
  509. dw = MAX_PATH;
  510. lResult = RegQueryValueEx(hKeyDLInfo, REGSTR_VALUE_INF, NULL, NULL,
  511. (unsigned char*)szBuffer, &dw);
  512. if (lResult == ERROR_SUCCESS)
  513. {
  514. pFileName = ReverseStrchr(szBuffer, '\\');
  515. if (pFileName != NULL)
  516. {
  517. pFileName++;
  518. // set INF member variable
  519. lstrcpyn(m_szInf, szBuffer, ARRAYSIZE(m_szInf));
  520. pFileNode = new CFileNode(szBuffer, "", NULL);
  521. if (pFileNode == NULL)
  522. {
  523. hr = E_OUTOFMEMORY;
  524. goto ExitDoParseDU;
  525. }
  526. // create and add node to list
  527. if (m_pHeadFileList == NULL)
  528. {
  529. m_pHeadFileList = pFileNode;
  530. m_pCurFileNode = m_pHeadFileList;
  531. }
  532. else
  533. {
  534. hr = m_pCurFileNode->Insert(pFileNode);
  535. m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
  536. }
  537. }
  538. }
  539. pFileName = NULL;
  540. dw = MAX_PATH;
  541. lResult = RegQueryValueEx(hKeyDLInfo, REGSTR_VALUE_OSD, NULL, NULL,
  542. (unsigned char*)szBuffer, &dw);
  543. if (lResult == ERROR_SUCCESS)
  544. {
  545. pFileName = ReverseStrchr(szBuffer, '\\');
  546. if (pFileName != NULL)
  547. {
  548. pFileName++;
  549. pFileNode = new CFileNode(szBuffer, "", NULL);
  550. // create and add node to list
  551. if (m_pHeadFileList == NULL)
  552. {
  553. m_pHeadFileList = pFileNode;
  554. m_pCurFileNode = m_pHeadFileList;
  555. }
  556. else
  557. {
  558. hr = m_pCurFileNode->Insert(pFileNode);
  559. m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
  560. }
  561. }
  562. }
  563. }
  564. // See if there's an Expire value, and if so, override the default/general expire.
  565. dw = sizeof(DWORD);
  566. dwExpire = 0;
  567. if ( RegQueryValueEx(hKeyDU, REGSTR_VALUE_EXPIRE, NULL, NULL, (LPBYTE)&dwExpire, &dw) == ERROR_SUCCESS )
  568. {
  569. if ( dwExpire )
  570. m_cExpireDays = dwExpire;
  571. else
  572. GetDaysBeforeExpireAuto(&m_cExpireDays);
  573. }
  574. // Find out where COM thinks our CLSID is, and what the server name is.
  575. if ( FGetCLSIDFile( szDUSvrName, szCLSID ) )
  576. {
  577. m_bHasActiveX = TRUE;
  578. pszSvrFile = PathFindFileName(szDUSvrName);
  579. }
  580. else
  581. szDUSvrName[0] = '\0';
  582. for (m_pCurFileNode = m_pHeadFileList;
  583. m_pCurFileNode != NULL;
  584. m_pCurFileNode = m_pCurFileNode->GetNextFileNode(), hr = S_OK)
  585. {
  586. const TCHAR *pszPath = m_pCurFileNode->GetPath();
  587. if (pszPath != NULL)
  588. {
  589. CatPathStrN( szFileName, m_pCurFileNode->GetPath(), m_pCurFileNode->GetName(), ARRAYSIZE(szFileName));
  590. }
  591. else
  592. {
  593. lstrcpyn(szFileName, m_pCurFileNode->GetName(),ARRAYSIZE(szFileName));
  594. }
  595. if (SUCCEEDED(hr = GetSizeOfFile(szFileName, &dwFileSize)))
  596. {
  597. if (pszPath == NULL ||
  598. IsModuleRemovable(szFileName) ||
  599. lstrcmpi(szFileName, m_szFileName) == 0)
  600. {
  601. m_dwFileSizeSaved += dwFileSize;
  602. }
  603. // only play with the status if we haven't already flagged the installation
  604. // as damaged and we're looking at the the file that should be the host for
  605. // our control, if any.
  606. if ( m_dwStatus != STATUS_CTRL_DAMAGED && pszSvrFile != NULL &&
  607. lstrcmpi( pszSvrFile, m_pCurFileNode->GetName() ) == 0 )
  608. {
  609. TCHAR szDUSvrNameSPN[MAX_PATH];
  610. TCHAR szFileNameSPN[MAX_PATH];
  611. GetShortPathName(szDUSvrName, szDUSvrNameSPN, MAX_PATH);
  612. GetShortPathName(szFileName, szFileNameSPN, MAX_PATH);
  613. if ( lstrcmpi( szDUSvrNameSPN, szFileNameSPN ) == 0 )
  614. m_dwStatus = STATUS_CTRL_INSTALLED; // no, we're not unplugged
  615. else // server and our file are in different directories - unplugged scenario
  616. m_dwStatus = STATUS_CTRL_UNPLUGGED;
  617. }
  618. m_dwTotalFileSize += dwFileSize;
  619. } else if ( !PathFileExists( szFileName ) ) // if a DU file is missing, then the installation is damaged.
  620. m_dwStatus = STATUS_CTRL_DAMAGED;
  621. m_nTotalFiles += 1;
  622. }
  623. // If we're still unsure, and there are packages, then this is a pure Java
  624. // DU and will say we're installed unless a check of the package files indicates otherwise.
  625. if ( m_pHeadPackageList != NULL && m_dwStatus == STATUS_CTRL_UNKNOWN )
  626. m_dwStatus = STATUS_CTRL_INSTALLED;
  627. // Accumulate package sizes and such into our running total
  628. for (m_pCurPackageNode = m_pHeadPackageList;
  629. m_pCurPackageNode != NULL;
  630. m_pCurPackageNode = m_pCurPackageNode->GetNextPackageNode(), hr = S_OK)
  631. {
  632. // the files can hold more than one of our packages, so only add a package
  633. // path file to the totals if we haven't already counted it.
  634. // N^2 to be sure, but the numbers will be small.
  635. CPackageNode *ppn;
  636. LPCTSTR szPackagePath = m_pCurPackageNode->GetPath();
  637. BOOL bAlreadySeen = FALSE;
  638. for ( ppn = m_pHeadPackageList;
  639. ppn != m_pCurPackageNode && !bAlreadySeen;
  640. ppn = ppn->GetNextPackageNode() )
  641. bAlreadySeen = lstrcmp( szPackagePath, ppn->GetPath() ) == 0;
  642. if ( bAlreadySeen )
  643. continue;
  644. // Must be a new file,
  645. if ( SUCCEEDED(GetSizeOfFile(szPackagePath, &dwFileSize)) )
  646. {
  647. m_dwFileSizeSaved += dwFileSize;
  648. m_dwTotalFileSize += dwFileSize;
  649. }
  650. else
  651. m_dwStatus = STATUS_CTRL_DAMAGED;
  652. // m_nTotalFiles += 1; don't count these files, or the dependency file list will have a bunch of blank entries
  653. }
  654. // Some DUs, like SportsZone or Shockwave, have no Contains subkeys.
  655. // If status is still unknown here, but the server is in place, consider it
  656. // installed.
  657. if ( m_dwStatus == STATUS_CTRL_UNKNOWN && PathFileExists( szDUSvrName ) )
  658. m_dwStatus = STATUS_CTRL_INSTALLED;
  659. ExitDoParseDU:
  660. if (hKeyDU)
  661. {
  662. RegCloseKey(hKeyDU);
  663. }
  664. if (hKeyDLInfo)
  665. {
  666. RegCloseKey(hKeyDLInfo);
  667. }
  668. return hr;
  669. }
  670. // ---------------------------------------------------------------------------
  671. // CParseInf::IsSectionInINF
  672. // Checks if a section is in the INF
  673. // returns:
  674. // S_OK: lpCurCode has the satellite binary name
  675. // S_FALSE: ignore this code and use default resources in main dll
  676. // E_XXX: any other error
  677. BOOL
  678. CParseInf::IsSectionInINF(
  679. LPCSTR lpCurCode)
  680. {
  681. const char *szDefault = "";
  682. DWORD len;
  683. #define FAKE_BUF_SIZE 3
  684. char szBuf[FAKE_BUF_SIZE];
  685. len = GetPrivateProfileString(lpCurCode, NULL, szDefault,
  686. szBuf, FAKE_BUF_SIZE, m_szInf);
  687. if (len == (FAKE_BUF_SIZE - 2)) { // returns Out Of Buffer Space?
  688. // yes, section found
  689. return TRUE;
  690. } else {
  691. return FALSE;
  692. }
  693. }
  694. // loop through the keys in [Add.Code} section and enumerate the
  695. // files and their corresponding sections.
  696. HRESULT CParseInf::HandleSatellites(LPCTSTR pszFileName)
  697. {
  698. HRESULT hr = S_OK;
  699. // BEGIN NOTE: add vars and values in matching order
  700. // add a var by adding a new define VAR_NEW_VAR = NUM_VARS++
  701. const char *szVars[] = {
  702. #define VAR_LANG 0 // expands to 3 letter lang code based on lcid
  703. "%LANG%",
  704. #define NUM_VARS 1
  705. ""
  706. };
  707. const char *szValues[NUM_VARS + 1];
  708. szValues[VAR_LANG] = "***"; // unint magic
  709. szValues[NUM_VARS] = NULL;
  710. // END NOTE: add vars and values in matching order
  711. // look for and substitute variables like %EXTRACT_DIR%
  712. // and expand out the command line
  713. TCHAR szSectionName[MAX_PATH];
  714. TCHAR szSectionNameCopy[MAX_PATH];
  715. hr = ExpandCommandLine(pszFileName, szSectionName, MAX_PATH, szVars, szValues);
  716. if (hr != S_OK)
  717. return hr; // no vars to expand ignore section
  718. lstrcpy(szSectionNameCopy, szSectionName); // preserve
  719. // OK, this is a satellite DLL. Now we need to find the section(s) that
  720. // got installed.
  721. // we first enum the registry's Module Usage looking for DLLs that were
  722. // installed by (or used by) this CLSID. For each of those we need to
  723. // check if the base filename matches the pattern of the section,
  724. // if it does then we process those sections
  725. DWORD iSubKey = 0;
  726. TCHAR szModName[MAX_PATH];
  727. while ( SUCCEEDED(hr = FindDLLInModuleUsage( szModName, m_szCLSID, iSubKey)) ) {
  728. if (PatternMatch(szModName, szSectionName) &&
  729. IsSectionInINF(szSectionName) ) {
  730. // create new node
  731. CFileNode *pFileNode = new CFileNode(szSectionName, szSectionName);
  732. if (pFileNode == NULL)
  733. {
  734. hr = E_OUTOFMEMORY;
  735. goto Exit;
  736. }
  737. // don't insert file into list if it's path cannot be found
  738. if (FAILED(GetFilePath(pFileNode)))
  739. {
  740. delete pFileNode;
  741. continue;
  742. }
  743. // create and add node to list
  744. if (m_pHeadFileList == NULL)
  745. {
  746. m_pHeadFileList = pFileNode;
  747. m_pCurFileNode = m_pHeadFileList;
  748. }
  749. else if (SUCCEEDED(hr = m_pCurFileNode->Insert(pFileNode)))
  750. {
  751. m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
  752. }
  753. else
  754. {
  755. goto Exit;
  756. }
  757. lstrcpy(szSectionName, szSectionNameCopy); // restore
  758. }
  759. }
  760. if ( hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
  761. hr = S_OK;
  762. }
  763. Exit:
  764. return hr;
  765. }
  766. // loop through the keys in [Add.Code} section and enumerate the
  767. // files and their corresponding sections.
  768. HRESULT CParseInf::EnumSections()
  769. {
  770. HRESULT hr = S_OK;
  771. TCHAR szSectionBuffer[MAX_INF_SECTION_SIZE];
  772. TCHAR szValueBuffer[MAX_PATH];
  773. TCHAR *pszFileName = NULL;
  774. CFileNode *pFileNode = NULL;
  775. DWORD dwLen = GetPrivateProfileString(
  776. KEY_ADDCODE,
  777. NULL,
  778. DEFAULT_VALUE,
  779. szSectionBuffer,
  780. MAX_INF_SECTION_SIZE,
  781. m_szInf);
  782. if (dwLen == 0)
  783. {
  784. // if inf file or [Add.Code] section
  785. // does not exist, just delete the OCX
  786. Assert (m_pHeadFileList == NULL);
  787. // separate file name from its directory
  788. Assert( lstrlen(m_szFileName) < ARRAYSIZE(szValueBuffer) );
  789. lstrcpy(szValueBuffer, m_szFileName);
  790. TCHAR *szName = ReverseStrchr(szValueBuffer, '\\');
  791. Assert (szName != NULL);
  792. if (szName == NULL)
  793. {
  794. hr = E_UNEXPECTED;
  795. goto ExitEnumSections;
  796. }
  797. // create a node of the OCX and put it in a linked list
  798. m_pHeadFileList = new CFileNode(szName + 1, DEFAULT_VALUE);
  799. if (m_pHeadFileList == NULL)
  800. {
  801. hr = E_OUTOFMEMORY;
  802. goto ExitEnumSections;
  803. }
  804. m_pCurFileNode = m_pHeadFileList;
  805. *szName = '\0';
  806. if (FAILED(hr = m_pHeadFileList->SetPath(szValueBuffer)))
  807. {
  808. goto ExitEnumSections;
  809. }
  810. hr = S_FALSE;
  811. goto ExitEnumSections;
  812. }
  813. // For OCX's that have an INF file and [Add.Code] section, loop
  814. // through the section to get filenames and section names. Store
  815. // each file and its section in a node and add the node to a
  816. // linked list
  817. for (pszFileName = szSectionBuffer;
  818. pszFileName[0] != '\0';
  819. pszFileName += lstrlen(pszFileName) + 1)
  820. {
  821. dwLen = GetPrivateProfileString(
  822. KEY_ADDCODE,
  823. pszFileName,
  824. DEFAULT_VALUE,
  825. szValueBuffer,
  826. MAX_PATH,
  827. m_szInf);
  828. // skip the file if no section is specified for it
  829. if (dwLen == 0) {
  830. continue;
  831. }
  832. if (StrChr(pszFileName, '%')) {
  833. // if section not found and it contains a %
  834. // could be a variable like %LANG% that gets
  835. // substituted to install satellite DLLs
  836. // check if it has any vars that we know about
  837. // and expand them and add filenodes if reqd.
  838. if (HandleSatellites(pszFileName) == S_OK) {
  839. // if this expanded to a satellite dll name then
  840. // we would have already added that
  841. // as a node in HandleSatellites
  842. continue;
  843. }
  844. }
  845. // create new node
  846. pFileNode = new CFileNode(pszFileName, szValueBuffer);
  847. if (pFileNode == NULL)
  848. {
  849. hr = E_OUTOFMEMORY;
  850. goto ExitEnumSections;
  851. }
  852. // don't insert file into list if it's path cannot be found
  853. if (FAILED(GetFilePath(pFileNode)))
  854. {
  855. delete pFileNode;
  856. continue;
  857. }
  858. // create and add node to list
  859. if (m_pHeadFileList == NULL)
  860. {
  861. m_pHeadFileList = pFileNode;
  862. m_pCurFileNode = m_pHeadFileList;
  863. }
  864. else if (SUCCEEDED(hr = m_pCurFileNode->Insert(pFileNode)))
  865. {
  866. m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
  867. }
  868. else
  869. {
  870. goto ExitEnumSections;
  871. }
  872. }
  873. // include inf file into file list
  874. if (m_pHeadFileList && m_pCurFileNode)
  875. {
  876. hr = m_pCurFileNode->Insert(new CFileNode(m_szInf, DEFAULT_VALUE));
  877. if (SUCCEEDED(hr))
  878. m_pCurFileNode = m_pCurFileNode->GetNextFileNode();
  879. }
  880. ExitEnumSections:
  881. return hr;
  882. }
  883. // Loop through all the sections in [Setup Hooks]. For each
  884. // section, call ParseUninstallSection to find its UNINSTALL section
  885. // and execute it.
  886. HRESULT CParseInf::ParseSetupHook()
  887. {
  888. HRESULT hr = S_FALSE; // Return S_FALSE if we don't run into any errors, but also don't do any work.
  889. TCHAR szSectionBuffer[MAX_INF_SECTION_SIZE];
  890. TCHAR szSection[MAX_PATH];
  891. TCHAR *pszKey = NULL;
  892. DWORD dwLen = GetPrivateProfileString(
  893. KEY_SETUPHOOK,
  894. NULL,
  895. DEFAULT_VALUE,
  896. szSectionBuffer,
  897. MAX_INF_SECTION_SIZE,
  898. m_szInf);
  899. // no Setup Hook section found
  900. if (dwLen == 0)
  901. goto EXITPARSESETUPHOOK;
  902. for (pszKey = szSectionBuffer;
  903. pszKey[0] != '\0';
  904. pszKey += lstrlen(pszKey) + 1)
  905. {
  906. // For each key, get the section and run the section with RunSetupCommand
  907. dwLen = GetPrivateProfileString(
  908. KEY_SETUPHOOK,
  909. pszKey,
  910. DEFAULT_VALUE,
  911. szSection,
  912. MAX_PATH,
  913. m_szInf);
  914. if (dwLen == 0)
  915. continue;
  916. hr = ParseUninstallSection(szSection);
  917. if (FAILED(hr))
  918. goto EXITPARSESETUPHOOK;
  919. }
  920. EXITPARSESETUPHOOK:
  921. return hr;
  922. }
  923. // Go to each file's section, find its conditional hook section, then
  924. // call ParseUninstallSection to execute the conditional hook section.
  925. HRESULT CParseInf::ParseConditionalHook()
  926. {
  927. HRESULT hr = S_FALSE; // Return S_FALSE if we don't run into any errors, but also don't do any work.
  928. TCHAR szHookSection[MAX_PATH];
  929. const TCHAR *pszSection = NULL;
  930. CFileNode *pNode = NULL;
  931. if (m_pHeadFileList == NULL)
  932. {
  933. hr = S_FALSE;
  934. goto EXITPARSECONDITIONALHOOK;
  935. }
  936. pNode = m_pHeadFileList;
  937. for (pNode = m_pHeadFileList; pNode != NULL; pNode = pNode->GetNextFileNode())
  938. {
  939. pszSection = pNode->GetSection();
  940. if (pszSection == NULL)
  941. continue;
  942. if (GetPrivateProfileString(
  943. pszSection,
  944. KEY_HOOK,
  945. DEFAULT_VALUE,
  946. szHookSection,
  947. MAX_PATH,
  948. m_szInf) == 0)
  949. continue;
  950. hr = ParseUninstallSection(szHookSection);
  951. if (FAILED(hr))
  952. goto EXITPARSECONDITIONALHOOK;
  953. }
  954. EXITPARSECONDITIONALHOOK:
  955. return hr;
  956. }
  957. // Given a file section, find its UNINSTALL section, go to the
  958. // section and executes the commands there
  959. HRESULT CParseInf::ParseUninstallSection(LPCTSTR lpszSection)
  960. {
  961. HRESULT hr = S_OK;
  962. TCHAR szUninstallSection[MAX_PATH];
  963. TCHAR szBuf[MAX_PATH];
  964. TCHAR szInfSection[MAX_PATH];
  965. TCHAR szCacheDir[MAX_PATH];
  966. HANDLE hExe = INVALID_HANDLE_VALUE;
  967. HINSTANCE hInst = NULL;
  968. // check for "UNINSTALL" key
  969. DWORD dwLen = GetPrivateProfileString(
  970. lpszSection,
  971. KEY_UNINSTALL,
  972. DEFAULT_VALUE,
  973. szUninstallSection,
  974. ARRAYSIZE(szUninstallSection),
  975. m_szInf);
  976. // UNINSTALL key not found, quit.
  977. if (dwLen == 0)
  978. {
  979. return S_FALSE;
  980. }
  981. // There are 4 possible combinations inside the uninstall section
  982. // 1) Both inffile and infsection are specified -> simply to go those
  983. // 2) Only inffile is given -> go to inffile and do DefaultInstall
  984. // 3) Only infsection is given -> do infsection in this inf file
  985. // 4) Nothing is specified -> simply do this section
  986. GetDirectory(GD_EXTRACTDIR, szCacheDir, ARRAYSIZE(szCacheDir), m_szFileName);
  987. lstrcpyn(szBuf, szCacheDir, MAX_PATH - 1);
  988. lstrcat(szBuf, TEXT("\\"));
  989. int cch = lstrlen(szBuf);
  990. dwLen = GetPrivateProfileString(
  991. szUninstallSection,
  992. KEY_INFFILE,
  993. DEFAULT_VALUE,
  994. szBuf + cch,
  995. MAX_PATH - cch,
  996. m_szInf);
  997. if (dwLen == 0)
  998. {
  999. szBuf[0] = '\0';
  1000. }
  1001. // get inf section
  1002. dwLen = GetPrivateProfileString(
  1003. szUninstallSection,
  1004. KEY_INFSECTION,
  1005. DEFAULT_VALUE,
  1006. szInfSection,
  1007. ARRAYSIZE(szInfSection),
  1008. m_szInf);
  1009. if (dwLen == 0)
  1010. {
  1011. if (szBuf[0] != '\0')
  1012. lstrcpyn(szInfSection, KEY_DEFAULTUNINSTALL,ARRAYSIZE(szInfSection));
  1013. else
  1014. {
  1015. lstrcpyn(szBuf, m_szInf,ARRAYSIZE(szBuf));
  1016. lstrcpyn(szInfSection, szUninstallSection,ARRAYSIZE(szInfSection));
  1017. }
  1018. }
  1019. // load advpack.dll and call RunSetupCommand() to process
  1020. // any special uninstall commands
  1021. hr = STG_E_FILENOTFOUND;
  1022. HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL"));
  1023. if (hinstAdvPack)
  1024. {
  1025. RUNSETUPCOMMAND pfnRunSetup = (RUNSETUPCOMMAND)GetProcAddress(
  1026. hinstAdvPack, achRUNSETUPCOMMANDFUNCTION);
  1027. if (pfnRunSetup)
  1028. {
  1029. hr = pfnRunSetup(NULL, szBuf, szInfSection,
  1030. szCacheDir, NULL, &hExe, 1, NULL);
  1031. }
  1032. }
  1033. return hr;
  1034. }
  1035. // For each file specified in the INF file, find its
  1036. // path in this order
  1037. // 1) OCX path
  1038. // 2) System dir
  1039. // 3) Windows dir
  1040. // 4) PATH directories
  1041. HRESULT CParseInf::GetFilePath(CFileNode *pFileNode)
  1042. {
  1043. Assert (pFileNode != NULL);
  1044. HRESULT hr = S_OK;
  1045. TCHAR szValueBuf[MAX_PATH];
  1046. TCHAR *pszPathPtr = NULL;
  1047. TCHAR *pszPathEnv = NULL;
  1048. TCHAR *pchPathEnd = NULL;
  1049. DWORD dwLenPATH = 0;
  1050. // ocx directory
  1051. hr = GetDirectory(GD_EXTRACTDIR, szValueBuf, ARRAYSIZE(szValueBuf), m_szFileName);
  1052. CatPathStrN( szValueBuf, szValueBuf, pFileNode->GetName(), ARRAYSIZE(szValueBuf));
  1053. // if file being searched for now is the OCX itself, just leave
  1054. if (lstrcmpi(szValueBuf, m_szFileName) == 0)
  1055. {
  1056. goto EXITGETFILEPATH;
  1057. }
  1058. if (SUCCEEDED(hr) &&
  1059. SUCCEEDED(LookUpModuleUsage(szValueBuf, m_szCLSID)))
  1060. {
  1061. goto EXITGETFILEPATH;
  1062. }
  1063. // system directory
  1064. hr = GetDirectory(GD_SYSTEMDIR, szValueBuf, ARRAYSIZE(szValueBuf));
  1065. if (SUCCEEDED(hr) && CatPathStrN( szValueBuf, szValueBuf, pFileNode->GetName(), ARRAYSIZE(szValueBuf)) &&
  1066. SUCCEEDED(LookUpModuleUsage(szValueBuf, m_szCLSID)))
  1067. {
  1068. goto EXITGETFILEPATH;
  1069. }
  1070. // windows directory
  1071. hr = GetDirectory(GD_WINDOWSDIR, szValueBuf, ARRAYSIZE(szValueBuf));
  1072. if (SUCCEEDED(hr) && CatPathStrN( szValueBuf, szValueBuf, pFileNode->GetName(), ARRAYSIZE(szValueBuf)) &&
  1073. SUCCEEDED(LookUpModuleUsage(szValueBuf, m_szCLSID)))
  1074. {
  1075. goto EXITGETFILEPATH;
  1076. }
  1077. // get PATH envirnment variable
  1078. dwLenPATH = GetEnvironmentVariable(ENV_PATH, szValueBuf, 0);
  1079. if (dwLenPATH == 0)
  1080. {
  1081. hr = E_FAIL;
  1082. goto EXITGETFILEPATH;
  1083. }
  1084. pszPathEnv = new TCHAR[dwLenPATH];
  1085. if (pszPathEnv == NULL)
  1086. {
  1087. hr = E_OUTOFMEMORY;
  1088. goto EXITGETFILEPATH;
  1089. }
  1090. GetEnvironmentVariable(ENV_PATH, pszPathEnv, dwLenPATH);
  1091. pchPathEnd = pszPathPtr = pszPathEnv;
  1092. // walk all directories in PATH and see if file is found
  1093. // in any of them
  1094. while (pchPathEnd != NULL)
  1095. {
  1096. pchPathEnd = StrChr(pszPathPtr, ';');
  1097. if (pchPathEnd != NULL)
  1098. *pchPathEnd = '\0';
  1099. CatPathStrN( szValueBuf, pszPathPtr, pFileNode->GetName(), ARRAYSIZE(szValueBuf));
  1100. if (SUCCEEDED(LookUpModuleUsage(szValueBuf, m_szCLSID)))
  1101. goto EXITGETFILEPATH;
  1102. if (pchPathEnd != NULL)
  1103. *(pchPathEnd++) = ';';
  1104. pszPathPtr = pchPathEnd;
  1105. }
  1106. // file not found anywhere
  1107. hr = E_FAIL;
  1108. EXITGETFILEPATH:
  1109. if (pszPathEnv != NULL)
  1110. delete [] pszPathEnv;
  1111. if (SUCCEEDED(hr))
  1112. {
  1113. hr = NullLastSlash(szValueBuf, 0);
  1114. if (SUCCEEDED(hr))
  1115. {
  1116. hr = pFileNode->SetPath(szValueBuf);
  1117. }
  1118. }
  1119. return hr;
  1120. }
  1121. HRESULT CParseInf::CheckFilesRemovability(void)
  1122. {
  1123. HRESULT hr = S_OK;
  1124. TCHAR szFullName[MAX_PATH];
  1125. const TCHAR *pszPath = NULL;
  1126. BOOL bFileExist;
  1127. // Walk through every file and see if it is deletable. If so,
  1128. // then check if for sharing violations on that file.
  1129. for (m_pCurFileNode = m_pHeadFileList;
  1130. m_pCurFileNode != NULL && SUCCEEDED(hr);
  1131. m_pCurFileNode = m_pCurFileNode->GetNextFileNode())
  1132. {
  1133. pszPath = m_pCurFileNode->GetPath();
  1134. if (pszPath == NULL || pszPath[0] == '\0')
  1135. continue;
  1136. CatPathStrN( szFullName, pszPath, m_pCurFileNode->GetName(), ARRAYSIZE(szFullName) );
  1137. if (IsModuleRemovable(szFullName))
  1138. {
  1139. HANDLE h = CreateFile(
  1140. szFullName,
  1141. GENERIC_READ|GENERIC_WRITE,
  1142. 0,
  1143. NULL,
  1144. OPEN_EXISTING,
  1145. FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING,
  1146. NULL);
  1147. if (h == INVALID_HANDLE_VALUE)
  1148. {
  1149. bFileExist = (GetLastError() != ERROR_FILE_NOT_FOUND);
  1150. if (bFileExist)
  1151. {
  1152. hr = STG_E_SHAREVIOLATION;
  1153. break;
  1154. }
  1155. }
  1156. else
  1157. {
  1158. CloseHandle(h);
  1159. m_pCurFileNode->SetRemovable( TRUE );
  1160. }
  1161. }
  1162. }
  1163. return hr;
  1164. }
  1165. HRESULT CParseInf::CheckLegacyRemovability(LONG *cOldSharedCount )
  1166. {
  1167. HRESULT hr = S_OK;
  1168. BOOL bFileExist;
  1169. HANDLE h = CreateFile(
  1170. m_szFileName,
  1171. GENERIC_READ|GENERIC_WRITE,
  1172. 0,
  1173. NULL,
  1174. OPEN_EXISTING,
  1175. FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING,
  1176. NULL);
  1177. if (h == INVALID_HANDLE_VALUE)
  1178. {
  1179. bFileExist = (GetLastError() != ERROR_FILE_NOT_FOUND);
  1180. if (bFileExist)
  1181. {
  1182. hr = STG_E_SHAREVIOLATION;
  1183. } else
  1184. hr = S_FALSE;
  1185. }
  1186. else
  1187. {
  1188. CloseHandle(h);
  1189. }
  1190. if ( SUCCEEDED(hr) )
  1191. hr = CheckFilesRemovability();
  1192. return hr;
  1193. }
  1194. HRESULT CParseInf::CheckDURemovability(HKEY hkeyDUDB, BOOL bSilent)
  1195. {
  1196. HRESULT hr = S_OK;
  1197. BOOL bAskSystemClass = TRUE;
  1198. hr = CheckFilesRemovability();
  1199. if (FAILED(hr)) {
  1200. goto CheckDURemovabilityExit;
  1201. }
  1202. hr = CheckDUDependencies(hkeyDUDB, bSilent);
  1203. if (FAILED(hr)) {
  1204. goto CheckDURemovabilityExit;
  1205. }
  1206. // Check for package removability.
  1207. // We shouldn't remove a package if another DU also uses it.
  1208. // TODO: Some sort of package-currently-in-use test. Either test the path file, as above,
  1209. // or use some groovy new IJavaPackage(Manager) method.
  1210. for (m_pCurPackageNode = m_pHeadPackageList;
  1211. m_pCurPackageNode != NULL;
  1212. m_pCurPackageNode = m_pCurPackageNode->GetNextPackageNode())
  1213. {
  1214. TCHAR szT[MAX_PATH];
  1215. LRESULT lResult;
  1216. BOOL bFoundInOtherDU = FALSE;
  1217. int cDistUnitEnum = 0;
  1218. if (!bAskSystemClass && m_pCurPackageNode->GetIsSystemClass()) {
  1219. char lpszBuf[MAX_MSGBOX_STRING_LEN];
  1220. char lpszBufTitle[MAX_MSGBOX_TITLE_LEN];
  1221. MLLoadString(IDS_OCCACHE_WARNING_JAVA_SYSTEM_CLASS,
  1222. lpszBuf, MAX_MSGBOX_STRING_LEN);
  1223. MLLoadString(IDS_REMOVAL_WARNING,
  1224. lpszBufTitle, MAX_MSGBOX_TITLE_LEN);
  1225. // Attempting to remove system class. Warn user.
  1226. if ( bSilent ||
  1227. MessageBox(NULL, lpszBuf, lpszBufTitle,
  1228. MB_YESNO | MB_ICONWARNING) != IDYES) {
  1229. hr = E_FAIL;
  1230. goto CheckDURemovabilityExit;
  1231. }
  1232. bAskSystemClass = FALSE;
  1233. }
  1234. // Enumerate distribution units
  1235. while ( (lResult = RegEnumKey(hkeyDUDB, cDistUnitEnum++, szT, MAX_PATH)) == ERROR_SUCCESS &&
  1236. !bFoundInOtherDU )
  1237. {
  1238. if ( lstrcmp(szT, m_szCLSID) != 0 ) // skip the current DU
  1239. {
  1240. HKEY hkeyDUCJ;
  1241. DWORD dw = MAX_PATH;
  1242. lstrcat(szT, REGSTR_DU_CONTAINS_JAVA );
  1243. lResult = RegOpenKeyEx( hkeyDUDB, szT, 0, KEY_READ, &hkeyDUCJ );
  1244. if ( lResult == ERROR_SUCCESS )
  1245. {
  1246. lResult = RegQueryValueEx(hkeyDUCJ, REGSTR_VALUE_INF, NULL, NULL,
  1247. (unsigned char*)szT, &dw);
  1248. // To be safe, assume that anything other than value not found means
  1249. // that the other DU also uses the package.
  1250. bFoundInOtherDU = lResult != ERROR_FILE_NOT_FOUND;
  1251. RegCloseKey( hkeyDUCJ );
  1252. } // if we could open other key's Contains\Java subkey
  1253. } // if it's a different DU
  1254. } // while enumerating DUs
  1255. // if we found it in another DU, then we shouldn't remove this package with this DU
  1256. m_pCurPackageNode->SetRemovable( !bFoundInOtherDU );
  1257. } // for each package
  1258. CheckDURemovabilityExit:
  1259. return hr;
  1260. }
  1261. HRESULT CParseInf::RemoveLegacyControl( LPCTSTR lpszTypeLibID, BOOL bSilent )
  1262. {
  1263. HRESULT hr = S_FALSE;
  1264. const TCHAR *pszPath;
  1265. BOOL bUnplug = m_dwStatus != STATUS_CTRL_UNPLUGGED;
  1266. BOOL bFileMissing = !PathFileExists( m_szFileName );
  1267. BOOL bDidRemove = FALSE;
  1268. TCHAR szFullName[MAX_PATH];
  1269. // loop through the list of assocated files, remove them as
  1270. // well as their registry entries.
  1271. for (m_pCurFileNode = m_pHeadFileList;
  1272. m_pCurFileNode != NULL;
  1273. m_pCurFileNode = m_pCurFileNode->GetNextFileNode())
  1274. {
  1275. int cOwners;
  1276. pszPath = m_pCurFileNode->GetPath();
  1277. // Process INF file, which as no path since it's not described in INF
  1278. if (pszPath == NULL || pszPath[0] == '\0')
  1279. {
  1280. if ( DeleteFile(m_pCurFileNode->GetName()) )
  1281. hr = S_OK; // hey, we did _something_ - averts the "not enough info" message
  1282. continue;
  1283. }
  1284. // If we're where, we had some other file besides the INF.
  1285. // Even if we don't remove it, we still knock down its module
  1286. // usage, which has gotta count for having done something.
  1287. hr = S_OK;
  1288. CatPathStrN( szFullName, pszPath, m_pCurFileNode->GetName(), MAX_PATH);
  1289. cOwners = SubtractModuleOwner( szFullName, m_szCLSID );
  1290. if (m_pCurFileNode->GetRemovable() && cOwners == 0)
  1291. {
  1292. if ( bUnplug )
  1293. UnregisterOCX(szFullName);
  1294. DeleteFile(szFullName);
  1295. bDidRemove = bDidRemove || StrCmpI(szFullName,m_szFileName) == 0;
  1296. }
  1297. }
  1298. if (hr == S_OK && bDidRemove && lpszTypeLibID != NULL)
  1299. CleanInterfaceEntries(lpszTypeLibID);
  1300. if ( bUnplug && bFileMissing )
  1301. {
  1302. if ( m_szFileName[0] != '\0' ) // only do this if there is an ocx to clean up after
  1303. CleanOrphanedRegistry(m_szFileName, m_szCLSID, lpszTypeLibID);
  1304. }
  1305. return hr;
  1306. }
  1307. HRESULT CParseInf::RemoveDU( LPTSTR szFullName, LPCTSTR lpszTypeLibID, HKEY hkeyDUDB, BOOL bSilent )
  1308. {
  1309. HRESULT hr = S_FALSE; // only say S_OK if we actually do something beyond yanking the INF
  1310. const TCHAR *pszPath = NULL;
  1311. hr = RemoveLegacyControl( lpszTypeLibID, bSilent );
  1312. if (SUCCEEDED(hr))
  1313. {
  1314. // Remove the packages that we have determined are safe to remove.
  1315. for (m_pCurPackageNode = m_pHeadPackageList;
  1316. m_pCurPackageNode != NULL;
  1317. m_pCurPackageNode = m_pCurPackageNode->GetNextPackageNode())
  1318. {
  1319. if ( m_pCurPackageNode->GetRemovable() )
  1320. {
  1321. Assert(m_pijpm != NULL);
  1322. #ifdef UNICODE
  1323. OLECHAR *swzPkg = m_pCurPackageNode->GetName();
  1324. OLECHAR *swzNamespace = m_pCurPackageNode->GetNamespace();
  1325. #else
  1326. MAKE_WIDEPTR_FROMANSI(swzPkg, m_pCurPackageNode->GetName());
  1327. MAKE_WIDEPTR_FROMANSI(swzNamespace, m_pCurPackageNode->GetNamespace());
  1328. #endif
  1329. hr = m_pijpm->UninstallPackage( swzPkg,
  1330. ((*swzNamespace == 0)? NULL : swzNamespace),
  1331. 0 );
  1332. }
  1333. }
  1334. }
  1335. DeleteKeyAndSubKeys(hkeyDUDB, m_szCLSID);
  1336. return hr;
  1337. }
  1338. HRESULT CParseInf::CheckDUDependencies(HKEY hKeyDUDB, BOOL bSilent )
  1339. {
  1340. long lrDist = 0;
  1341. long lResult = 0;
  1342. long lr = 0;
  1343. int iSubKey = 0;
  1344. HKEY hkeyCurrent = 0;
  1345. HKEY hkeyCurDU = 0;
  1346. char szName[MAX_REGPATH_LEN];
  1347. int iValue = 0;
  1348. unsigned long ulSize;
  1349. char szDependency[MAX_REGPATH_LEN];
  1350. HKEY hkeyCOM = 0;
  1351. DWORD dwType = 0;
  1352. char szDepName[MAX_CONTROL_NAME_LEN];
  1353. char szDepWarnBuf[MAX_MSGBOX_STRING_LEN];
  1354. char szCOMControl[MAX_REGPATH_LEN];
  1355. DWORD dwSize = 0;
  1356. HRESULT hr = S_OK;
  1357. // Iterate through DUs that have a ...\contains\Distribution Units
  1358. // key in the registry and compare the entries inside with the DU
  1359. // being removed.
  1360. while ((lResult = RegEnumKey(hKeyDUDB, iSubKey++, szName,
  1361. MAX_REGPATH_LEN)) == ERROR_SUCCESS)
  1362. {
  1363. if (!lstrcmpi(szName, m_szCLSID))
  1364. {
  1365. // Skip ourselves
  1366. continue;
  1367. }
  1368. if (RegOpenKeyEx(hKeyDUDB, szName, 0, KEY_READ, &hkeyCurrent) ==
  1369. ERROR_SUCCESS)
  1370. {
  1371. lr = RegOpenKeyEx(hkeyCurrent, REGSTR_DU_CONTAINS_DIST_UNITS,
  1372. 0, KEY_READ, &hkeyCurDU);
  1373. if (lr != ERROR_SUCCESS)
  1374. {
  1375. RegCloseKey(hkeyCurrent);
  1376. continue;
  1377. }
  1378. ulSize = MAX_REGPATH_LEN;
  1379. while ((lResult = RegEnumValue(hkeyCurDU, iValue++, szDependency,
  1380. &ulSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
  1381. {
  1382. if (!lstrcmpi(szDependency, m_szCLSID))
  1383. {
  1384. // dependency found
  1385. // Try to get a friendly name for the dependency control
  1386. dwSize = MAX_CONTROL_NAME_LEN;
  1387. lResult = RegQueryValueEx(hkeyCurrent, NULL, NULL,
  1388. &dwType, (unsigned char *)szDepName,
  1389. &dwSize);
  1390. if (lResult != ERROR_SUCCESS || szDepName[0] == '\0') {
  1391. // Couldn't get a friendly name. Try the COM branch.
  1392. // Technically, this could overflow because
  1393. // szName and szCOMControl are the same size, but
  1394. // this is already at our defined maximum size for reg
  1395. // entries.
  1396. wsprintf(szCOMControl, "%s\\%s", REGSTR_COM_BRANCH, szName);
  1397. lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, szCOMControl,
  1398. 0, KEY_READ, &hkeyCOM);
  1399. if (lResult != ERROR_SUCCESS) {
  1400. MLLoadString(IDS_OCCACHE_WARNING_DEP_REMOVAL_NAME_UNKNOWN,
  1401. szDepWarnBuf, MAX_MSGBOX_STRING_LEN);
  1402. }
  1403. else {
  1404. dwSize = MAX_CONTROL_NAME_LEN;
  1405. lResult = RegQueryValueEx(hkeyCOM, NULL, NULL,
  1406. &dwType, (unsigned char *)szDepName,
  1407. &dwSize);
  1408. if (lResult != ERROR_SUCCESS || szDepName[0] == '\0') {
  1409. MLLoadString(IDS_OCCACHE_WARNING_DEP_REMOVAL_NAME_UNKNOWN,
  1410. szDepWarnBuf, MAX_MSGBOX_STRING_LEN);
  1411. }
  1412. else {
  1413. char lpszBuf[MAX_MSGBOX_STRING_LEN];
  1414. MLLoadString(IDS_OCCACHE_WARNING_DEPENDENCY_REMOVAL,
  1415. lpszBuf, MAX_MSGBOX_STRING_LEN);
  1416. wsprintf(szDepWarnBuf, lpszBuf, szDepName);
  1417. }
  1418. if (hkeyCOM) {
  1419. RegCloseKey(hkeyCOM);
  1420. }
  1421. }
  1422. }
  1423. else {
  1424. char lpszBuf[MAX_MSGBOX_STRING_LEN];
  1425. MLLoadString(IDS_OCCACHE_WARNING_DEPENDENCY_REMOVAL,
  1426. lpszBuf, MAX_MSGBOX_STRING_LEN);
  1427. wsprintf(szDepWarnBuf, lpszBuf, szDepName);
  1428. }
  1429. // TODO: Consider using better HWND than desktop
  1430. char lpszBufTitle[MAX_MSGBOX_TITLE_LEN];
  1431. MLLoadString(IDS_REMOVAL_WARNING,
  1432. lpszBufTitle, MAX_MSGBOX_TITLE_LEN);
  1433. if (bSilent ||
  1434. MessageBox(NULL, szDepWarnBuf, lpszBufTitle,
  1435. MB_YESNO | MB_ICONWARNING) != IDYES)
  1436. {
  1437. hr = E_FAIL;
  1438. RegCloseKey(hkeyCurDU);
  1439. RegCloseKey(hkeyCurrent);
  1440. RegCloseKey(hKeyDUDB);
  1441. goto ReturnCheckDUDependencies;
  1442. }
  1443. }
  1444. ulSize = MAX_REGPATH_LEN;
  1445. }
  1446. RegCloseKey(hkeyCurDU);
  1447. RegCloseKey(hkeyCurrent);
  1448. }
  1449. }
  1450. ReturnCheckDUDependencies:
  1451. return hr;
  1452. }
  1453. // uninstall OCX and its associated files
  1454. HRESULT CParseInf::RemoveFiles(
  1455. LPCTSTR lpszTypeLibID /* = NULL */,
  1456. BOOL bForceRemove, /* = FALSE */
  1457. DWORD dwIsDistUnit,
  1458. BOOL bSilent)
  1459. {
  1460. HRESULT hr = S_OK;
  1461. HRESULT hrInf1;
  1462. HRESULT hrInf2;
  1463. TCHAR szFullName[MAX_PATH];
  1464. const TCHAR *pszPath = NULL;
  1465. BOOL bRemovable = (dwIsDistUnit) ? (TRUE) : (IsModuleRemovable(m_szFileName));
  1466. BOOL bIsOCX = FALSE;
  1467. LONG cRefOld = 0;
  1468. HKEY hKeyDUDB = 0;
  1469. BOOL bUnplug = m_dwStatus == STATUS_CTRL_DAMAGED || m_dwStatus == STATUS_CTRL_INSTALLED;
  1470. if ( !g_fAllAccess || (!bForceRemove && !bRemovable))
  1471. {
  1472. hr = E_ACCESSDENIED;
  1473. goto ExitRemoveFiles;
  1474. }
  1475. // Check sharing violation (if it is a legacy control)
  1476. if (!dwIsDistUnit)
  1477. {
  1478. hr = CheckLegacyRemovability( &cRefOld );
  1479. // set SharedDlls count to 1 and save up the old
  1480. // count in case the removal fails
  1481. if (hr == S_OK && !bRemovable &&
  1482. FAILED(hr = SetSharedDllsCount(m_szFileName, 1, &cRefOld)))
  1483. {
  1484. hr = (!PathFileExists( m_szFileName ) ? S_OK : hr);
  1485. goto ExitRemoveFiles;
  1486. }
  1487. if ( FAILED(hr) )
  1488. goto ExitRemoveFiles;
  1489. }
  1490. else
  1491. {
  1492. long lResultDist;
  1493. lResultDist = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0,
  1494. KEY_READ, &hKeyDUDB);
  1495. if (lResultDist == ERROR_SUCCESS)
  1496. hr = CheckDURemovability( hKeyDUDB, bSilent );
  1497. else
  1498. hr = E_FAIL;
  1499. if ( FAILED(hr) )
  1500. goto ReturnRemoveFiles;
  1501. }
  1502. // ** keyword UNINSTALL -- new feature that hasn't been implemented yet **
  1503. // parse [Setup Hook], look for "UNINSTALL" key
  1504. if (FAILED(hrInf1 = ParseSetupHook()))
  1505. {
  1506. goto ExitRemoveFiles;
  1507. }
  1508. // parse conditional hooks in each of the file sections
  1509. if (FAILED(hrInf2 = ParseConditionalHook()))
  1510. {
  1511. goto ExitRemoveFiles;
  1512. }
  1513. // Okay, if the both didn't do anything, we'll try the DefaultUninstall
  1514. if ( hrInf1 == S_FALSE && hrInf2 == S_FALSE && PathFileExists( m_szInf ) )
  1515. {
  1516. // see if there's anybody home in the default uninstall section
  1517. DWORD dwSize = GetPrivateProfileString( KEY_DEFAULTUNINSTALL,
  1518. NULL,
  1519. DEFAULT_VALUE,
  1520. szFullName,
  1521. MAX_PATH,
  1522. m_szInf );
  1523. if ( dwSize > 0 )
  1524. {
  1525. HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL"));
  1526. HANDLE hExe = INVALID_HANDLE_VALUE;
  1527. GetDirectory(GD_EXTRACTDIR, szFullName, ARRAYSIZE(szFullName), m_szInf);
  1528. if (hinstAdvPack)
  1529. {
  1530. RUNSETUPCOMMAND pfnRunSetup = (RUNSETUPCOMMAND)GetProcAddress(
  1531. hinstAdvPack, achRUNSETUPCOMMANDFUNCTION);
  1532. if (pfnRunSetup)
  1533. {
  1534. // reset hrINf2 to reflect the success of running the default
  1535. // uninstall section. This will prevent us from pointing to the
  1536. // Add/Remove control panel in some cases, like Shockwave.
  1537. hrInf2 = pfnRunSetup(NULL, m_szInf, KEY_DEFAULTUNINSTALL,
  1538. szFullName, NULL, &hExe, RSC_FLAG_INF, NULL);
  1539. }
  1540. FreeLibrary( hinstAdvPack );
  1541. }
  1542. }
  1543. }
  1544. if ( !dwIsDistUnit )
  1545. hr = RemoveLegacyControl( lpszTypeLibID, bSilent );
  1546. else
  1547. hr = RemoveDU( szFullName, lpszTypeLibID, hKeyDUDB, bSilent );
  1548. if ( FAILED(hr) )
  1549. goto ExitRemoveFiles;
  1550. // Return S_FALSE iff none of our uninstall efforts succeeded
  1551. if ( hr == S_FALSE && (hrInf1 == S_OK || hrInf2 == S_OK) )
  1552. hr = S_OK;
  1553. // remove conflict directory
  1554. if (SUCCEEDED(GetDirectory(GD_CONFLICTDIR, szFullName, ARRAYSIZE(szFullName))) &&
  1555. LStrNICmp(m_szFileName, szFullName, lstrlen(szFullName)) == 0)
  1556. {
  1557. TCHAR *pCh = ReverseStrchr(m_szFileName, '\\');
  1558. Assert (pCh != NULL);
  1559. TCHAR chTemp = *pCh;
  1560. *pCh = '\0';
  1561. RemoveDirectory(m_szFileName);
  1562. *pCh = chTemp;
  1563. }
  1564. DestroyFileList();
  1565. ExitRemoveFiles:
  1566. // set shared dlls count back to where it was if OCX cannot be removed
  1567. if (cRefOld > 0 && FileExist(m_szFileName))
  1568. {
  1569. if (SUCCEEDED(hr))
  1570. hr = SetSharedDllsCount(m_szFileName, cRefOld);
  1571. else
  1572. SetSharedDllsCount(m_szFileName, cRefOld);
  1573. }
  1574. if ( hKeyDUDB )
  1575. RegCloseKey( hKeyDUDB );
  1576. ReturnRemoveFiles:
  1577. return hr;
  1578. }
  1579. void CParseInf::SetIsDistUnit(BOOL bDist)
  1580. {
  1581. m_bIsDistUnit = bDist;
  1582. }
  1583. BOOL CParseInf::GetIsDistUnit() const
  1584. {
  1585. return m_bIsDistUnit;
  1586. }
  1587. // return total size of OCX and its associated files
  1588. DWORD CParseInf::GetTotalFileSize() const
  1589. {
  1590. return m_dwTotalFileSize;
  1591. }
  1592. DWORD CParseInf::GetTotalSizeSaved() const
  1593. {
  1594. return m_dwFileSizeSaved;
  1595. }
  1596. DWORD CParseInf::GetStatus() const
  1597. {
  1598. return m_dwStatus;
  1599. }
  1600. // return total number of files which will be removed
  1601. // together with the OCX
  1602. int CParseInf::GetTotalFiles() const
  1603. {
  1604. return m_nTotalFiles;
  1605. }
  1606. // return first file in the list of associated files
  1607. CFileNode* CParseInf::GetFirstFile()
  1608. {
  1609. m_pFileRetrievalPtr = m_pHeadFileList;
  1610. return m_pFileRetrievalPtr;
  1611. }
  1612. // get the next file in the list of associated files
  1613. CFileNode* CParseInf::GetNextFile()
  1614. {
  1615. m_pFileRetrievalPtr = m_pFileRetrievalPtr->GetNextFileNode();
  1616. return m_pFileRetrievalPtr;
  1617. }
  1618. // return first file in the list of associated files
  1619. CPackageNode* CParseInf::GetFirstPackage()
  1620. {
  1621. m_pPackageRetrievalPtr = m_pHeadPackageList;
  1622. return m_pPackageRetrievalPtr;
  1623. }
  1624. // get the next file in the list of associated files
  1625. CPackageNode* CParseInf::GetNextPackage()
  1626. {
  1627. m_pPackageRetrievalPtr = (m_pPackageRetrievalPtr != NULL)?
  1628. m_pPackageRetrievalPtr->GetNextPackageNode() :
  1629. NULL;
  1630. return m_pPackageRetrievalPtr;
  1631. }