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.
 
 
 
 
 
 

832 lines
23 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1999 - 2000
//
// File: processes.cpp
//
//--------------------------------------------------------------------------
// Processes.cpp: implementation of the CProcesses class.
//
//////////////////////////////////////////////////////////////////////
#ifndef NO_STRICT
#ifndef STRICT
#define STRICT 1
#endif
#endif /* NO_STRICT */
#include <WINDOWS.H>
#include <STDIO.H>
#include <TCHAR.H>
#include <stdlib.h>
#include "Globals.h"
#include "Processes.h"
#include "ProcessInfo.h"
#include "ProcessInfoNode.h"
#include "FileData.h"
#include "UtilityFunctions.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CProcesses::CProcesses()
{
m_fInitialized = false;
m_iNumberOfProcesses = 0;
m_enumProcessCollectionMethod = NO_METHOD;
// Contained Objects
m_lpProcessInfoHead = NULL;
m_ProcessInfoHeadMutex = NULL;
// m_lpProgramOptions = NULL;
m_lpModuleInfoCache = NULL;
m_lpOutputFile = NULL;
m_lpInputFile = NULL;
}
CProcesses::~CProcesses()
{
WaitForSingleObject(m_ProcessInfoHeadMutex, INFINITE);
// If we have Process Info Objects... nuke them now...
if (m_lpProcessInfoHead)
{
CProcessInfoNode * lpProcessInfoNodePointer = m_lpProcessInfoHead;
CProcessInfoNode * lpProcessInfoNodePointerToDelete = m_lpProcessInfoHead;
// Traverse the linked list to the end..
while (lpProcessInfoNodePointer)
{ // Keep looking for the end...
// Advance our pointer to the next node...
lpProcessInfoNodePointer = lpProcessInfoNodePointer->m_lpNextProcessInfoNode;
// Delete the one behind us...
delete lpProcessInfoNodePointerToDelete;
// Set the node to delete to the current...
lpProcessInfoNodePointerToDelete = lpProcessInfoNodePointer;
}
// Now, clear out the Head pointer...
m_lpProcessInfoHead = NULL;
}
// Be a good citizen and release the Mutex
ReleaseMutex(m_ProcessInfoHeadMutex);
// Now, close the Mutex
if (m_ProcessInfoHeadMutex)
{
CloseHandle(m_ProcessInfoHeadMutex);
m_ProcessInfoHeadMutex = NULL;
}
}
//bool CProcesses::Initialize(CProgramOptions * lpProgramOptions, CModuleInfoCache * lpModuleInfoCache, CFileData * lpInputFile, CFileData * lpOutputFile)
bool CProcesses::Initialize(CModuleInfoCache * lpModuleInfoCache, CFileData * lpInputFile, CFileData * lpOutputFile)
{
// We need the following objects to do business...
// if ( lpProgramOptions == NULL || lpModuleInfoCache == NULL)
if ( lpModuleInfoCache == NULL)
return false;
// Let's save away our program options (beats passing it as an
// argument to every method...)
// m_lpProgramOptions = lpProgramOptions;
m_lpInputFile = lpInputFile;
m_lpOutputFile = lpOutputFile;
m_lpModuleInfoCache = lpModuleInfoCache;
m_ProcessInfoHeadMutex = CreateMutex(NULL, FALSE, NULL);
if (m_ProcessInfoHeadMutex == NULL)
return false;
// We only need to grab these exported functions if we intend to
// actively query our local machine's processes directly...
if (g_lpProgramOptions->GetMode(CProgramOptions::InputProcessesFromLiveSystemMode))
{
// PSAPI.DLL API's ARE NOW PREFERRED!!
// It doesn't tend to hang when enumerating modules for a process that is being debugged.
// The Toolhelp32 APIs seem to hang occasionally taking a snapshot of a process being debugged
// and this impacts Exception Monitor (which runs from a script against a process under
// windbg)
if ( g_lpProgramOptions->IsRunningWindowsNT() )
{
// Get the functions for Windows NT 4.0/2000
// Load library and get the procedures explicitly. We do
// this so that we don't have to worry about modules using
// this code failing to load under Windows 95, because
// it can't resolve references to the PSAPI.DLL.
if (g_lpDelayLoad->Initialize_PSAPI())
{
m_enumProcessCollectionMethod = PSAPI_METHOD;
} else
{
_tprintf(TEXT("Unable to load PSAPI.DLL, which may be required for enumeration of processes.\n"));
}
}
if ( m_enumProcessCollectionMethod == NO_METHOD )
{
if (g_lpDelayLoad->Initialize_TOOLHELP32())
{
m_enumProcessCollectionMethod = TOOLHELP32_METHOD;
} else
{
_tprintf(TEXT("KERNEL32.DLL is missing required function entry points!!\n"));
}
}
// On Windows NT, we need to enable SeDebugPrivilege to open some processes...
if ( ( m_enumProcessCollectionMethod != NO_METHOD ) &&
g_lpProgramOptions->IsRunningWindowsNT() )
{
HANDLE hOurProcessToken = 0;
bool fPrivilegeSet = false;
// To permit as much access to obtain a process handle as possible,
// we need to set the SeDebugPrivilege on our process handle, we can
// then open nearly any process...
if(OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hOurProcessToken))
{
// We got our Process Token...
if(SetPrivilege(hOurProcessToken, SE_DEBUG_NAME, TRUE))
{
fPrivilegeSet = true;
}
}
if (!fPrivilegeSet)
{
_tprintf(TEXT("\nWARNING: A required privilege (SeDebugPrivilege) is not held by the user\n"));
_tprintf(TEXT("running this program. Due to security, some processes running on this\n"));
_tprintf(TEXT("system may not be accessible. An administrator of this machine can grant\n"));
_tprintf(TEXT("you this privilege by using User Manager to enable the advanced User Right\n"));
_tprintf(TEXT("\"Debug Programs\" to enable complete access to this system.\n"));
}
if (hOurProcessToken)
CloseHandle(hOurProcessToken);
}
// We are initialized if we were able to enable a Process Collection Method
m_fInitialized = ( m_enumProcessCollectionMethod != NO_METHOD );
} else
{
m_fInitialized = true;
}
return m_fInitialized;
}
bool CProcesses::GetProcessesDataForRunningProcessesUsingPSAPI()
{
LPDWORD lpdwPIDs = NULL;
DWORD dwProcessIDHeapSizeUsed, dwProcessIDHeapSize, dwIndex ;
CProcessInfo * lpProcessInfo = NULL;
bool fRetval = false;
if (!m_fInitialized)
return false;
// It's possible the user provided a PID directly... if so,
// we can circumvent the whole search of PIDs on the system...
if (g_lpProgramOptions->GetProcessID())
{
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
lpProcessInfo = new CProcessInfo();
if (lpProcessInfo == NULL)
goto error_cleanup;
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, NULL, m_lpOutputFile, NULL))
{
goto error_cleanup;
}
if (lpProcessInfo->EnumerateModules(g_lpProgramOptions->GetProcessID(), this, NULL))
{
// Success... add this to the Processes Object...
if (!AddNewProcessInfoObject(lpProcessInfo))
{ // Failure adding the node...
goto error_cleanup; // For now, let's just error on out...
}
} else
{
// Failure enumerating modules on the only PID of interest... very bad...
goto error_cleanup;
}
}
else
{ // Nope, we brute force this baby...
// Call the PSAPI function EnumProcesses to get all of the
// ProcID's currently in the system.
// NOTE: In the documentation, the third parameter of
// EnumProcesses is named cbNeeded, which implies that you
// can call the function once to find out how much space to
// allocate for a buffer and again to fill the buffer.
// This is not the case. The cbNeeded parameter returns
// the number of PIDs returned, so if your buffer size is
// zero cbNeeded returns zero.
// NOTE: The loop here ensures that we
// actually allocate a buffer large enough for all the
// PIDs in the system.
dwProcessIDHeapSize = 256 * sizeof( DWORD ) ;
lpdwPIDs = NULL ;
do
{
if( lpdwPIDs )
{ // Hmm.. we've been through this loop already, double the HeapSize and try again.
delete [] lpdwPIDs;
dwProcessIDHeapSize *= 2 ;
}
lpdwPIDs = (LPDWORD) new DWORD[dwProcessIDHeapSize];
if( lpdwPIDs == NULL )
{
goto error_cleanup;
}
// Query the system for the total number of processes
if( !g_lpDelayLoad->EnumProcesses( lpdwPIDs, dwProcessIDHeapSize, &dwProcessIDHeapSizeUsed ) )
{
// It's bad if we can't enum processes... no place to go but to bail out...
goto error_cleanup;
}
} while( dwProcessIDHeapSizeUsed == dwProcessIDHeapSize );
// How many ProcID's did we get?
DWORD dwNumberOfPIDs = dwProcessIDHeapSizeUsed / sizeof( DWORD ) ;
// Loop through each ProcID.
for( dwIndex = 0 ; dwIndex < dwNumberOfPIDs; dwIndex++ )
{
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
// Each Process gets its own
lpProcessInfo = new CProcessInfo();
if (lpProcessInfo == NULL)
goto error_cleanup;
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, NULL, m_lpOutputFile, NULL))
{ // Failure initializing the ProcessInfo object?!?
delete lpProcessInfo;
lpProcessInfo = NULL;
continue;
}
if (lpProcessInfo->EnumerateModules(lpdwPIDs[dwIndex], this, NULL))
{
// Success... add this to the Processes Object...
if (!AddNewProcessInfoObject(lpProcessInfo))
{ // Failure adding the node...
delete lpProcessInfo;
lpProcessInfo = NULL;
continue;
}
// For now, let's error out...
} else
{
// An error enumerating modules might be normal...
delete lpProcessInfo;
lpProcessInfo = NULL;
continue;
}
}
}
fRetval = true;
goto cleanup;
error_cleanup:
if (lpProcessInfo)
delete lpProcessInfo;
cleanup:
if (lpdwPIDs)
{
delete [] lpdwPIDs;
}
return fRetval;
}
bool CProcesses::AddNewProcessInfoObject(CProcessInfo * lpProcessInfo)
{
if (!m_fInitialized)
return false;
// First, create a ProcessInfoNode object and then attach it to the bottom of the
// linked list of nodes...
CProcessInfoNode * lpProcessInfoNode = new CProcessInfoNode(lpProcessInfo);
/*
#ifdef _DEBUG
_tprintf(TEXT("Adding Process Info Object for [%s]\n"), lpProcessInfo->m_tszProcessName);
#endif
*/
if (lpProcessInfoNode == NULL)
return false; // Couldn't allocate memory..
// Acquire Mutex object to protect the linked-list...
WaitForSingleObject(m_ProcessInfoHeadMutex, INFINITE);
CProcessInfoNode * lpProcessInfoNodePointer = m_lpProcessInfoHead;
if (lpProcessInfoNodePointer) {
// Traverse the linked list to the end..
while (lpProcessInfoNodePointer->m_lpNextProcessInfoNode)
{ // Keep looking for the end...
lpProcessInfoNodePointer = lpProcessInfoNodePointer->m_lpNextProcessInfoNode;
}
lpProcessInfoNodePointer->m_lpNextProcessInfoNode = lpProcessInfoNode;
}
else
{ // First time through, the Process Info Head pointer is null...
m_lpProcessInfoHead = lpProcessInfoNode;
}
// Be a good citizen and release the Mutex
ReleaseMutex(m_ProcessInfoHeadMutex);
InterlockedIncrement(&m_iNumberOfProcesses);
return true;
}
bool CProcesses::SetPrivilege(HANDLE hToken, LPCTSTR Privilege, bool bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
LUID luid;
TOKEN_PRIVILEGES tpPrevious = {0};
DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
if(!LookupPrivilegeValue( NULL, Privilege, &luid )) return false;
//
// first pass. get current privilege setting
//
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
&tpPrevious,
&cbPrevious
);
if (GetLastError() != ERROR_SUCCESS) return false;
//
// second pass. set privilege based on previous setting
//
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
if(bEnablePrivilege) {
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
}
else {
tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
tpPrevious.Privileges[0].Attributes);
}
AdjustTokenPrivileges(
hToken,
FALSE,
&tpPrevious,
cbPrevious,
NULL,
NULL
);
if (GetLastError() != ERROR_SUCCESS) return false;
return true;
}
bool CProcesses::OutputProcessesData(CollectionTypes enumCollectionType, bool fCSVFileContext, bool fDumpHeader)
{
// Output to file?
if ( !g_lpProgramOptions->GetMode(CProgramOptions::QuietMode) &&
!g_lpProgramOptions->GetMode(CProgramOptions::PrintTaskListMode) )
{
// Output to Stdout?
if (!OutputProcessesDataToStdout(enumCollectionType, fCSVFileContext, fDumpHeader))
return false;
}
// Output to file?
if (g_lpProgramOptions->GetMode(CProgramOptions::OutputCSVFileMode))
{
// Try and output to file...
if (!OutputProcessesDataToFile(enumCollectionType, fDumpHeader))
return false;
}
if (m_lpProcessInfoHead) {
CProcessInfoNode * lpCurrentProcessInfoNode = m_lpProcessInfoHead;
while (lpCurrentProcessInfoNode)
{
// We have a node... print out Process Info for it, then the Modules Data...
if (lpCurrentProcessInfoNode->m_lpProcessInfo)
{
lpCurrentProcessInfoNode->m_lpProcessInfo->OutputProcessData(enumCollectionType, fCSVFileContext, false);
}
lpCurrentProcessInfoNode = lpCurrentProcessInfoNode->m_lpNextProcessInfoNode;
}
}
return true;
}
bool CProcesses::OutputProcessesDataToStdout(CollectionTypes enumCollectionType, bool fCSVFileContext, bool fDumpHeader)
{
if (fDumpHeader)
{
// Output to stdout...
_tprintf(TEXT("\n"));
CUtilityFunctions::OutputLineOfStars();
_tprintf(TEXT("%s - Printing Process Information for %d Processes.\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel, m_iNumberOfProcesses);
_tprintf(TEXT("%s - Context: %s\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel, fCSVFileContext ? g_tszCollectionArray[enumCollectionType].tszCSVContext : g_tszCollectionArray[enumCollectionType].tszLocalContext);
CUtilityFunctions::OutputLineOfStars();
}
return true;
}
bool CProcesses::OutputProcessesDataToFile(CollectionTypes enumCollectionType, bool fDumpHeader)
{
// Don't write anything if there are no processes to report...
if (0 == m_iNumberOfProcesses)
return true;
if (fDumpHeader)
{
// We skip output of the [PROCESSES] header if -E was specified...
if (!g_lpProgramOptions->GetMode(CProgramOptions::ExceptionMonitorMode))
{
// Write out the Processes 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 have different output for -E
if (g_lpProgramOptions->GetMode(CProgramOptions::ExceptionMonitorMode))
{
// 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
{
// Write out the Processes 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 CProcesses::GetProcessesData()
{
// Is this being collected interactively?
if (g_lpProgramOptions->GetMode(CProgramOptions::InputProcessesFromLiveSystemMode))
{
// Invoke the correct Process Collection Method
if (GetProcessCollectionMethod() == TOOLHELP32_METHOD)
{
GetProcessesDataForRunningProcessesUsingTOOLHELP32();
}
else if (GetProcessCollectionMethod() == PSAPI_METHOD)
{
GetProcessesDataForRunningProcessesUsingPSAPI();
}
}
// Is this being collected from a file?
if (g_lpProgramOptions->GetMode(CProgramOptions::InputCSVFileMode))
GetProcessesDataFromFile();
return true;
}
bool CProcesses::GetProcessesDataFromFile()
{
CProcessInfo * lpProcessInfo = NULL;
char szProcessNameToMatch[_MAX_FNAME+1];
// In case we're matching against these...
LPTSTR tszProcessNameToMatch = g_lpProgramOptions->GetProcessName();
DWORD dwProcessIDToMatch = g_lpProgramOptions->GetProcessID();
// If we're going to be matching on a process name, go ahead and grab it
// once now, convert to ANSI if necessary (since it comes from the CSV
// file in ANSI), and upper case it...
if ( ( g_lpProgramOptions->GetMode(CProgramOptions::InputProcessesWithMatchingNameOrPID) ) &&
( tszProcessNameToMatch )
)
{
// Let's save away the Process Name...
CUtilityFunctions::CopyTSTRStringToAnsi(tszProcessNameToMatch, szProcessNameToMatch, _MAX_FNAME+1);
// Upper case the process name... we should be ready to match on this now...
_strupr(szProcessNameToMatch);
}
// 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;
}
if ( g_lpProgramOptions->GetMode(CProgramOptions::InputProcessesWithMatchingNameOrPID) )
{
// Okay, the user has provided us something to match against our data...
if ( tszProcessNameToMatch )
{
// Process name provided... does it match?
if ( strcmp(szProcessNameToMatch, szProcessName) )
{
// Nope... well then, we should nuke this line...
m_lpInputFile->ReadFileLine();
// Then, jump to the next line processing...
goto ReadNewLine;
}
#ifdef _DEBUG
else
{
printf("DEBUG: MATCH FOUND ON Process Name [%s]\n", szProcessName);
}
#endif
} else
{
if ( dwProcessIDToMatch != iProcessID )
{
// Nope... well then, we should nuke this line...
m_lpInputFile->ReadFileLine();
// Then, jump to the next line processing...
goto ReadNewLine;
}
#ifdef _DEBUG
else
{
_tprintf(TEXT("DEBUG: MATCH FOUND ON Process ID [%d]\n"), iProcessID);
}
#endif
}
}
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
// Each Process gets its own
lpProcessInfo = new CProcessInfo();
if (lpProcessInfo == NULL)
{
fReturn = false;
break;
}
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, m_lpInputFile, m_lpOutputFile, NULL))
{ // Failure initializing the ProcessInfo object?!?
delete lpProcessInfo;
lpProcessInfo = NULL;
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...
lpProcessInfo->SetProcessName(tszProcessName);
// Enumerate the modules for the process
if (!lpProcessInfo->EnumerateModules(iProcessID, this, tszProcessName))
{
fReturn = false;
break;
}
// Success... add this to the Processes Object...
if (!AddNewProcessInfoObject(lpProcessInfo))
{ // Failure adding the node...
delete lpProcessInfo;
lpProcessInfo = NULL;
return false;
}
ReadNewLine:
// 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;
}
CProcesses::ProcessCollectionMethod CProcesses::GetProcessCollectionMethod()
{
return m_enumProcessCollectionMethod;
}
bool CProcesses::GetProcessesDataForRunningProcessesUsingTOOLHELP32()
{
CProcessInfo * lpProcessInfo = NULL;
HANDLE hSnapShot = NULL;
bool fReturn = false;
if (!m_fInitialized)
return false;
// It's possible the user provided a PID directly... if so,
// we can circumvent the whole search of PIDs on the system...
if (g_lpProgramOptions->GetProcessID())
{
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
lpProcessInfo = new CProcessInfo();
if (lpProcessInfo == NULL)
goto error_cleanup;
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, NULL, m_lpOutputFile, NULL))
{
goto error_cleanup;
}
if (lpProcessInfo->EnumerateModules(g_lpProgramOptions->GetProcessID(), this, NULL))
{
// Success... add this to the Processes Object...
if (!AddNewProcessInfoObject(lpProcessInfo))
{ // Failure adding the node...
goto error_cleanup; // For now, let's just error on out...
}
} else
{
// Failure enumerating modules on the only PID of interest... very bad...
goto error_cleanup;
}
}
else
{
PROCESSENTRY32 procentry;
BOOL bFlag;
// Get a handle to a Toolhelp snapshot of the systems processes.
hSnapShot = g_lpDelayLoad->CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if( hSnapShot == INVALID_HANDLE_VALUE )
{
goto error_cleanup ;
}
// Clear this structure
memset(&procentry, 0, sizeof(procentry));
// Get the first process' information.
procentry.dwSize = sizeof(PROCESSENTRY32) ;
bFlag = g_lpDelayLoad->Process32First( hSnapShot, &procentry ) ;
// While there are processes, keep looping.
while( bFlag )
{
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
// Each Process gets its own
lpProcessInfo = new CProcessInfo();
if (lpProcessInfo == NULL)
goto error_cleanup;
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, NULL, m_lpOutputFile, NULL))
{
// Failure initializing the ProcessInfo object?!?
delete lpProcessInfo;
lpProcessInfo = NULL;
// Clear this structure
memset(&procentry, 0, sizeof(procentry));
// Get the next Process...
procentry.dwSize = sizeof(PROCESSENTRY32) ;
bFlag = g_lpDelayLoad->Process32Next( hSnapShot, &procentry );
continue;
}
// Enumerate the modules for this process...
if (lpProcessInfo->EnumerateModules(procentry.th32ProcessID, this, procentry.szExeFile))
{
// Success... add this to the Processes Object...
if (!AddNewProcessInfoObject(lpProcessInfo))
{
// Failure adding the node...
delete lpProcessInfo;
lpProcessInfo = NULL;
}
} else
{
// An error enumerating modules might be normal...
delete lpProcessInfo;
lpProcessInfo = NULL;
}
// Get the next Process...
procentry.dwSize = sizeof(PROCESSENTRY32) ;
bFlag = g_lpDelayLoad->Process32Next( hSnapShot, &procentry );
}
}
fReturn = true;
goto cleanup;
error_cleanup:
if (lpProcessInfo)
delete lpProcessInfo;
cleanup:
if (hSnapShot != INVALID_HANDLE_VALUE)
CloseHandle(hSnapShot);
return fReturn;
}