//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1999 - 2000 // // File: processinfo.cpp // //-------------------------------------------------------------------------- // ProcessInfo.cpp: implementation of the CProcessInfo class. // ////////////////////////////////////////////////////////////////////// #ifndef NO_STRICT #ifndef STRICT #define STRICT 1 #endif #endif /* NO_STRICT */ #include #include #include #include #include "Globals.h" #include "ProcessInfo.h" #include "ProcessInfoNode.h" #include "ProgramOptions.h" #include "Processes.h" #include "ModuleInfo.h" #include "ModuleInfoNode.h" #include "ModuleInfoCache.h" #include "FileData.h" #include "UtilityFunctions.h" #include "DmpFile.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CProcessInfo::CProcessInfo() { m_fInitialized = false; m_tszProcessName= NULL; m_iProcessID = 0; m_lpModuleInfoHead = NULL; m_hModuleInfoHeadMutex = NULL; m_iNumberOfModules = 0; m_lpInputFile = NULL; m_lpOutputFile = NULL; m_lpModuleInfoCache = NULL; m_hModuleInfoHeadMutex = NULL; m_lpDmpFile = NULL; } CProcessInfo::~CProcessInfo() { if (m_tszProcessName) delete [] m_tszProcessName; WaitForSingleObject(m_hModuleInfoHeadMutex, INFINITE); // If we have Module Info Objects... nuke them now... if (m_lpModuleInfoHead) { CModuleInfoNode * lpModuleInfoNodePointer = m_lpModuleInfoHead; CModuleInfoNode * lpModuleInfoNodePointerToDelete = m_lpModuleInfoHead; // Traverse the linked list to the end.. while (lpModuleInfoNodePointer) { // Keep looking for the end... // Advance our pointer to the next node... lpModuleInfoNodePointer = lpModuleInfoNodePointer->m_lpNextModuleInfoNode; // Delete the one behind us... delete lpModuleInfoNodePointerToDelete; // Set the node to delete to the current... lpModuleInfoNodePointerToDelete = lpModuleInfoNodePointer; } // Now, clear out the Head pointer... m_lpModuleInfoHead = NULL; } // Be a good citizen and release the Mutex ReleaseMutex(m_hModuleInfoHeadMutex); // Now, close the Mutex if (m_hModuleInfoHeadMutex) { CloseHandle(m_hModuleInfoHeadMutex); m_hModuleInfoHeadMutex = NULL; } } //bool CProcessInfo::Initialize(CProgramOptions *lpProgramOptions, CModuleInfoCache * lpModuleInfoCache, CFileData * lpInputFile, CFileData * lpOutputFile) bool CProcessInfo::Initialize(CModuleInfoCache * lpModuleInfoCache, CFileData * lpInputFile, CFileData * lpOutputFile, CDmpFile * lpDmpFile) { if (lpModuleInfoCache == NULL) return false; // Let's save off big objects so we don't have to keep passing this to // our methods... m_lpInputFile = lpInputFile; m_lpOutputFile = lpOutputFile; m_lpModuleInfoCache = lpModuleInfoCache; m_lpDmpFile = lpDmpFile; m_hModuleInfoHeadMutex = CreateMutex(NULL, FALSE, NULL); if (m_hModuleInfoHeadMutex == NULL) return false; m_fInitialized = true; return true; } bool CProcessInfo::EnumerateModules(DWORD iProcessID, CProcesses * lpProcesses, LPTSTR tszProcessName) { bool fReturn = true; // Is this being collected interactively? if (g_lpProgramOptions->GetMode(CProgramOptions::InputProcessesFromLiveSystemMode)) { // Invoke the correct Process Collection Method if (lpProcesses->GetProcessCollectionMethod() == CProcesses::TOOLHELP32_METHOD) { fReturn = EnumerateModulesForRunningProcessUsingTOOLHELP32(iProcessID, tszProcessName); } else if (lpProcesses->GetProcessCollectionMethod() == CProcesses::PSAPI_METHOD) { fReturn = EnumerateModulesForRunningProcessUsingPSAPI(iProcessID); } } // Is this being collected from a file? if (g_lpProgramOptions->GetMode(CProgramOptions::InputCSVFileMode)) { fReturn = EnumerateModulesFromFile(iProcessID, tszProcessName); } return fReturn; } bool CProcessInfo::fModuleNameMatches(LPTSTR tszProcessName, LPTSTR tszModulePath) { if (!tszProcessName || !tszModulePath) return false; TCHAR fname1[_MAX_FNAME], fname2[_MAX_FNAME]; TCHAR ext1[_MAX_EXT], ext2[_MAX_EXT]; _tsplitpath( tszProcessName, NULL, NULL, fname1, ext1 ); _tsplitpath( tszModulePath, NULL, NULL, fname2, ext2 ); if (!ext1 && _tcsicmp(ext1, ext2)) return false; // Extensions must match (if provided on process name) if (_tcsicmp(fname1, fname2)) return false; // Filename must match // Do we care about a full path? If so, we can add in drive and dir vars... return true; } // // This function takes a provided tszFileName, and looks to see if it has an // extension of EXE. If it does, it's saved off... bool CProcessInfo::fIsProcessName(LPTSTR tszFileName) { if (!tszFileName) return false; TCHAR fname[_MAX_FNAME]; TCHAR ext[_MAX_EXT]; _tsplitpath( tszFileName, NULL, NULL, fname, ext ); if (!ext || _tcsicmp(ext, TEXT(".EXE"))) return false; // Extensions must match (if provided on process name) // Let's save off the process name... m_tszProcessName = new TCHAR[_tcsclen(fname)+_tcsclen(ext)+1]; if (m_tszProcessName == NULL) return false; _stprintf(m_tszProcessName, TEXT("%s%s"), _tcsupr(fname), _tcsupr(ext)); // Yup! It's the Process Name... return true; } bool CProcessInfo::AddNewModuleInfoObject(CModuleInfo *lpModuleInfo) { if (!m_fInitialized) return false; // First, create a ModuleInfoNode object and then attach it to the bottom of the // linked list of nodes... CModuleInfoNode * lpModuleInfoNode = new CModuleInfoNode(lpModuleInfo); if (lpModuleInfoNode == NULL) return false; // Couldn't allocate memory.. // Acquire Mutex object to protect the linked-list... WaitForSingleObject(m_hModuleInfoHeadMutex, INFINITE); CModuleInfoNode * lpModuleInfoNodePointer = m_lpModuleInfoHead; if (lpModuleInfoNodePointer) { // Traverse the linked list to the end.. while (lpModuleInfoNodePointer->m_lpNextModuleInfoNode) { // Keep looking for the end... lpModuleInfoNodePointer = lpModuleInfoNodePointer->m_lpNextModuleInfoNode; } lpModuleInfoNodePointer->m_lpNextModuleInfoNode = lpModuleInfoNode; } else { // First time through, the Process Info Head pointer is null... m_lpModuleInfoHead = lpModuleInfoNode; } // Be a good citizen and release the Mutex ReleaseMutex(m_hModuleInfoHeadMutex); InterlockedIncrement(&m_iNumberOfModules); return true; } bool CProcessInfo::OutputProcessData(CollectionTypes enumCollectionType, bool fCSVFileContext, bool fDumpHeader) { if (g_lpProgramOptions->GetMode(CProgramOptions::PrintTaskListMode)) { if ( g_lpProgramOptions->IsRunningWindowsNT() ) { // Provide TLIST like output (though no window text info) _tprintf(TEXT("%4d %s\n"), m_iProcessID, m_tszProcessName); } else { // Provide TLIST like output (though no window text info) _tprintf(TEXT("%9d %s\n"), m_iProcessID, m_tszProcessName); } return true; } // Output to STDOUT? if ( !g_lpProgramOptions->GetMode(CProgramOptions::QuietMode) ) { // Output to Stdout? if (!OutputProcessDataToStdout(enumCollectionType, fCSVFileContext, fDumpHeader)) return false; } if (!g_lpProgramOptions->GetMode(CProgramOptions::QuietMode)) { CUtilityFunctions::OutputLineOfDashes(); // Output to STDOUT _tprintf(TEXT("\nProcess Name [%s] - PID=%d (0x%x) - "), m_tszProcessName, m_iProcessID, m_iProcessID); } // Output to file? if (g_lpProgramOptions->GetMode(CProgramOptions::OutputCSVFileMode)) { // Try and output to file... if (!OutputProcessDataToFile(enumCollectionType, fDumpHeader)) return false; } if (m_lpModuleInfoHead) { if (!g_lpProgramOptions->GetMode(CProgramOptions::QuietMode)) { _tprintf(TEXT("%d modules recorded\n\n"), m_iNumberOfModules); CUtilityFunctions::OutputLineOfDashes(); _tprintf(TEXT("\n")); } CModuleInfoNode * lpCurrentModuleInfoNode = m_lpModuleInfoHead; unsigned int dwModuleNumber = 1; while (lpCurrentModuleInfoNode) { // We have a node... print out Module Info for it... if (lpCurrentModuleInfoNode->m_lpModuleInfo) { lpCurrentModuleInfoNode->m_lpModuleInfo->OutputData(m_tszProcessName, m_iProcessID, dwModuleNumber); dwModuleNumber++; } lpCurrentModuleInfoNode = lpCurrentModuleInfoNode->m_lpNextModuleInfoNode; } } else { if (!g_lpProgramOptions->GetMode(CProgramOptions::QuietMode) && fDumpHeader) { _tprintf(TEXT("no recorded modules\n\n")); CUtilityFunctions::OutputLineOfDashes(); _tprintf(TEXT("\n")); } } return true; } //bool CProcessInfo::OutputProcessDataToStdout(LPCTSTR tszOutputContext, bool fDumpHeader) bool CProcessInfo::OutputProcessDataToStdout(CollectionTypes enumCollectionType, bool fCSVFileContext, bool fDumpHeader) { if (fDumpHeader) { CUtilityFunctions::OutputLineOfStars(); _tprintf(TEXT("%s - Printing Process Information for 1 Process.\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel); _tprintf(TEXT("%s - Context: %s\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel, fCSVFileContext ? g_tszCollectionArray[enumCollectionType].tszCSVContext : g_tszCollectionArray[enumCollectionType].tszLocalContext); CUtilityFunctions::OutputLineOfStars(); } return true; } bool CProcessInfo::OutputProcessDataToFile(CollectionTypes enumCollectionType, bool fDumpHeader) { // We skip output of the [processes ]header if -E was specified... if (!g_lpProgramOptions->GetMode(CProgramOptions::ExceptionMonitorMode) && fDumpHeader) { // Write out the Process tag so I can detect this output format... if (!m_lpOutputFile->WriteString(TEXT("\r\n")) || !m_lpOutputFile->WriteString(g_tszCollectionArray[enumCollectionType].tszCSVLabel) || !m_lpOutputFile->WriteString(TEXT("\r\n")) ) { _tprintf(TEXT("Failure writing CSV header to file [%s]!"), m_lpOutputFile->GetFilePath()); m_lpOutputFile->PrintLastError(); return false; } } // We skip output of the [processes ]header if -E was specified... if (g_lpProgramOptions->GetMode(CProgramOptions::ExceptionMonitorMode) && fDumpHeader) { // Write out the header... for the -E option... if (!m_lpOutputFile->WriteString(TEXT("Module Path,Symbol Status,Time/Date String,File Version,Company Name,File Description,File Time/Date String,Local DBG Status,Local DBG,Local PDB Status,Local PDB\r\n"))) { _tprintf(TEXT("Failure writing CSV header to file [%s]!"), m_lpOutputFile->GetFilePath()); m_lpOutputFile->PrintLastError(); return false; } } else { if (fDumpHeader) { // Write out the Process Header if (!m_lpOutputFile->WriteString(g_tszCollectionArray[enumCollectionType].tszCSVColumnHeaders)) { _tprintf(TEXT("Failure writing CSV header to file [%s]!"), m_lpOutputFile->GetFilePath()); m_lpOutputFile->PrintLastError(); return false; } } } return true; } bool CProcessInfo::EnumerateModulesForRunningProcessUsingPSAPI(DWORD iProcessID) { HMODULE hMod[1024] ; HANDLE hProcess = NULL; TCHAR tszFileName[_MAX_PATH] ; DWORD cbNeeded; bool fReturn = true; // Optimisim ;) tszFileName[0] = 0 ; CModuleInfo * lpModuleInfo = NULL; // Open the process (if we can... security does not // permit every process in the system). if (iProcessID) { // Only try to open a Process ID if it's not 0 hProcess = OpenProcess( PROCESS_QUERY_INFORMATION| PROCESS_VM_READ, FALSE, iProcessID ) ; } if( hProcess != NULL ) { // Save off our PID (in case we need it later?) m_iProcessID = iProcessID; // Now, get a handle to each of the modules // in our target process... // Here we call EnumProcessModules to get only the // first module in the process this is important, // because this will be the .EXE module for which we // will retrieve the full path name in a second. if( g_lpDelayLoad->EnumProcessModules( hProcess, hMod, sizeof( hMod ), &cbNeeded ) ) { int iNumberOfModules = cbNeeded / sizeof(HMODULE); bool fProcessNameFound = false; bool fNew = false; for(int i=0; iGetModuleFileNameEx( hProcess, hMod[i], tszFileName, sizeof( tszFileName ) ) ) { tszFileName[0] = 0 ; } else { CUtilityFunctions::UnMungePathIfNecessary(tszFileName); // We need a full path to the module to do anything useful with it... // At this point, let's ... party... if (!fProcessNameFound) fProcessNameFound = fIsProcessName(tszFileName); // First, if we were provided a Process name on the commandline, we // need to look for a match on the 1st module... if (i == 0 && g_lpProgramOptions->GetProcessName()) { if (!fModuleNameMatches(g_lpProgramOptions->GetProcessName(), tszFileName)) { // Bail if this is not a match, and we requested one... fReturn = false; goto cleanup; } } // Are we ONLY interested in the process list? if (g_lpProgramOptions->GetMode(CProgramOptions::PrintTaskListMode)) { // All we want is process name.. bail before collecting module info... fReturn = true; goto cleanup; } // If "-MATCH" was specified, look to see if this filename meets our criteria // before we save this away in our module cache... if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszFileName)) continue; // Okay, let's go ahead and get a ModuleInfo Object from our cache... // If pfNew returns TRUE, then this object is new and we'll need // to populate it with data... lpModuleInfo = m_lpModuleInfoCache->AddNewModuleInfoObject(tszFileName, &fNew); if (false == fNew) { // We may have the object in the cache... now we need to // save a pointer to this object in our Process Info list AddNewModuleInfoObject(lpModuleInfo); // Just do our best... continue; // We save having to get the module info again for this module... } // Not in the cache... so we need to init it, and get the module info... // Okay, let's create a ModuleInfo object and pass this down // routines that will populate it full of data... if (!lpModuleInfo->Initialize(NULL, m_lpOutputFile, NULL)) { continue; // Hmmm... memory error? } // Let's do it!! Populate the ModuleInfo object with data!!!! if (!lpModuleInfo->GetModuleInfo(tszFileName)) { // Well, for now we've at least got the path to the module... // Go ahead and get another module.. continue; } // Start obtaining information about the modules... // We may have the object in the cache... now we need to // save a pointer to this object in our Process Info list if (!AddNewModuleInfoObject(lpModuleInfo)) { // Failure adding the node.... This is pretty serious... continue; } } } fReturn = true; // Looks good... } else { fReturn = false; if (!g_lpProgramOptions->GetProcessName()) { // Let's not be so hasty... we couldn't enum modules, but to be friendly we can probably put a name // to the Process (based on the Process ID)... // // This Process ID tends to be "System" // // On Windows 2000, the Process ID tends to be 8 // // On Windows NT 4.0, this Process ID tends to be 2 switch (m_iProcessID) { case 2: case 8: SetProcessName(TEXT("SYSTEM")); fReturn = true; break; default: // Couldn't enumerate modules... fReturn = false; } } } cleanup: CloseHandle( hProcess ) ; } else { // Gotta be able to open the process to look at it... fReturn = false; if (!g_lpProgramOptions->GetProcessName()) { // Let's not be so hasty... we couldn't enum modules, but to be friendly we can probably put a name // to the Process (based on the Process ID)... // // On Windows 2000, the only Process ID we can't open at all tends to be the "System Idle Process" switch (m_iProcessID) { case 0: SetProcessName(TEXT("[SYSTEM PROCESS]")); fReturn = true; break; default: // Couldn't enumerate modules... fReturn = false; } } } return fReturn; } bool CProcessInfo::EnumerateModulesFromFile(DWORD iProcessID, LPTSTR tszProcessName) { CModuleInfo * lpModuleInfo; // I need these near the end when I probe to see if the next module // is for this process... enum { BUFFER_SIZE = 128}; char szTempProcessName[BUFFER_SIZE]; char szProcessName[BUFFER_SIZE]; DWORD iTempProcessID; // Let's save away the Process Name... // Unfortunately, when reading from the CSV file, the data is MBCS... so I need // to convert... CUtilityFunctions::CopyTSTRStringToAnsi(tszProcessName, szProcessName, BUFFER_SIZE); // Copy the Process ID m_iProcessID = iProcessID; // Local buffer for reading data... char szModulePath[_MAX_PATH+1]; TCHAR tszModulePath[_MAX_PATH+1]; bool fDone = false; bool fNew = false; while (!fDone) { // Read in the Module Path if (!m_lpInputFile->ReadString(szModulePath, _MAX_PATH+1)) return true; CUtilityFunctions::CopyAnsiStringToTSTR(szModulePath, tszModulePath, _MAX_PATH+1); // If "-MATCH" was specified, look to see if this filename meets our criteria // before we save this away in our module cache... if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszModulePath)) { // Okay... read to the start of the next line... if (!m_lpInputFile->ReadFileLine()) goto cleanup; goto probe_line; // We save having to get the module info again for this module... } // Okay, let's go ahead and get a ModuleInfo Object from our cache... // If pfNew returns TRUE, then this object is new and we'll need // to populate it with data... lpModuleInfo = m_lpModuleInfoCache->AddNewModuleInfoObject(tszModulePath, &fNew); if (false == fNew) { // We may have the object in the cache... now we need to // save a pointer to this object in our Process Info list AddNewModuleInfoObject(lpModuleInfo); // Just do our best... // Okay... read to the start of the next line... if (!m_lpInputFile->ReadFileLine()) goto cleanup; goto probe_line; // We save having to get the module info again for this module... } // Not in the cache... so we need to init it, and get the module info... if (!lpModuleInfo->Initialize(m_lpInputFile, m_lpOutputFile, NULL)) { return false; // Hmmm... memory error? } // Let's do it!! Populate the ModuleInfo object with data!!!! if (!lpModuleInfo->GetModuleInfo(tszModulePath, false, 0, true) ) { // Well, we tried and failed... return false; } // Start obtaining information about the modules... if (!AddNewModuleInfoObject(lpModuleInfo)) { // Failure adding the node.... This is pretty serious... return false; } // Okay, let's go ahead and probe to see what's coming... probe_line: // Read the first field (should be blank, unless this is a new collection type if (m_lpInputFile->ReadString()) goto cleanup; // Read the Process Name... if (!m_lpInputFile->ReadString(szTempProcessName, BUFFER_SIZE)) goto cleanup; // Compare the process name to the current process... if (_stricmp(szTempProcessName, szProcessName)) goto cleanup; // Read the Process ID if (!m_lpInputFile->ReadDWORD(&iTempProcessID)) goto cleanup; // Compare the process ID to the current process ID if (iTempProcessID != iProcessID) goto cleanup; } cleanup: // We need to reset out pointer so the functions above us can re-read // them (they expect to)... m_lpInputFile->ResetBufferPointerToStart(); return true; } bool CProcessInfo::EnumerateModulesForRunningProcessUsingTOOLHELP32(DWORD iProcessID, LPTSTR tszProcessName) { BOOL bFlag; MODULEENTRY32 modentry; TCHAR tszFileName[_MAX_PATH]; bool fProcessNameFound = false; bool fProcessNameProvided = false; bool fReturn = false; bool fNew = false; int iNumberOfModules = 0; HANDLE hSnapShot = INVALID_HANDLE_VALUE; CModuleInfo * lpModuleInfo = NULL; // Save off our PID (in case we need it later?) m_iProcessID = iProcessID; if (tszProcessName && SetProcessName(tszProcessName)) { fProcessNameProvided = true; } // If we were provided a process name to match, we can do it here... if ( fProcessNameProvided && g_lpProgramOptions->GetProcessName() ) { // Let's go ahead and look to see if this is a module name match fProcessNameFound = fModuleNameMatches(g_lpProgramOptions->GetProcessName(), GetProcessName()); // Quit now if we can... if (fProcessNameFound == false) goto cleanup; } // If we're doing this for TLIST output... then we already have the process name... // We're done! if (g_lpProgramOptions->GetMode(CProgramOptions::PrintTaskListMode)) { fReturn = true; goto cleanup; } // Get a handle to a Toolhelp snapshot of the systems processes. hSnapShot = g_lpDelayLoad->CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, iProcessID); if( hSnapShot == INVALID_HANDLE_VALUE ) { goto cleanup; } // Get the first process' information. modentry.dwSize = sizeof(MODULEENTRY32) ; bFlag = g_lpDelayLoad->Module32First( hSnapShot, &modentry ) ; // While there are modules, keep looping. while( bFlag ) { // We have a new module for this process! iNumberOfModules++; // Copy the path! _tcscpy(tszFileName, modentry.szExePath); //#ifdef _DEBUG // _tprintf(TEXT("[%d] Module = %s\n"), iNumberOfModules, tszFileName); //#endif CUtilityFunctions::UnMungePathIfNecessary(tszFileName); // If "-MATCH" was specified, look to see if this filename meets our criteria // before we save this away in our module cache... if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszFileName)) goto getnextmodule; // Okay, let's go ahead and get a ModuleInfo Object from our cache... // If pfNew returns TRUE, then this object is new and we'll need // to populate it with data... lpModuleInfo = m_lpModuleInfoCache->AddNewModuleInfoObject(tszFileName, &fNew); if (false == fNew) { // We may have the object in the cache... now we need to // save a pointer to this object in our Process Info list AddNewModuleInfoObject(lpModuleInfo); // Just do our best... // We save having to get the module info again for this module... goto getnextmodule; } // Not in the cache... so we need to init it, and get the module info... // Okay, let's create a ModuleInfo object and pass this down // routines that will populate it full of data... if (lpModuleInfo->Initialize(NULL, m_lpOutputFile, NULL)) { // Let's do it!! Populate the ModuleInfo object with data!!!! if (lpModuleInfo->GetModuleInfo(tszFileName)) { // Start obtaining information about the modules... // We may have the object in the cache... now we need to // save a pointer to this object in our Process Info list if (AddNewModuleInfoObject(lpModuleInfo)) { } } } getnextmodule: // Get the next Module... modentry.dwSize = sizeof(MODULEENTRY32) ; bFlag = g_lpDelayLoad->Module32Next( hSnapShot, &modentry ); } fReturn = true; cleanup: if (hSnapShot != INVALID_HANDLE_VALUE) CloseHandle(hSnapShot); return fReturn; } bool CProcessInfo::SetProcessName(LPTSTR tszFileName) { // Confirm we were given a process name... if (!tszFileName) return false; TCHAR fname[_MAX_FNAME]; TCHAR ext[_MAX_EXT]; TCHAR tszTempFileName[_MAX_FNAME+_MAX_EXT+1]; // Let's extract the filename from the module path _tsplitpath( tszFileName, NULL, NULL, fname, ext ); // Reconstruct the filename... _stprintf(tszTempFileName, TEXT("%s%s"), _tcsupr(fname), _tcsupr(ext)); // Let's free anything that's already here... if (m_tszProcessName) delete [] m_tszProcessName; // No conversion necessary... copy over... m_tszProcessName = new TCHAR[_tcslen(tszTempFileName)+1]; if (!m_tszProcessName) return false; _tcscpy(m_tszProcessName, tszTempFileName); return true; } LPTSTR CProcessInfo::GetProcessName() { return m_tszProcessName; } bool CProcessInfo::GetProcessData() { // Is this being collected from a file? if (g_lpProgramOptions->GetMode(CProgramOptions::InputCSVFileMode)) GetProcessDataFromFile(); return true; } bool CProcessInfo::GetProcessDataFromFile() { // Read the Process Header Line if (!m_lpInputFile->ReadFileLine()) return false; // Currently, we don't actually read the data... enum { BUFFER_SIZE = 128}; char szProcessName[BUFFER_SIZE]; TCHAR tszProcessName[BUFFER_SIZE]; DWORD iProcessID; // Read the first field (should be blank, unless this is a new collection type if (m_lpInputFile->ReadString()) return true; bool fReturn = true; while (fReturn == true) { // Read the process name... if (0 == m_lpInputFile->ReadString(szProcessName, BUFFER_SIZE)) break; if (!m_lpInputFile->ReadDWORD(&iProcessID)) { fReturn = false; break; } // We need to convert this to Unicode possibly... (it will be copied in EnumModules()) CUtilityFunctions::CopyAnsiStringToTSTR(szProcessName, tszProcessName, BUFFER_SIZE); // Save the process name... SetProcessName(tszProcessName); // Enumerate the modules for the process if (!EnumerateModules(iProcessID, NULL, tszProcessName)) { fReturn = false; break; } // Before we read a new line... are we already pointing to the end? if (m_lpInputFile->EndOfFile()) { break; } // Read the first field (should be blank, unless this is a new collection type if (m_lpInputFile->ReadString()) break; } // We don't expect to find anything... return fReturn; }