Source code of Windows XP (NT5)
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.

686 lines
19 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1999 - 2000
  6. //
  7. // File: modules.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. // Modules.cpp: implementation of the CModules class.
  11. //
  12. //////////////////////////////////////////////////////////////////////
  13. #ifndef NO_STRICT
  14. #ifndef STRICT
  15. #define STRICT 1
  16. #endif
  17. #endif /* NO_STRICT */
  18. #include <WINDOWS.H>
  19. #include <STDIO.H>
  20. #include <TCHAR.H>
  21. #include "Globals.h"
  22. #include "Modules.h"
  23. #include "ProgramOptions.h"
  24. #include "UtilityFunctions.h"
  25. #include "ModuleInfo.h"
  26. #include "ModuleInfoNode.h"
  27. #include "ModuleInfoCache.h"
  28. #include "FileData.h"
  29. //////////////////////////////////////////////////////////////////////
  30. // Construction/Destruction
  31. //////////////////////////////////////////////////////////////////////
  32. CModules::CModules()
  33. {
  34. m_lpModuleInfoHead = NULL;
  35. m_hModuleInfoHeadMutex = NULL;
  36. m_lpDmpFile = NULL;
  37. m_fInitialized = false;
  38. m_iNumberOfModules = 0;
  39. }
  40. CModules::~CModules()
  41. {
  42. WaitForSingleObject(m_hModuleInfoHeadMutex, INFINITE);
  43. // If we have Module Info Objects... nuke them now...
  44. if (m_lpModuleInfoHead)
  45. {
  46. CModuleInfoNode * lpModuleInfoNodePointer = m_lpModuleInfoHead;
  47. CModuleInfoNode * lpModuleInfoNodePointerToDelete = m_lpModuleInfoHead;
  48. // Traverse the linked list to the end..
  49. while (lpModuleInfoNodePointer)
  50. { // Keep looking for the end...
  51. // Advance our pointer to the next node...
  52. lpModuleInfoNodePointer = lpModuleInfoNodePointer->m_lpNextModuleInfoNode;
  53. // Delete the one behind us...
  54. delete lpModuleInfoNodePointerToDelete;
  55. // Set the node to delete to the current...
  56. lpModuleInfoNodePointerToDelete = lpModuleInfoNodePointer;
  57. }
  58. // Now, clear out the Head pointer...
  59. m_lpModuleInfoHead = NULL;
  60. }
  61. // Be a good citizen and release the Mutex
  62. ReleaseMutex(m_hModuleInfoHeadMutex);
  63. // Now, close the Mutex
  64. if (m_hModuleInfoHeadMutex)
  65. {
  66. CloseHandle(m_hModuleInfoHeadMutex);
  67. m_hModuleInfoHeadMutex = NULL;
  68. }
  69. }
  70. bool CModules::Initialize(CModuleInfoCache *lpModuleInfoCache, CFileData * lpInputFile, CFileData * lpOutputFile, CDmpFile * lpDmpFile)
  71. {
  72. // We need the following objects to do business...
  73. if ( lpModuleInfoCache == NULL)
  74. return false;
  75. m_lpModuleInfoCache = lpModuleInfoCache;
  76. m_lpInputFile = lpInputFile;
  77. m_lpOutputFile = lpOutputFile;
  78. m_lpDmpFile = lpDmpFile;
  79. m_hModuleInfoHeadMutex = CreateMutex(NULL, FALSE, NULL);
  80. if (m_hModuleInfoHeadMutex == NULL)
  81. return false;
  82. m_fInitialized = true;
  83. return true;
  84. }
  85. bool CModules::GetModulesData(CProgramOptions::ProgramModes enumProgramModes, bool fGetDataFromCSVFile)
  86. {
  87. switch (enumProgramModes)
  88. {
  89. case CProgramOptions::InputModulesDataFromFileSystemMode:
  90. if (fGetDataFromCSVFile)
  91. {
  92. GetModulesDataFromFile();
  93. } else
  94. {
  95. GetModulesDataFromFileSystem();
  96. }
  97. break;
  98. case CProgramOptions::InputDriversFromLiveSystemMode:
  99. if (fGetDataFromCSVFile)
  100. {
  101. GetModulesDataFromFile(); // ISSUE-2000/07/24-GREGWI: I think we can use the same method as above ????
  102. } else
  103. {
  104. GetModulesDataFromDeviceDrivers();
  105. }
  106. break;
  107. default:
  108. break;
  109. }
  110. return true;
  111. }
  112. bool CModules::GetModulesDataFromFileSystem()
  113. {
  114. bool fProcessPath = true;
  115. // Okay... here we go...
  116. //#ifdef _DEBUG
  117. // _tprintf(TEXT("Processing the path [%s]\n"), m_lpProgramOptions->GetInputModulesDataFromFileSystemPath());
  118. //#endif
  119. LPTSTR tszExpandedSymbolPath= NULL, tszSymbolPathStart, tszSymbolPathEnd;
  120. // Mark the start of the path to process
  121. tszSymbolPathStart = g_lpProgramOptions->GetInputModulesDataFromFileSystemPath();
  122. // Find the end of the path
  123. tszSymbolPathEnd = _tcschr( tszSymbolPathStart, ';' );
  124. // If tszSymbolPathEnd is non-zero, then there is another path following...
  125. if (tszSymbolPathEnd)
  126. *tszSymbolPathEnd = '\0'; // Change the ';' to a Null temporarily...
  127. while (fProcessPath)
  128. {
  129. //#ifdef _DEBUG
  130. // _tprintf(TEXT("\n\nProcessing Path [%s]\n"), tszSymbolPathStart);
  131. //#endif
  132. // Begin the "madness"... ;)
  133. ScavengeForFiles(tszSymbolPathStart, 1);
  134. // Post processing... replace the null if necessary, and advance to next string
  135. if (tszSymbolPathEnd)
  136. {
  137. *tszSymbolPathEnd = ';';
  138. tszSymbolPathStart = tszSymbolPathEnd + 1;
  139. tszSymbolPathEnd = _tcschr( tszSymbolPathStart, ';' );
  140. if (tszSymbolPathEnd) {
  141. *tszSymbolPathEnd = '\0';
  142. }
  143. } else
  144. fProcessPath = false;
  145. }
  146. if (tszExpandedSymbolPath)
  147. {
  148. delete [] tszExpandedSymbolPath;
  149. }
  150. return true;
  151. }
  152. bool CModules::ScavengeForFiles(LPCTSTR tszSymbolPathStart, int iRecurseDepth)
  153. {
  154. // Bale if we're in too deep...
  155. if (iRecurseDepth > MAX_RECURSE_DEPTH)
  156. return true;
  157. TCHAR tszFileBuffer[MAX_PATH+1];
  158. TCHAR drive[_MAX_DRIVE];
  159. TCHAR dir[_MAX_DIR];
  160. TCHAR fname[_MAX_FNAME];
  161. TCHAR ext[_MAX_EXT];
  162. bool fNew;
  163. CModuleInfo * lpModuleInfo;
  164. _tsplitpath(tszSymbolPathStart, drive, dir, fname, ext);
  165. WIN32_FIND_DATA lpFindFileData;
  166. HANDLE hFileOrDirectoryHandle = FindFirstFile(tszSymbolPathStart, &lpFindFileData);
  167. while ( INVALID_HANDLE_VALUE != hFileOrDirectoryHandle )
  168. {
  169. // Compose the path to the file or directory...
  170. _tmakepath(tszFileBuffer, drive, dir, NULL, NULL);
  171. _tcscat(tszFileBuffer, lpFindFileData.cFileName);
  172. if (lpFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  173. {
  174. // Check to see if we've got the . or .. directories!
  175. if ( ( 0 == _tcscmp(lpFindFileData.cFileName, TEXT(".")) ) ||
  176. ( 0 == _tcscmp(lpFindFileData.cFileName, TEXT("..")) )
  177. )
  178. {
  179. // Skip this one...
  180. if (!FindNextFile(hFileOrDirectoryHandle, &lpFindFileData))
  181. break;
  182. // Go up for more fun...
  183. continue;
  184. }
  185. //#ifdef _DEBUG
  186. // _tprintf(TEXT("DIRECTORY FOUND: [%s]\n"), tszFileBuffer);
  187. //#endif
  188. // If this is a directory, and no wild card was provided.. then use *.*
  189. if ( CUtilityFunctions::ContainsWildCardCharacter(tszSymbolPathStart) )
  190. {
  191. // We need to preserve the Wild Char stuff...
  192. _tcscat(tszFileBuffer,TEXT("\\"));
  193. _tcscat(tszFileBuffer,fname);
  194. _tcscat(tszFileBuffer, ext);
  195. } else
  196. {
  197. // Append the *.*
  198. _tcscat(tszFileBuffer, TEXT("\\*.*"));
  199. }
  200. ScavengeForFiles(tszFileBuffer, iRecurseDepth+1);
  201. } else
  202. {
  203. //#ifdef _DEBUG
  204. // _tprintf(TEXT("FILE FOUND: [%s]\n"), tszFileBuffer);
  205. //#endif
  206. fNew = false;
  207. TCHAR tszFullFileBuffer[_MAX_PATH+1];
  208. LPTSTR tszFileNamePointer;
  209. DWORD cbBytesCopied = GetFullPathName(tszFileBuffer , _MAX_PATH+1, tszFullFileBuffer, &tszFileNamePointer);
  210. if (cbBytesCopied)
  211. {
  212. // If "-MATCH" was specified, look to see if this filename meets our criteria
  213. // before we save this away in our module cache...
  214. if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszFileBuffer))
  215. goto getnextmodule;
  216. // Okay, let's go ahead and get a ModuleInfo Object from our cache...
  217. // If pfNew returns TRUE, then this object is new and we'll need
  218. // to populate it with data...
  219. lpModuleInfo = m_lpModuleInfoCache->AddNewModuleInfoObject(tszFullFileBuffer, &fNew);
  220. if (false == fNew)
  221. {
  222. // We may have the object in the cache... now we need to
  223. // save a pointer to this object in our Process Info list
  224. AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
  225. // We save having to get the module info again for this module...
  226. goto getnextmodule;
  227. }
  228. // Not in the cache... so we need to init it, and get the module info...
  229. // Okay, let's create a ModuleInfo object and pass this down
  230. // routines that will populate it full of data...
  231. if (lpModuleInfo->Initialize(NULL, m_lpOutputFile, NULL))
  232. {
  233. // Let's do it!! Populate the ModuleInfo object with data!!!!
  234. if (lpModuleInfo->GetModuleInfo(tszFileBuffer))
  235. {
  236. // Start obtaining information about the modules...
  237. /*
  238. #ifdef _DEBUG
  239. _tprintf(TEXT("Module[%3d] = [%s]\n"), i+1, szFileName);
  240. #endif
  241. */
  242. // We may have the object in the cache... now we need to
  243. // save a pointer to this object in our Process Info list
  244. if (AddNewModuleInfoObject(lpModuleInfo))
  245. {
  246. }
  247. }
  248. }
  249. }
  250. }
  251. getnextmodule:
  252. if (!FindNextFile(hFileOrDirectoryHandle, &lpFindFileData))
  253. break;
  254. }
  255. if ( INVALID_HANDLE_VALUE != hFileOrDirectoryHandle )
  256. FindClose(hFileOrDirectoryHandle);
  257. return true;
  258. }
  259. bool CModules::AddNewModuleInfoObject(CModuleInfo *lpModuleInfo)
  260. {
  261. if (!m_fInitialized)
  262. return false;
  263. // First, create a ModuleInfoNode object and then attach it to the bottom of the
  264. // linked list of nodes...
  265. CModuleInfoNode * lpModuleInfoNode = new CModuleInfoNode(lpModuleInfo);
  266. //#ifdef _DEBUG
  267. // _tprintf(TEXT("Adding Module Info Object for [%s]\n"), lpModuleInfo->GetModulePath());
  268. //#endif
  269. if (lpModuleInfoNode == NULL)
  270. return false; // Couldn't allocate memory..
  271. // Acquire Mutex object to protect the linked-list...
  272. WaitForSingleObject(m_hModuleInfoHeadMutex, INFINITE);
  273. CModuleInfoNode * lpModuleInfoNodePointer = m_lpModuleInfoHead;
  274. if (lpModuleInfoNodePointer) {
  275. // Traverse the linked list to the end..
  276. while (lpModuleInfoNodePointer->m_lpNextModuleInfoNode)
  277. { // Keep looking for the end...
  278. lpModuleInfoNodePointer = lpModuleInfoNodePointer->m_lpNextModuleInfoNode;
  279. }
  280. lpModuleInfoNodePointer->m_lpNextModuleInfoNode = lpModuleInfoNode;
  281. }
  282. else
  283. { // First time through, the Process Info Head pointer is null...
  284. m_lpModuleInfoHead = lpModuleInfoNode;
  285. }
  286. // Be a good citizen and release the Mutex
  287. ReleaseMutex(m_hModuleInfoHeadMutex);
  288. InterlockedIncrement(&m_iNumberOfModules);
  289. return true;
  290. }
  291. //bool CModules::OutputModulesData(LPCTSTR tszOutputContext)
  292. bool CModules::OutputModulesData(CollectionTypes enumCollectionType, bool fCSVFileContext)
  293. {
  294. // Are we in quiet mode?
  295. if ( !g_lpProgramOptions->GetMode(CProgramOptions::QuietMode) )
  296. {
  297. // Output to Stdout?
  298. if (!OutputModulesDataToStdout(enumCollectionType, fCSVFileContext))
  299. return false;
  300. }
  301. // Output to file?
  302. if (g_lpProgramOptions->GetMode(CProgramOptions::OutputCSVFileMode))
  303. {
  304. // Try and output to file...
  305. if (!OutputModulesDataToFile(enumCollectionType))
  306. return false;
  307. }
  308. if (m_lpModuleInfoHead) {
  309. CModuleInfoNode * lpCurrentModuleInfoNode = m_lpModuleInfoHead;
  310. DWORD dwModuleNumber = 1;
  311. while (lpCurrentModuleInfoNode)
  312. {
  313. // We have a node... print out Module Info for it, then the Modules Data...
  314. if (lpCurrentModuleInfoNode->m_lpModuleInfo)
  315. {
  316. lpCurrentModuleInfoNode->m_lpModuleInfo->OutputData(NULL, 0, dwModuleNumber);
  317. dwModuleNumber++;
  318. }
  319. lpCurrentModuleInfoNode = lpCurrentModuleInfoNode->m_lpNextModuleInfoNode;
  320. }
  321. }
  322. return true;
  323. }
  324. bool CModules::OutputModulesDataToStdout(CollectionTypes enumCollectionType, bool fCSVFileContext)
  325. {
  326. _tprintf(TEXT("\n"));
  327. CUtilityFunctions::OutputLineOfStars();
  328. // Output to stdout...
  329. if (m_iNumberOfModules)
  330. {
  331. _tprintf(TEXT("%s - Printing Module Information for %d Modules.\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel, m_iNumberOfModules);
  332. _tprintf(TEXT("%s - Context: %s\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel, fCSVFileContext ? g_tszCollectionArray[enumCollectionType].tszCSVContext : g_tszCollectionArray[enumCollectionType].tszLocalContext);
  333. } else
  334. {
  335. _tprintf(TEXT("\n%s - No modules were found!\n\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel);
  336. }
  337. CUtilityFunctions::OutputLineOfStars();
  338. _tprintf(TEXT("\n"));
  339. return true;
  340. }
  341. bool CModules::OutputModulesDataToFile(CollectionTypes enumCollectionType)
  342. {
  343. // Don't write anything if there are no processes to report...
  344. if (0 == m_iNumberOfModules)
  345. return true;
  346. // Write out the Modules tag so I can detect this output format...
  347. if (!m_lpOutputFile->WriteString(TEXT("\r\n")) ||
  348. !m_lpOutputFile->WriteString(g_tszCollectionArray[enumCollectionType].tszCSVLabel) ||
  349. !m_lpOutputFile->WriteString(TEXT("\r\n"))
  350. )
  351. {
  352. _tprintf(TEXT("Failure writing CSV header to file [%s]!"), m_lpOutputFile->GetFilePath());
  353. m_lpOutputFile->PrintLastError();
  354. return false;
  355. }
  356. // Write out the [Modules] header...
  357. if (!m_lpOutputFile->WriteString(g_tszCollectionArray[enumCollectionType].tszCSVColumnHeaders))
  358. {
  359. _tprintf(TEXT("Failure writing CSV header to file [%s]!"), m_lpOutputFile->GetFilePath());
  360. m_lpOutputFile->PrintLastError();
  361. return false;
  362. }
  363. return true;
  364. }
  365. bool CModules::GetModulesDataFromFile()
  366. {
  367. CModuleInfo * lpModuleInfo;
  368. // Read the Modules Header Line
  369. if (!m_lpInputFile->ReadFileLine())
  370. return false;
  371. // I need these near the end when I probe to see if the next module
  372. // is for this process...
  373. enum { BUFFER_SIZE = 128};
  374. // Unfortunately, when reading from the CSV file, the data is MBCS... so I need
  375. // to convert...
  376. // Read the first field (should be blank, unless this is a new collection type
  377. if (m_lpInputFile->ReadString())
  378. return true;
  379. // Read the second field (should be blank)
  380. if (m_lpInputFile->ReadString())
  381. return true;
  382. // Read the second field (should be blank)
  383. if (m_lpInputFile->ReadString())
  384. return true;
  385. // Local buffer for reading data...
  386. char szModulePath[_MAX_PATH+1];
  387. TCHAR tszModulePath[_MAX_PATH+1];
  388. bool fDone = false;
  389. bool fNew = false;
  390. while (!fDone)
  391. {
  392. // Read in the Module Path
  393. if (!m_lpInputFile->ReadString(szModulePath, _MAX_PATH+1))
  394. return true;
  395. CUtilityFunctions::CopyAnsiStringToTSTR(szModulePath, tszModulePath, _MAX_PATH+1);
  396. // If "-MATCH" was specified, look to see if this filename meets our criteria
  397. // before we save this away in our module cache...
  398. if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszModulePath))
  399. {
  400. // Okay... read to the start of the next line...
  401. if (!m_lpInputFile->ReadFileLine())
  402. goto cleanup;
  403. goto probe_line; // We save having to get the module info again for this module...
  404. }
  405. // Okay, let's go ahead and get a ModuleInfo Object from our cache...
  406. // If pfNew returns TRUE, then this object is new and we'll need
  407. // to populate it with data...
  408. lpModuleInfo = m_lpModuleInfoCache->AddNewModuleInfoObject(tszModulePath, &fNew);
  409. if (false == fNew)
  410. {
  411. // We may have the object in the cache... now we need to
  412. // save a pointer to this object in our Process Info list
  413. AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
  414. // Okay... read to the start of the next line...
  415. if ( !m_lpInputFile->ReadFileLine() )
  416. goto cleanup;
  417. goto probe_line; // We save having to get the module info again for this module...
  418. }
  419. // Not in the cache... so we need to init it, and get the module info...
  420. if (!lpModuleInfo->Initialize(m_lpInputFile, m_lpOutputFile, NULL))
  421. {
  422. return false; // Hmmm... memory error?
  423. }
  424. // Let's do it!! Populate the ModuleInfo object with data!!!!
  425. if (!lpModuleInfo->GetModuleInfo(tszModulePath, false, 0, true))
  426. {
  427. // Well, we tried and failed...
  428. return false;
  429. }
  430. // Start obtaining information about the modules...
  431. if (!AddNewModuleInfoObject(lpModuleInfo))
  432. { // Failure adding the node.... This is pretty serious...
  433. return false;
  434. }
  435. // Okay, let's go ahead and probe to see what's coming...
  436. probe_line:
  437. if ( m_lpInputFile->EndOfFile() )
  438. goto cleanup;
  439. // Read the first field (should be blank, unless this is a new collection type
  440. if (m_lpInputFile->ReadString())
  441. goto cleanup;
  442. // Read the second field (should be blank)
  443. if (m_lpInputFile->ReadString())
  444. return true;
  445. // Read the second field (should be blank)
  446. if (m_lpInputFile->ReadString())
  447. return true;
  448. }
  449. cleanup:
  450. // We need to reset out pointer so the functions above us can re-read
  451. // them (they expect to)...
  452. m_lpInputFile->ResetBufferPointerToStart();
  453. return true;
  454. }
  455. // We need to enumerate device drivers on this system
  456. bool CModules::GetModulesDataFromDeviceDrivers()
  457. {
  458. LPVOID * lpImageBaseArray = NULL;
  459. DWORD dwImageBaseArraySizeUsed, dwImageBaseArraySize, dwNumberOfDeviceDrivers, dwIndex;
  460. TCHAR tszModulePath[_MAX_PATH];
  461. CModuleInfo * lpModuleInfo = NULL;
  462. bool fReturn = false, fNew = false;
  463. // NOTE: In the documentation, the third parameter of
  464. // EnumProcesses is named cbNeeded, which implies that you
  465. // can call the function once to find out how much space to
  466. // allocate for a buffer and again to fill the buffer.
  467. // This is not the case. The cbNeeded parameter returns
  468. // the number of PIDs returned, so if your buffer size is
  469. // zero cbNeeded returns zero.
  470. dwImageBaseArraySize = 256 * sizeof( LPVOID ) ;
  471. do
  472. {
  473. if( lpImageBaseArray )
  474. { // Hmm.. we've been through this loop already, double the HeapSize and try again.
  475. delete [] lpImageBaseArray;
  476. dwImageBaseArraySize *= 2 ;
  477. }
  478. lpImageBaseArray = (LPVOID *) new DWORD[dwImageBaseArraySize];
  479. if( lpImageBaseArray == NULL )
  480. {
  481. goto error_cleanup;
  482. }
  483. // Query the system for the total number of processes
  484. if( !g_lpDelayLoad->EnumDeviceDrivers(lpImageBaseArray, dwImageBaseArraySize, &dwImageBaseArraySizeUsed ) )
  485. {
  486. // It's bad if we can't enum device drivers... no place to go but to bail out...
  487. goto error_cleanup;
  488. }
  489. } while( dwImageBaseArraySizeUsed == dwImageBaseArraySize );
  490. // How many DeviceDrivers did we get?
  491. dwNumberOfDeviceDrivers = dwImageBaseArraySizeUsed / sizeof( LPVOID ) ;
  492. // Loop through each Device Drivers
  493. for(dwIndex = 0 ; dwIndex < dwNumberOfDeviceDrivers; dwIndex++ )
  494. {
  495. // Spin until we get a device driver filename!
  496. if (!g_lpDelayLoad->GetDeviceDriverFileName(lpImageBaseArray[dwIndex], tszModulePath, _MAX_PATH))
  497. continue;
  498. CUtilityFunctions::UnMungePathIfNecessary(tszModulePath);
  499. // For some reason, even though GetDeviceDriverFileName() is supposed to return the fullpath to the device
  500. // driver... it don't always... sometimes it returns only the base file name...
  501. CUtilityFunctions::FixupDeviceDriverPathIfNecessary(tszModulePath, _MAX_PATH);
  502. if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszModulePath))
  503. continue;
  504. // Okay, let's go ahead and get a ModuleInfo Object from our cache...
  505. // If pfNew returns TRUE, then this object is new and we'll need
  506. // to populate it with data...
  507. lpModuleInfo = m_lpModuleInfoCache->AddNewModuleInfoObject(tszModulePath, &fNew);
  508. if (false == fNew)
  509. {
  510. // We may have the object in the cache... now we need to
  511. // save a pointer to this object in our Process Info list
  512. AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
  513. continue; // We save having to get the module info again for this module...
  514. }
  515. // Not in the cache... so we need to init it, and get the module info...
  516. if (!lpModuleInfo->Initialize(m_lpInputFile, m_lpOutputFile, NULL))
  517. {
  518. continue;
  519. }
  520. // Let's do it!! Populate the ModuleInfo object with data!!!!
  521. if (!lpModuleInfo->GetModuleInfo(tszModulePath, false, 0, false))
  522. {
  523. // Well, we tried and failed...
  524. continue;
  525. }
  526. // We may have the object in the cache... now we need to
  527. // save a pointer to this object in our Process Info list
  528. if (!AddNewModuleInfoObject(lpModuleInfo))
  529. { // Failure adding the node.... This is pretty serious...
  530. continue;
  531. }
  532. }
  533. fReturn = true;
  534. goto cleanup;
  535. error_cleanup:
  536. cleanup:
  537. if (lpImageBaseArray)
  538. {
  539. delete [] lpImageBaseArray;
  540. }
  541. return fReturn;
  542. }