Process Spectator: View All Running Processes with Modules and Thread Details

Environment: VC7, XP (will run, but not tested on other OS)
Tools Used: MFC 7.0, .NET Framework 1.0

Introduction

One day, I was working in VB.NET to see what’s new in it. One control, named ‘Process,’ interested me. So, I dove deeper into it and wrote out code that lists all processes (names only) running.

The next step is to hard code it in VC.NET. Coding it is almost the same as the .NET Framework provides. (Please note here that I am discussing ‘Managed Extensions for C++’ and .NET because C++ is built on the top of it.) Going on this way, I tried to develop a process viewer like PVIEW and Task Manager.

Most of you know what processes, threads, handles, and modules are, so I do not discuss them in detail. But, for the article’s completeness, I’ll brief you through them in my own way:

  • Process: Any running task in the system, either visible to the user (for example, Notepad) or not (as in, crss.exe). Process is the execution mode of any program.
  • Thread: In a Windows system, any smallest executable module running is a thread. A process has one or more threads. The process is the primary thread of the given process.
  • Handle: Handle/Pointer to any resource in the system—windows, files, threads, and the like.
  • Module: Executables (DLL, EXE, and so forth) that are required to load (by OS) to run the desired process. A process is said to be dependent on modules.

.NET Classes for It

Before discussing anything further, I do believe that you have some basic knowledge of MC++, such as ‘__gc’ classes, garbage collection, ‘__value’ type, and so on. If you don’t have this knowledge, don’t worry and proceed. You will know how to get process information. Look at the MSDN for the MC++ language.

Here I discuss the .NET Framework namespaces and classes that I used and required for acquiring details of processes, threads, and modules. Some exception handling .NET classes are also discussed.

The System.Diagonistics namespace

The System namespace is the same as the CObject MFC class—root in the hierarchy (except that the former is the namespace and the latter is the class). The System namespace contains fundamental classes and base classes. (Please look at the MSDN for more info.)

The System.Diagonistics namespace provides classes that allow interaction with system processes, event logs, performance counters, and so on. Our interest is with the classes that provide information related to processes and here they are:

  • Process Class: Provides access to local and remote processes and to start and stop local system processes. The functions used in the application are given below with a brief description:

    Process::GetProcesses(): The most significant method for the application, which returns an array of ‘Process’ objects (every ‘__gc’ array is derived from the System.Array class—it is a basic datatype array). Let’s have a code excerpt before we proceed:

  • // Get all processes in 'p' array
    Diagnostics::Process *p[]=Diagnostics::Process::GetProcesses();
        // Will return array of Process objects
    
    // Run through all processes
    for(int n=p->GetLength(0),i=0; i < n; i++)
        // Argument 0 is dimension desired
    {
      Console::Write(p[i]->ProcessName);  Console::Write("\t");
        // Name of the process (no extension)
      Console::Write(p[i]->Threads->Count);
        // Number of threads
      Console::Write("\t");  Console::Write(p[i]->HandleCount);
        // Number of handles
    
      /*System::TimeSpan ts=p[i]->TotalProcessorTime;
      sprintf(s,"%02d:%02d:%02d",ts.Hours,ts.Minutes,ts.Seconds);*/
    
      Console::WriteLine(p[i]->MainWindowTitle);
        // Title of main window - null if no UI
    }
    // Will someone delete 'p'??
    
  • ProcessThread and ProcessThreadCollection classes

    ‘ProcessThread’ represents a single thread view and ‘ProcessThreadCollection’ presents a collection of threads (this value is returned by Process::Threads). The ‘Item’ property of ProcessThreadCollection represents an array of ProcessThread objects and can be accessed like an ordinary array. Have some code:

    // Assume 'g' is a Process object for which thread info desired
    ProcessThreadCollection *tc = g->Threads;
        // Get thread collection of process
    ProcessThread *pt;    // A single thread object
    
    for(i=0;i<tc->Count;i++)
    {
      pt=tc->Item[i];      // Hold up current thread item from
                           // collection
    
      Console::Write(pt->Id);              // Thread ID
      Console::Write(pt->BasePriority);    // Base priority
                                           // (Calculated by OS)
    
      /*System::TimeSpan ts=pt->TotalProcessorTime;
      str.Format("%02d:%02d:%02d",ts.Hours,ts.Minutes,
                                  ts.Seconds);*/
    
      Console::WriteLine(pt->StartAddress);    // Start/Base
                                               // address of thread
    }
    
  • ProcessModule, ProcessModuleCollection, and FileVersionInfo classes

    As with the threads view, ‘ProcessModule’ and ‘ProcessModuleCollection’ represent a module view of a process that can be accessed via Process::Modules. ‘FileVersionInfo’ represents the complete information of a module and is accessed via ProcessModule::FileVersionInfo. Sample code:

    ProcessModuleCollection *mc= g->Modules;;    // Collection of
                                                 // modules
    ProcessModule *pm;
    FileVersionInfo *fv;
    String *s;    // For filename
    
    for(i=0 ;i<mc->Count; i++)
    {
      // Assign this process module to pm
      pm=mc->Item[i];
    
      // Following code stips out path from filename
      s=pm->FileName;
      n=s->LastIndexOf(L'\\') + 1;
      if(n)
        s=s->Substring(n);
      Console::Write(s);
    
      fv=pm->FileVersionInfo;
      /*str.Format("%d.%d.%d.%d",fv->FileMajorPart,
                                 fv->FileMinorPart,
                                 fv->FileBuildPart,
                                 fv->FilePrivatePart);*/
    
      Console::Write(pm->BaseAddress);    // Base address of
                                          // this module
      Console::WriteLine(pm->ModuleMemorySize);
      /*str.Format("%.2fK",pm->ModuleMemorySize/1024.0);*/
    }
    

Exceptions while accessing information

Some definable and undefinable exceptions arise when we access information regarding a process and also while accessing the process object. The code must handle these exceptions. Unlike MFC exceptions, we place .NET exceptions in their original C++ form using a try/catch statement because there is no need to delete the exception objects. Let me discuss known cases that I have coded:

  • An exception raises when the user tries to Kill/Close a process that has already been exited and the view is not refreshed. It is an ArgumentException (for an incorrect argument to a method). All exception classes are in the System namespace. See the following code:

    try {
      Diagnostics::Process *g=Diagnostics::Process
                 ::GetProcessById(pid));  // 'pid' is process ID -
                                          // see source code.
      // Close or kill routine on success
    }
    catch(ArgumentException *) {
      AfxMessageBox("That process has already been terminated.");
    }
    
  • An undefinable (from me) exception is raised when we try to access the Modules property (properties are a special language enchancement of MC++, such as VB) under the ‘Process’ object. It is undefinable because when and why it raises is not known and also because MSDN states ‘SystemException’ and exception raises is ‘Win32Exception’. For the same reason, I was forced to use catch(…):

    // Set string value that will be used further
    try{
      sprintf(s,"%d",p[i]->Modules->Count);    // On accessing
                                               // Modules and not
                                               // Count
    }
    catch(...){
      sprintf(s,"**");
    }
    pProcess-<SetItemText(i,3,s);
    
  • In the same manner as above, an undefined type of exception raises on accessing the ‘FileVersionInfo’ property under ‘ProcessModule’. One interesting case with this is that it always occurs when accessing the first module (that is, the program itself). Exception handling code is the same as above.

Other Facts

  1. Most of the code is in Developer Studio and no information is provided for MFC.
  2. Other exceptions than those discussed may also be raised. Please notify me if so.
  3. * means title not present/available; ** means ‘Access Denied’.
  4. A function named FromMCtoUCStr (from managed code to unmanaged code) is coded to convert a MC++ String to LPCSTR (only ANSI string). It is required because no Win32/MFC function accepts String as parameter, no conversion function/operator exists in the String class, and no ‘MultiByteToWideChar’ type of function could be found.
  5. Please see a potential problem just above ‘RefreshList’ or in the “PID Problem.txt” file method about process IDs.
  6. You may use and modify the code in any way. You may inform me if you like.
  7. I do not have knowledge whether this type of article has been posted or not.

Downloads


Download demo project – 41 Kb



Download source – 23 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read