Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Process viewer

4.94/5 (104 votes)
10 Mar 2008CPOL9 min read 39   22.1K  
Lists out the details of running processes in a system, loaded drivers, loaded dlls, version of each dll and process, process times, command line, owner, priority, GDI resource usage, privileges, loaded symbols, window heirarchy, autostart app finding and more.

Download ProcViewer.zip - 195.9 KB
Download Exe ProcessViewer.zip - 118.1 KB

Screenshot - ProcessViewer.jpg

Options Dialog

Screenshot - Options.jpg

Privilege Manager Dialog

PrivilegeMgr.JPG

Introduction

A utility application that lists out all the processes that are running in a system along with dll's loaded by them and also displays path of dll's loaded, their load addresses, dll base, image size and per process information.

Features of this tool

  1. Display's all loaded dll's of a process
    • Symbols in each dll, you click on any of them
      • Index
      • Name
      • Address
      • Size
    • Version of each dll
    • Description of dll
    • Company name
    • View each dll with dependency walker if dependency viewing is enabled
    • Full module path, i.e. from where is it loaded?
    • Load address
    • Entry point
    • Image size
    • Load order
  2. Display's all drivers loaded in the system if you click on main kernel process
  3. Process id's
  4. Process owner
  5. Process command line
  6. Process auto startup information
  7. Type of application, i.e. whether application is a console, windows or ms-dos app.
  8. Process priority
  9. GUI resource usage statistics ( Will be expanded in future version )
  10. Size of each process
  11. Symbols in each process
  12. Window details if any
    • Displays windows in it's proper hierarchy i.e. all child windows of a parent window come under it's node.
    • Text or caption
    • HWND
    • Window proc
    • Icon if any
    • Cursor
    • Class of window, i.e Toolbarwindow32 for a toolbar.
    • Thread that owns the window
    • State of window, i.e Maximized, minimized, restored, or invisible
    • Bounds of window
    • ID of window
    • Parent window HWND
    • Class style of window
    • Window styles
    • All child windows for a window displayed hierarchically
  13. Process times
    • Creation time
    • Exit time, i.e. kill a process and then you this time get's updated
    • Kernel mode time, how much of kernel time is used
    • User mode time
  14. File times
    • Creation time
    • Last accessed on
    • Last written on
  15. IO Counters
    • Input and output counter values
  16. Memory details, page fault count etc
  17. Version of process, you get some information also as a tooltip
  18. Privileges of a process along with a privilege manager
  19. Sorting of view
  20. Switching of layout
  21. Kill a process and kill all instances of a process
  22. Search for a process or for a dll/driver loaded
  23. Options dialog for switching off unwanted details
  24. You have an about box. ;)
  25. And an exit button too. ;))

Using the code

I used PSAPI functions provided by microsoft to enumerate the modules loaded by a process.

The API's used are

  1. EnumProcesses -- To get all processes running in the system
  2. EnumProcessModules -- To get all modules/dlls loaded by a process
  3. GetModuleBaseName -- To get the base name of module... for eg: mydll.dll instead of C:\AnyLongPath\mydll.dll.
  4. GetModuleFileNameEx -- To get the long path of a module
  5. GetModuleInformation -- Retrieves information pertaining to a module
  6. GetProcessMemoryInfo -- To get memory details of a process.
  7. EnumDeviceDrivers -- Process with id 4(System) is the process which loads up the drivers, so we use this function to enumerate through these device drivers
  8. GetDeviceDriverBaseName -- To get base name of a device driver
  9. GetDeviceDriverFileName -- To get full path of a device driver
  10. GetProcessTimes -- To get the amount of time each process has taken, when did it start, how much kernel mode, and user mode time has it taken.
  11. SearchPath -- Searches PATH environment variable. Docs give a detailed description of the searching process that takes place.
  12. GetFileVersionInfo -- For extracting version.
  13. GetFileVersionInfoSize -- Size of version
  14. VerQueryValue -- Query for version components. There is a class called FileVersionInfo written for this purpose. Lookup that class for more information on these version functions and how to use them.

This is the main function...

void GetProcessDetails()
{
   // Clear previous associated item data if any 


   ClearItemData();
   // Clear tree view 


   m_ctvProcess.DeleteAllItems(); 
   // Clear list view 


   m_clvProcessDetails.DeleteAllItems(); 
 
   #define MAX 10000
   DWORD dwSize = 0;
   DWORD dwProcIdentifiers[MAX] = { 0 };
   EnumProcesses( dwProcIdentifiers, sizeof( dwProcIdentifiers ), &dwSize );
  
   if( !dwSize )
   {
       return;
   }
 
   // Process count 


   dwSize /= sizeof( DWORD ); 
   HTREEITEM hItemRoot = m_ctvProcess.InsertItem( _T( "Process list" ), 60, 60 );
   TCHAR szName[MAX_PATH] = { 0 };
  
   for( DWORD dwIndex = 0; dwIndex < dwSize; ++ dwIndex )
   {
       HANDLE hProcModule = 
            OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 
                        FALSE, 
                        dwProcIdentifiers[dwIndex] );
       if( !hProcModule )
       {
           continue;
       }

       DWORD dwModuleSize = 0;
       HMODULE hModules[MAX] = { 0 };

       // Get all modules for a process 


       EnumProcessModules( hProcModule, 
                           hModules, 
                           sizeof( hModules ), 
                           &dwModuleSize );
       dwModuleSize /= sizeof( HMODULE );
       if( !dwModuleSize )
       {
          continue;
       }

       // Structure for storing process information 


       PPROC_INFO_t pstProcInfo = new PROC_INFO_t;
       if( !pstProcInfo )
       {
           return;
       }
 
       // Fill out process information 


       pstProcInfo->dwProcId = dwProcIdentifiers[dwIndex];
       GetModuleFileNameEx( hProcModule, hModules[0], szName, MAX_PATH ); 
       pstProcInfo->csFullPath = szName;
       GetModuleBaseName( hProcModule, hModules[0], szName, MAX_PATH );
       pstProcInfo->csBaseName = szName;

       // Get full path display status, if checked then we will display full path 


       const BOOL bFullPathChecked = 
       m_ToolBar.GetToolBarCtrl().IsButtonChecked( ID_OPTIONS_SHOWPATH ); 

       CString csProc;
       csProc.Format( _T( "%s, PID: %lu" ), 
              SCAST( LPCTSTR , 
               ( bFullPathChecked ? 
                 pstProcInfo->csFullPath : pstProcInfo->csBaseName )), 
              dwProcIdentifiers[dwIndex] );

       HTREEITEM hItem = m_ctvProcess.InsertItem( csProc, 20, 20, hItemRoot );
       m_ctvProcess.SetItemData( hItem, RCAST( DWORD, pstProcInfo ));

       // Process details 


       PROCESS_MEMORY_COUNTERS& pmcProcMemCounter = pstProcInfo->stpmcMemCounters;
       GetProcessMemoryInfo( hProcModule, 
                 &pmcProcMemCounter, 
                 sizeof( pmcProcMemCounter ));

       // Insert process memory related values 


       HTREEITEM hPInfoItem = m_ctvProcess.InsertItem( _T( "Details" ), 
                               7, 7, hItem );
       CString csTempStr;
       const int nIconIndex = 63;

       // Page fault count 


       csTempStr.Format( _T( "Page fault count: %lu" ), 
             pmcProcMemCounter.PageFaultCount );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );

       // Page file usage 


       csTempStr.Format( _T( "Page file usage: %.02lf KB" ), 
             pmcProcMemCounter.PagefileUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
       
       // Peak page file usage 


       csTempStr.Format( _T( "Peak page file usage: %.02lf KB" ), 
             pmcProcMemCounter.PeakPagefileUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );

       // Peak working set size 


       csTempStr.Format( _T( "Peak working set size: %.02lf KB" ), 
             pmcProcMemCounter.PeakWorkingSetSize/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );

       // Quota non pages pool usage 


       csTempStr.Format( _T( "Quota non paged pool size: %.02lf KB" ), 
             pmcProcMemCounter.QuotaNonPagedPoolUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );

       // Quota pages pool usage 


       csTempStr.Format( _T( "Quota paged pool size: %.02lf KB" ), 
             pmcProcMemCounter.QuotaPagedPoolUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
 
       // Quota peak non pages pool usage 


       csTempStr.Format( _T( "Quota peak non paged pool size: %.02lf KB" ), 
             pmcProcMemCounter.QuotaPeakNonPagedPoolUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
 
       // Quota peak pages pool usage 


       csTempStr.Format( _T( "Quota peak paged pool size: %.02lf KB" ), 
             pmcProcMemCounter.QuotaPeakPagedPoolUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
 
       // Working set size 


       csTempStr.Format( _T( "Working set size: %.02lf KB" ), 
             pmcProcMemCounter.WorkingSetSize/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
 
       // Prepare module list 


       PMODULEINFO_LIST_t* ppModuleInfo = &pstProcInfo->pstModuleInfoList;
       for( DWORD dwModIndex = 1; dwModIndex < dwModuleSize; ++ dwModIndex )
       {
            // Allocate new module 


            *ppModuleInfo = new MODULEINFO_LIST_t;;
            if( !*ppModuleInfo )
            {
                continue;
            }

            // Get module related information 


            GetModuleInformation( hProcModule, 
                  hModules[dwModIndex], 
                  &( *ppModuleInfo )->stmiModInfo, 
                  sizeof( MODULEINFO ));
 
            // Get full path of module 


            GetModuleFileNameEx( hProcModule, 
                 hModules[dwModIndex], 
                 szName, 
                 MAX_PATH );

            ( *ppModuleInfo )->csModuleFullPath = szName;

            // Get base name of module 


            GetModuleBaseName( hProcModule, 
                   hModules[dwModIndex], 
                               szName, 
                               MAX_PATH );
        ( *ppModuleInfo )->csModuleBaseName = szName;
 
            // Move forward 


            ppModuleInfo = &( *ppModuleInfo )->pstNextModuleInfo;

       }// End for 



       // Close process handle 


       CloseHandle( hProcModule );
    }// End for 



    // Expand root node


    m_ctvProcess.Expand( hItemRoot, TVE_EXPAND );
}


PSAPI Function usage

EnumProcesses:

C++
// A large array of DWORDs for storing process ids of processes


DWORD dwProcIds[2048];
DWORD dwProcRunning = 0;
 
// Get all running processes


EnumProcesses( dwProcIds, sizeof( dwProcIds ) /sizeof( dwProcIds[0] ), 
               &dwNeeded );

// To get the count of processes running use


dwProcRunning /= sizeof( DWORD );

EnumProcessModules:

C++
// Pass in a large array of modules just to be safe


HMODULE hModuleArray[2048];
DWORD dwModuleCount = 0;
 
// Open process to get it's handle, for some processes 


// we may be denied access.


HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 
                               FALSE, 
                               dwAnyProcessId );
 
// Get processes 


EnumProcessModules( hProcess, hModuleArray, 
                    sizeof( hModuleArray )/ sizeof( hModuleArray[0] ), 
                    &dwModuleCount );
 
// Now get actual count of modules loaded


dwModuleCount /= sizeof( HMODULE );

On return the first module in the array is the executable of the process. Rest of the modules will be actual modules loaded.

GetModuleBaseName:

Returns the base name of a module for eg: if module path is C:\MyPath\Averylongpath\Basename.dll, then this API will return Basename.dll

C++
TCHAR szName[MAX_PATH] = { 0 }
GetModuleBaseName( hProcess, hModule, szName, MAX_PATH )

GetModuleFileNameEx:

Very similar to the above function except that it returns full path of the requested module.

GetModuleInformation and GetProcessMemoryInfo:

These two API's are simple to use. Just takes a structure and returns information in them.

C++
// Get module related information


MODULEINFO miModInfo = { 0 };
GetModuleInformation( hProcModule, hModule, &miModInfo, sizeof( MODULEINFO ));

// Get process related information


PROCESS_MEMORY_COUNTERS pmcProcMemCounter = { 0 };
GetProcessMemoryInfo( hProcModule, 
                      &pmcProcMemCounter, 
                      sizeof( pmcProcMemCounter ));

These are the API's that I have used. Main code can be found in ProcessViewerDlg.cpp and ProcessViewerDlg.h

EnumDeviceDrivers

Enumerates device drivers loaded by System process. Even though name of the process is shown as System, it's real name is %systemroot%\System32\ntoskrnl.exe.

C++
const int nSize = 2048;
DWORD dwCount = 0;
LPVOID lpvImageBases[nSize] = { 0 };
 
// Get drivers


EnumDeviceDrivers( lpvImageBases, nSize, &dwCount );
 

// Get count of drivers loaded


dwCount /= sizeof( LPVOID );

Note that first item in the array is name of the process, rest will be drivers.

GetDeviceDriverBaseName

Extracts base name of a device driver.

C++
TCHAR szName[MAX_PATH] = { 0 };
 
// Returns main process name, since we are accessing the first element of the 


// array


GetDeviceDriverBaseName( lpvImageBases[0], szName, MAX_PATH );

GetDeviceDriverFileName

Extracts full path of device driver. This didn't work well for me though. Most of the path returned was of the form \Windows\System32\Some.sys etc or \??\C:\..\Some.sys. So I had to use SearchPath API for finding out where exactly these device drivers are located, of course I modified the PATH variable to force this API to look into Drivers folder too.

C++
TCHAR szName[MAX_PATH] = { 0 };
 
// Returns full path of device driver loader process, since we are accessing 


// first element of the array.


GetDeviceDriverFileName( lpvImageBases[0], szName, MAX_PATH );

GetProcessTimes

Returns timing information for a specified process.

C++
HANDLE hProcess; // Handle returned by OpenProcess


 
// Prepare filetime structures



FILETIME ftStartTime = { 0 }, 
         ftExitTime = { 0 }, 
         ftKernelTime = { 0 }, 
         ftUserTime = { 0 }
 
// Get time


GetProcessTimes( hProcess, 
                 &ftStartTime, 
                 &ftExitTime, 
                 &ftKernelTime, 
                 &ftUserTime );

You can directly use FILETIME, but should be careful, except for start time and exit time, others won't work as expected (read the docs). You need to do some addtional work on Kernel time and user time.

Best way is to either call FileTimeToSystemTime or put it into a COleDateTime. DavidCrow has written a nice article on this topic. I recommend you read it.

SearchPath

Read the docs carefully for this function. It's a waste of time to describe the working of this API here, since it's well documented.

C++
const int nAllocLength = MAX_PATH * 2;
TCHAR szPathBuffer[ nAllocLength ];
 

// Search for file 


if( SearchPath( 0, _T( "Anyfile.anytype" ), 0<;/span>, nAllocLength, szPathBuffer, 0 ))
{
     csFilePath_o = szPathBuffer;
     return true;
}// End if

ProcessPriority

To change priority of a process call SetPriorityClass, to retrieve priority of a process call GetPriorityClass.

The six classes of priority are as follows...

  1. IDLE_PRIORITY_CLASS
  2. BELOW_NORMAL_PRIORITY_CLASS
  3. NORMAL_PRIORITY_CLASS
  4. ABOVE_NORMAL_PRIORITY_CLASS
  5. HIGH_PRIORITY_CLASS
  6. REALTIME_PRIORITY_CLASS
Usage:

// For setting priority use


VERIFY( SetPriorityClass( hProcess, ABOVE_NORMAL_PRIORITY_CLASS ));

// For retrieving priority use


VERIFY( GetPriorityClass( hProcess ) != 0 );

Search

Process viewer supports searching of loaded modules. You can search for a particular process or a dll. Search does not support wildcard searching, just does an "StrStrI" search. So if you search for "shell", hits will be "shell32.dll" "someshell.dll" "myShelldll.dll etc.

Press Ctrl + F to search.

Dependency viewing

Double click on any module or process to view it's dependency. You should have dependency walker installed on your system. This will happen only if dependency viewing is enabled. One of the toolbar buttons(fourth one) is for this purpose.

You can download dependency walker from here.

Full path

To view full path of a loaded process just enable full path by clicking on green tick button(third one) on toolbar.

Refresh

To refresh either click first button on toolbar or press F5.

Swap view

To switch from vertical view to horizontal view or vice versa press F6 or second button on toolbar.

Kill Process

Select a particular process to kill. Press F8 or click on the kill button. When a process is killed it's not removed from the process listing instead the process name is set to bold. Press F5/Refresh for removing killed process from tree. Mainly done for retaining snapshot for that process. Helps if there is some trojan running and you want to see a detailed information for that trojan without having the trojan running.

NOTE: Do not kill the kernel process, I tried and the whole system came down. :)

Options

Press F7 for displaying options dialog. You can set various options for process viewing. Options will include much more.

CSV Module listing and symbol listing

To save list of modules for a process press Ctrl + A to select all modules and then Ctrl + C. Things to remember...

  1. If cursor is on an item then that subitem(s) will be copied to clipboard. So to copy more than one sub item, first select required sub items then put your cursor on that sub item column to copy to clipboard.
  2. To copy entire contents take cursor out of list view.
  3. Open notepad and paste.
  4. Save as anyfilename.csv.
  5. Open with a CSV viewer like Excel.

The same applies to symbol listing.

View aspect saving

Saves your settings to an ini file. For eg: column order, divider pane size, main window size. This enables you to change order of list view to your liking. Even sizes of columns are saved.

Utils

There is a FileVersionInfo class and DividerWnd class which can reused. A WindowCollection class for enumerating all open windows.

FileVersionInfo extracts version information from a module. DividerWnd is a simple lightweight splitter.

Acknowledgements

  1. The bitmap that you see in this application's treeview and listview, has been taken from Dominik's Keepass app.
  2. Hey Ralph thanks for those comments. I have implemented most of them, but a rewrite is pending (Reusability). Thanks once again.
  3. Thanks to DavidCrow for his article on Process times.
  4. Thanks to Todd C Wilson for his article on Aggressive optimizations and I am using his header file(I've commented out linker commands that merge sections).
  5. Thanks to CodeProject. Thanks to all those developers who helped me by posting those wonderful articles. If it was not for them then this wouldn't have happened. :)

History

  1. Created on 05/08/2007.
  2. Added "Search" on 05/14/2007.
  3. Added description of application on 05/16/2007.
  4. Updated application to show loaded device drivers, version information, size, GUI change.
  5. Added progress bar, display of icons related to a process, type of process(Console, Windows, MS-DOS) on 05/28/07.
  6. Added support for sorting, column re-ordering and saving this order on 05/31/2007.
  7. Added extraction of HWND's from running processes. Changed toolbar format. On 06/02/2007.
  8. Added HWND style listing, parent listing. On 06/05/2007.
  9. Made toolbar transparent, enabled window style, id viewing. On 06/07/2007.
  10. Disabled window style viewing due to some issues, fixing going on. On 06/08/2007.
  11. Added options dialog and various different tweaks. On 06/09/2007.
  12. Added kill process option, fixed some issues for saving settings. On 06/10/2007.
  13. Added process priority, and IO counters. On 06/13/2007.
  14. Now correctly displays kids of windows. On 06/18/2007.
  15. Added symbol listing for each loaded module of a process. On 06/25/2007.
  16. Added process privilege display. On 06/26/2007.
  17. Added process owner, process command line, and total window count display. On 10/14/2007.
  18. Added dynamic process priority updation support. On 10/24/07
  19. Added window icon, charset, enabled/disabled status to window details and added support for process priority boost. On 10/26/07
  20. Changed root node text to machine name and user name ;). On 11/2/2007
  21. Finds out whether a process is registered for auto startup. On 11/12/2007
  22. Re-Enabled window style viewing after some bug fixes. On 11/27/2007
  23. Fixed compilation errors in VS2005 (Note: You will get a manifest error in VS2005, so as a fix delete the manifest resource in the resource editor).
  24. Kill all instances of an application support added. Also added "Open parent folder", "Properties" support for processes.
  25. Added privilege manager which helps in dynamically enabling/disabling/removing privileges(Fine tuning needed though). On 1/Jan/2008(Happy new year!)
  26. Optimized a bit for speed!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)