FREE BOOK

Chapter 10: Processes, AppDomains,Contexts, and Threads

Posted by Apress Free Book | C#.NET February 02, 2009
In this Chapter you drill deeper into the constitution of a .NET executable host and come to understand the relationship between Win32 processes, application domains, contexts, and threads.

Enumerating Running Processes

To illustrate the process of manipulating Process types (pardon the redundancy), assume you have a C# console application named ProcessManipulator, which defines the following static helper method:

  public static void ListAllRunningProcesses()
        {
            // Get all the processes on the local machine.
            Process[] runningProcs = Process.GetProcesses(".");
            // Print out PID and name of each proc.
            foreach (Process p in runningProcs)
            {
                string info = string.Format("-> PID: {0}\tName: {1}",
                p.Id, p.ProcessName);
                Console.WriteLine(info);
            }
            Console.WriteLine("************************************\n");
        }

Notice how the static Process.GetProcesses() method returns an array of Process types that represent the running processes on the target machine (the dot notation seen here represents the local computer).

Once you have obtained the array of Process types, you are able to trigger any of the members seen in Table 10-2. Here, simply dump the process identifier (PID) and the name of each process. Assuming the Main() method has been updated to call this helper function, you will see something like the output in Figure 10-3.



Figure 10-3. Enumerating running processes

Investigating a Specific Process

In addition to obtaining a full and complete list of all running processes on a given machine, the static Process.GetProcessById() method allows you to obtain a single Process type via the associated PID. As you would hope, if you request access to a nonexistent process ID, an ArgumentException exception is thrown:

      
        // If there is no process with the PID of 987, a
            // runtime exception will be thrown.
            int pID = 987;
            Process theProc;
            try
            { theProc = Process.GetProcessById(pID); }
            catch // Generic catch for simplicitiy
            { Console.WriteLine("-> Sorry...bad PID!"); }

Investigating a Process' Thread Set

Now that you understand how to gain access to a Process type, you are able to programmatically investigate the set of all threads currently alive in the process at hand. This set of threads is represented by the strongly typed ProcessThreadCollection collection, which contains any number of individual ProcessThread types. To illustrate, assume the following additional static helper function has been added to your current application:

      public static void EnumThreadsForPid(int pID)
        {
            Process theProc;
            try
            { theProc = Process.GetProcessById(pID); }
            catch
            {
                Console.WriteLine("-> Sorry...bad PID!");
                Console.WriteLine("************************************\n");
                return;
            }
            // List out stats for each thread in the specified process.
            Console.WriteLine("Here are the thread IDs for: {0}",
            theProc.ProcessName);
            ProcessThreadCollection theThreads = theProc.Threads;
            foreach (ProcessThread pt in theThreads)
            {
                string info =
                string.Format("-> Thread ID: {0}\tStart Time {1}\tPriority {2}",
                pt.Id, pt.StartTime.ToShortTimeString(), pt.PriorityLevel);
                Console.WriteLine(info);
            }
            Console.WriteLine("************************************\n");
        }

As you can see, the Threads property of the System.Diagnostics.Process type provides access to the ProcessThreadCollection class. Here, we are printing out the assigned thread ID, start time, and priority level of each thread in the process specified by the client. Thus, if you update your program's Main() method to prompt the user for a PID to investigate:

   
         Console.WriteLine("***** Enter PID of process to investigate *****");
            Console.Write("PID: ");
            string pID = Console.ReadLine();
            int theProcID = int.Parse(pID);
            EnumThreadsForPid(theProcID);

you would find output along the lines of the Figure 10-4.



Figure 10-4. Enumerating the threads within a running process

The ProcessThread type has additional members of interest beyond Id, StartTime, and PriorityLevel. Table 10-3 documents some members of interest.

Table 10-3. Select Members of the ProcessThread Type

Member of System.Diagnostics.ProcessThread Meaning in Life
BasePriority Gets the base priority of the thread
CurrentPriority Gets the current priority of the thread
Id Gets the unique identifier of the thread
IdealProcessor Sets the preferred processor for this thread to run on
PriorityLevel Gets or sets the priority level of the thread
ProcessorAffinity Sets the processors on which the associated thread can run
StartAddress Gets the memory address of the function that the operating system called that started this thread
StartTime Gets the time that the operating system started the thread
ThreadState Gets the current state of this thread
TotalProcessorTime Gets the total amount of time that this thread has spent using the processor
WaitReason Gets the reason that the thread is waiting

Now before reading any further, be very aware that the ProcessThread type is not the entity used to create, suspend, or kill threads under the .NET platform. Rather, ProcessThread is a vehicle used to obtain diagnostic information for the active threads within a running process.

Investigating a Process' Module Set

Next up, let's check out how to iterate over the number of loaded modules that are hosted within a given process. Recall that a module is a generic name used to describe a given *.dll (or the *.exe itself) which is hosted by a specific process. When you access the ProcessModuleCollection via the Process.Module property, you are able to enumerate over all modules in a process; .NET-based, COM-based, or traditional C-based binaries. Ponder the following helper function:

       public static void EnumModsForPid(int pID)
        {
            Process theProc;
            try
            { theProc = Process.GetProcessById(pID); }
            catch
            {
                Console.WriteLine("-> Sorry...bad PID!");
                Console.WriteLine("************************************\n");
                return;
            }
            Console.WriteLine("Here are the loaded modules for: {0}",
            theProc.ProcessName);
            try
            {
                ProcessModuleCollection theMods = theProc.Modules;
                foreach (ProcessModule pm in theMods)
                {
                    string info = string.Format("-> Mod Name: {0}", pm.ModuleName);
                    Console.WriteLine(info);
                }
                Console.WriteLine("************************************\n");
            }
            catch { Console.WriteLine("No mods!"); }
        }

To illustrate one possible invocation of this function, let's check out the loaded modules for the process hosting your current console application (ProcessManipulator). To do so, run the application, identify the PID assigned to ProcessManipulator.exe, and pass this value to the EnumModsForPid() method (be sure to update your Main() method accordingly). Once you do, you may be surprised to see the list of *.dlls used for a simple console application (atl.dll, mfc42u.dll, oleaut32.dll and so forth.) Figure 10-5

shows a test run.

Figure 10-5. Enumerating the loaded modules within a running process

Total Pages : 13 12345

comments