void MainMenu()
{
TCHAR *MenuStrings[] = {
TEXT( "Get Tick Count"),
TEXT( "Process Viewer"),
NULL
};
int Done = FALSE;
while( ! Done )
{
switch( MenuHandler( MenuStrings ) )
{
case 0:
RETAILMSG(1,(TEXT("Tick Count %X\n"), GetTickCount() ));
break;
case 1:
ProcessViewerMenu();
break;
default:
Done = TRUE;
break;
}
}
}
In ProcessViewerMenu(), I will add three new menu items to the original "Show Running Processes" that I removed from the main menu. These are:
TCHAR *MenuStrings[] = {
TEXT( "Show Running Processes"),
TEXT( "Show Running Processes and Modules"),
TEXT( "Show Running Processes and Heaps"),
TEXT( "Show Running Processes and Threads"),
NULL
};
So I will add output for Modules (loaded DLLs), heaps and threads to the process viewer. For these to be of value, they should be listed with the process, so I will extend ShowRunningProcesses() to add the information with the process list as an option. To do that, I will add a parameter to the function which will be a set of flags. So the new function signature is:
void ShowRunningProcesses(DWORD Flags)
I will use the same flags that ToolHelpAPi uses for CreateToolhelp32Snapshot(), but I will only support the following; TH32CS_SNAPMODULE, TH32CS_SNAPHEAPLIST and TH32CS_SNAPTHREAD. They can be used individually, or combined by or'ing them together. But you will see that each one creates quite a bit of output, so combining them may not make sense. The new ShowRunningProcesses() is:
void ShowRunningProcesses(DWORD Flags)
{
PROCESSENTRY32 *CurrentProcess;
PROCESSENTRY32 Process[ MAX_PROCESSES ];
DWORD ProcessCount;
DWORD index ;
DWORD MaxProcessNameLength;
ProcessCount = GetRunningProcesses( Process );
MaxProcessNameLength = GetMaxProcessNameLength( Process, ProcessCount );
RETAILMSG( 1,(TEXT("%-*s %8s %13s %9s %9s %10s\n"),
MaxProcessNameLength, TEXT("Process"),
TEXT("PID"),
TEXT("Base Priority"),
TEXT("# Threads"),
TEXT("Base Addr"),
TEXT("Access Key")
)) ;
for( index = 0; index < ProcessCount; index++ )
{
CurrentProcess = &(Process[ index ] );
RETAILMSG(1, ( TEXT("%-*s %8X %13d %9d %9X %10X\n"),
MaxProcessNameLength, CurrentProcess->szExeFile,
CurrentProcess->th32ProcessID,
CurrentProcess->pcPriClassBase,
CurrentProcess->cntThreads,
CurrentProcess->th32MemoryBase,
CurrentProcess->th32AccessKey
));
if( Flags & TH32CS_SNAPMODULE )
OutputModules( CurrentProcess->th32ProcessID );
if( Flags & TH32CS_SNAPHEAPLIST )
OutputHeap( CurrentProcess->th32ProcessID );
if( Flags & TH32CS_SNAPTHREAD )
OutputThreads( CurrentProcess->th32ProcessID );
}
}
For each processes, if one of the flags is set call on the appropriate function to display the information.
The first flag is TH32CS_SNAPMODULE which causes OutputModules() to be called. OutputModules will use the Module32First() and Module32Next() to enumerate the loaded modules. This function looks a lot like GetRunningProcesses() that I wrote in a previous post. The difference is that outputs the modules as it finds them. There are more members of MODULEENTRY32 than the ones that I am outputting, but I didn't find much value in them.
static void OutputModules( DWORD ProcessID)
{
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessID);
MODULEENTRY32 CurrentModule;
if(hSnapShot == (HANDLE)-1)
{
RETAILMSG( 1,
(TEXT("OutputModules: Failed CreateToolhelp32Snapshot Error: %d\n"),
GetLastError()));
return;
}
memset(&CurrentModule,0,sizeof(MODULEENTRY32));
CurrentModule.dwSize = sizeof(MODULEENTRY32);
if(Module32First(hSnapShot, &CurrentModule))
{
// Section header
RETAILMSG(1, ( TEXT("\t%8s %8s %8s %8s %s\n"),
TEXT("Mod ID"),
TEXT("Base Add"),
TEXT("Size"),
TEXT("HANDLE"),
TEXT("Module Name")
));
while(TRUE)
{
RETAILMSG(1, ( TEXT("\t%8X %8X %8X %8X %s\n"),
CurrentModule.th32ModuleID,
CurrentModule.modBaseAddr,
CurrentModule.modBaseSize,
CurrentModule.hModule,
CurrentModule.szModule
)) ;
if(!Module32Next(hSnapShot, &CurrentModule))
{
break;
}
}
}
CloseToolhelp32Snapshot (hSnapShot);
}
The next flag is TH32CS_SNAPHEAPLIST, which is more involved to handle. The reason is that we must first enumerate the heaps using Heap32ListFirst() and Heap32ListNext(), then enumerate the allocations using Heap32First() and Heap32Next(). This creates a lot of output and in its raw form may not be much value. I will leave it to you to process the data to analyze it. OutputHeap() is:
static void OutputHeap( DWORD ProcessID)
{
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, ProcessID);
HEAPLIST32 CurrentHeap;
if(hSnapShot == (HANDLE)-1)
{
RETAILMSG( 1,
(TEXT("OutputThreads: Failed CreateToolhelp32Snapshot Error: %d\n"),
GetLastError()));
return;
}
memset(&CurrentHeap,0,sizeof(HEAPLIST32));
CurrentHeap.dwSize = sizeof(HEAPLIST32);
if(Heap32ListFirst(hSnapShot, &CurrentHeap))
{
RETAILMSG(1, ( TEXT("\t%8s\n"),
TEXT("Heap ID")
));
while(TRUE)
{
HEAPENTRY32 CurrentHeapEntry;
memset(&CurrentHeapEntry,0,sizeof(HEAPENTRY32));
CurrentHeapEntry.dwSize = sizeof(CurrentHeapEntry);
RETAILMSG(1, ( TEXT("\t%8X\n"),
CurrentHeap.th32HeapID
)) ;
if(Heap32First( hSnapShot, &CurrentHeapEntry, ProcessID, CurrentHeap.th32HeapID ) )
{
// Section header
RETAILMSG(1, ( TEXT("\t\t%8s %8s %8s %8s %8s %8s\n"),
TEXT("Handle"),
TEXT("Address"),
TEXT("Size"),
TEXT("Flags"),
TEXT("Lock Cnt") ,
TEXT("ProcessID")
)) ;
while( TRUE )
{
RETAILMSG(1, ( TEXT("\t\t%8X %8X %8X %8X %8X %8X\n"),
CurrentHeapEntry.hHandle,
CurrentHeapEntry.dwAddress,
CurrentHeapEntry.dwBlockSize,
CurrentHeapEntry.dwFlags,
CurrentHeapEntry.dwLockCount ,
CurrentHeapEntry.th32ProcessID
)) ;
if(!Heap32Next( hSnapShot, &CurrentHeapEntry))
{
break;
}
}
}
if(!Heap32ListNext(hSnapShot, &CurrentHeap))
{
break;
}
}
}
CloseToolhelp32Snapshot (hSnapShot);
}
The last flag is TH32CS_SNAPTHREAD, which is similar to handling processes and modules, except that the snapshot includes threads for all processes, so we need to filter them.
static void OutputThreads( DWORD ProcessID)
{
// Passing in the ProcessID, but it is ignored by CreateToolhelp32Snapshot()
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, ProcessID);
THREADENTRY32 CurrentThread;
if(hSnapShot == (HANDLE)-1)
{
RETAILMSG( 1,
(TEXT("OutputThreads: Failed CreateToolhelp32Snapshot Error: %d\n"),
GetLastError()));
return;
}
memset(&CurrentThread,0,sizeof(THREADENTRY32));
CurrentThread.dwSize = sizeof(THREADENTRY32);
if(Thread32First(hSnapShot, &CurrentThread))
{
// Section header on two lines
RETAILMSG(1, ( TEXT("\t%8s %8s %8s %8s %8s %8s %8s %8s\n"),
TEXT("Usage"),
TEXT("Thread"),
TEXT("Owner"),
TEXT("Base"),
TEXT("Delta"),
TEXT("Flags"),
TEXT("Access"),
TEXT("Current")
));
RETAILMSG(1, ( TEXT("\t%8s %8s %8s %8s %8s %8s %8s %s\n"),
TEXT("Count"),
TEXT("ID"),
TEXT(" "),
TEXT("Priority"),
TEXT("Priority"),
TEXT(" "),
TEXT("Key"),
TEXT("Thread ID")
));
while(TRUE)
{
// Filter the threads for just this process
if( CurrentThread.th32OwnerProcessID == ProcessID )
{
RETAILMSG(1, ( TEXT("\t%8X %8X %8X %8d %8d %8X %8X %8X\n"),
CurrentThread.cntUsage,
CurrentThread.th32ThreadID,
CurrentThread.th32OwnerProcessID,
CurrentThread.tpBasePri,
CurrentThread.tpDeltaPri,
CurrentThread.dwFlags,
CurrentThread.th32AccessKey,
CurrentThread.th32CurrentProcessID
)) ;
}
if(!Thread32Next(hSnapShot, &CurrentThread))
{
break;
}
}
}
CloseToolhelp32Snapshot (hSnapShot);
}
Finally, the body of ProcessViewerMenu() is:
void ProcessViewerMenu()
{
TCHAR *MenuStrings[] = {
TEXT( "Show Running Processes"),
TEXT( "Show Running Processes and Modules"),
TEXT( "Show Running Processes and Heaps"),
TEXT( "Show Running Processes and Threads"),
NULL
};
int Done = FALSE;
while( ! Done )
{
switch( MenuHandler( MenuStrings ) )
{
case 0:
ShowRunningProcesses(0);
break;
case 1:
ShowRunningProcesses(TH32CS_SNAPMODULE);
break;
case 2:
ShowRunningProcesses(TH32CS_SNAPHEAPLIST);
break;
case 3:
ShowRunningProcesses(TH32CS_SNAPTHREAD);
break;
default:
Done = TRUE;
break;
}
}
}
Copyright © 2008 – Bruce Eitman
All Rights Reserved