VIEWING PROCESS AND THREAD DATA PROGRAMATICALLY

 

Today, I am going to talk about how to retrieve process and thread information from PB debugger. This post assumes that you already know how to download a debug image to a Device Emulator using PB. Please look at Brent Bishop’s post on "Automating the Download of a Run-Time Image" for getting code for this purpose.

 

We will follow the following steps for this post.

 

Step 1: Create a new Visual Studio add-in by following all steps that you followed for Brent Bishop’s post on “Automating the Download of a Run-Time Image ” with two modifications.

 

  1. I named my project ViewProcessAndThreadInfo (in Visual Studio’s New Project dialog).
  2. I gave it the following name and description.(on page 3 of the Visual Studio Add-in Wizard):

 

1) Name: Platform Builder – View Process and Thread Information from Debugger

2) Description: This can be used to view Processes and Thread information on a device.

 

 

Step 2: Add all the references that Brent Bishop added to his post on “Automating the Download of a Run-Time Image ” by right clicking on the References node in the Solution Explorer and then clicking on "Add Reference...".

 

Step 3: Add a new class to the project for all the code that we will be adding for the add-in. Add a new class to the project by right clicking on the project in the Solution Explorer and clicking Add -> Class. Next name the class, I named my class ViewProcessAndThreadInfo, and click Add.

Step 4: Open Connect.cs (created by Visual Studio) and find the OnConnection method. I modified this to add two menu items to the tools menu. One for Viewing process and Thread and the other for detaching. Note that AddNamedCommand2 has a lot of parameters, I just copied the parameters generated by Visual Studio. Check out the MSDN for more information on those parameters if you are interested.

//Add a command to the Commands collection:

Command command1 = commands.AddNamedCommand2(_addInInstance, "ViewProcessAndThreadInfo", "View Process And Thread Info", "Executes the command for ViewProcessAndThreadInfo", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);

 

Command command2 = commands.AddNamedCommand2(_addInInstance, "Detach", "Detach From Device", "This detaches the PB instance from device", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);

       //Add a control for the command to the tools menu:

        if ((command1 != null) && (toolsPopup != null))

        {

            command1.AddControl(toolsPopup.CommandBar, 1);

        }

       //Add a control for the command to the tools menu:

      if ((command2 != null) && (toolsPopup != null))

      {

            command2.AddControl(toolsPopup.CommandBar, 2);

      }

Step 5: Next scroll to the bottom to the Exec method, which is called when our add-in's menu item is clicked. Here we can add code to handle the two menu items. As in previous examples, add calls to static methods in our newly created ViewProcessAndThreadInfo class.

public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)

{

            handled = false;

           

            if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)

            {

                if (commandName == "ViewProcessAndThreadInfo.Connect.ViewProcessAndThreadInfo")

                {

                    ViewProcessAndThreadInfo.GetProcessAndThreadInfo(_applicationObject);

                    handled = true;

                    return;

                }

    else if (commandName == "ViewProcessAndThreadInfo.Connect.Detach")

                {

                    ViewProcessAndThreadInfo.Detach(_applicationObject);

                    handled = true;

                    return;

                }

    }

}

 

Step 6: Next, I made changes to QueryStatus method which gets called to decide if our menu items should appear enabled or disabled.This code uses a static method Connected and CommandNotCompleted . We will impliment CommandNotCompleted in the ViewProcessAndThreadInfo class. We will use the implementation of Connected from Brent Bishop's blog on "Automating the Download of a Run-Time Image"

 

public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)

{

                 

            if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)

            {

                if (commandName == "ViewProcessAndThreadInfo.Connect.ViewProcessAndThreadInfo")

                {

         if (ViewProcessAndThreadInfo.CommandNotCompleted)

                    {

                        status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported;

                    }

                    else

                    {

            status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;

                    }

                    return;

                }

                else if (commandName == "ViewProcessAndThreadInfo.Connect.Detach")

                {

                    if (!ViewProcessAndThreadInfo.Connected(_applicationObject) || ViewProcessAndThreadInfo.CommandNotCompleted)

                    {

                        status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported;

                    }

                    else

                    {

                        status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;

                    }

           return;

}

           

Step 7: I copied all the variables and functions defined in DownloadSample class in Brent Bishop‘s post for “Automating the Download of a Run-Time Image” to my class. My addin uses his code for the purpose of downloading image to a Device Emulator after making the following 4 changes.

1) I deleted the Attach() function implementation completely.

2) I changed

private const string Caption = "PB - Download Sample";

to

private const string Caption = "PB - View Process and Thread Info";

 

     3) In DetachToThread call, I set the variable done to false at the end. I did this to make sure that image got downloaded when I next click the menu item View process and Thread Info.

     4) I modified the function call WaitTillImageIsUpAndRunning as follows to set a boolean flag when the image is booted up.

 

public static void WaitTillImageIsUpAndRunning(int timeout, int idletime)

{

//Get the current text in the active pane

string oldText = GetCurrentText();

string newText;

bool ImgBootedUp = false;

// Calculate how many times to try

int attempts = timeout * 60 / idletime;

do

{

System.Threading.Thread.Sleep(idletime * 1000);

//Get the latest text from Active Pane

newText = GetCurrentText();

//If we have no new text, it means device has booted up completely

if (oldText == newText)

ImgBootedUp = true;

oldText = newText;

newText = null;

attempts--;

} while ((!ImgBootedUp) && (attempts != 0));

if (0 == timeout)

MessageBox.Show("ERROR: The device did not launch in 10 minutes..", Caption, MessageBoxButtons.OK, MessageBoxIcon.Error);

else

{

imagebooted = true;

}

}

Step 8: Add the following using statements to the top of ViewProcessAndThreadInfo.cs:

 

// Standard namespaces

using System;

using System.IO;

using System.Collections;

using System.Windows.Forms;

// PlatformBuilder and Visual Studio namespaces

using EnvDTE;

using EnvDTE80;

using Interop.Microsoft.PlatformBuilder.Diagnostics;

Step 9: I defined a Boolean variable to keep track if we are in between executing a menu item operation and also a property CommandNotCompleted that we called earlier in Connect.cs. I added a Boolean variable imageBooted that we have set in the WaitTillImageIsUpAndRunning function call when the image has booted up. Here is the code for it.

/// <summary>

/// A value indicating whether image booted up completely.

/// </summary>

private static bool imagebooted = false;

 

/// <summary>

/// A value indicating whether the ViewProcessandThreadInfo call is still running.

/// </summary>

private static bool commandNotCompleted = false;

/// <summary>

/// Checks if the command is still running and menu option should be disabled

/// </summary>

/// <returns>A boolean indicating command is running or not</returns>

public static bool CommandNotCompleted

{

    get

    {

         return commandNotCompleted;

    }

}

Step 10: I created the GetProcessAndThreadInfo function which is called in Connect.cs when the Tools menu item View Process and Thread info is clicked. Here is the implementation

 /// <summary>

/// Downloads a run-time image to a device. Displays the process and thread information

/// </summary>

/// <param name="dte">A reference to the DTE for controlling Visual Studio.</param>

public static void GetProcessAndThreadInfo(DTE2 vsdte)

 {

          

            // Save the reference to the DTE

            dte = vsdte;

            // Start the thread

            System.Threading.Thread thread = new System.Threading.Thread(GetProcessAndThreadInfo);

    thread.Start();

}

Step 11: In the GetProcessAndThreadInfo() , I set the commandNotCompleted flag to true so that option ViewProcessAndThreadInfo appears disabled in Visual Studio Tool menu till the function call has been completed. I download an image to a device and wait for it to boot up after making sure that we are in a disconnected state. We have used AttachThread call implemented in Brent Bishop’s post “Downloading an image to Device Emulator”. Once we are sure that the image has booted up we make a call to ViewProcessAndThreadInformation function to get process and thread information for process "shell.exe".

/// <summary>

/// Downloads a run-time image to a device. Displays the process and thread information

/// </summary>

public static void GetProcessAndThreadInfo()

{

            //Setting it so that we can disable the menu option for ViewProcessandThreadInfo call while its running

            commandNotCompleted = true;

            //Check if PB is already connected to a device

            if (!done || !imagebooted)

            {

                //Connect PB to a device

                AttachThread();

                //Wait for the image to be completely launched on the device

                WaitTillImageIsUpAndRunning(10,20);

                if (imagebooted)

                {

                    MessageBox.Show("PB has successfully downloaded and connected to the device.", Caption, MessageBoxButtons.OK);

             }

                else

                {

                    MessageBox.Show("PB was not able to connect to device.", Caption, MessageBoxButtons.OK, MessageBoxIcon.Error);

                }

            }

            if (imagebooted)

            {

               string processName = "shell.exe";

       //Get Process and Thread Information for a particular process

                ViewProcessAndThreadInformation(processName);

            }

            // Command has completed and we are ready for next one

            commandNotCompleted = false;

}

Step 12: We need to implement the method ViewProcessAndThreadInformation() that we called in GetProcessAndThreadInfo method.This method gives the process and thread infromation for a particular process.

/// <summary>

/// This displays information about the process and Thread running on the device

/// </summary>

/// <param name="processName">Process we want to get information about</param>

public static void ViewProcessAndThreadInformation(string processName)

{

            try

            {

                // Get the debugger object model

                CCeSystemDiagnostics diagnostics = (CCeSystemDiagnostics)dte.GetObject("Yamazaki-OM");

                csdConnection connection = (csdConnection)diagnostics.GetConnection();

                //Get the interface to get information about modules/processes window

                csdDebugViews debugView = (csdDebugViews)diagnostics.GetDebugViews();

                //Get the List of Process

                csdProcesses processList = (csdProcesses)debugView.GetProcesses();

                //Get the Total number of process running on device

                int processCount = processList.Count();

                if (processCount == 0)

                {

                    MessageBox.Show("ERROR: There are no processes running on the device..", Caption, MessageBoxButtons.OK, MessageBoxIcon.Error);

                }

                else

                {

                    bool matchFound = false;

                    csdProcess process = null;

                    //Get an enumerator to read through the list of process

     IEnumerator enumProcess = (IEnumerator)processList._NewEnum;

                    enumProcess.MoveNext();

                    //Looping through all the processes

                    while (!matchFound && (process = (csdProcess)enumProcess.Current) != null)

                    {

                        //Messages variables to be displayed

                        string Caption = "View Process Information for Shell.exe";

                        string message = "";

                        //Set the enumerator for the PropertyList of the current Process

                        CProperty property = null;

                        IEnumerator enumProcessProperties = (IEnumerator)Proc.GetProperties();

                        enumProcessProperties.MoveNext();

                    //Get the current Process name by looking at the first property

                        string CurrentProcessName = (String)(((CProperty)(enumProcessProperties.Current)).Value);

                        //Check if the current process is the one we are looking for

                        if (CurrentProcessName == processName)

                        {

                            //We found the process "shell.exe"

                            matchFound = true;

                            //Looping though all the properties of a process

                            while ((property = (CProperty)enumProcessProperties.Current) != null)

                            {

                                //Generating a message which has all the property names and Values

  message += property.Name + ": " + property.Value + "\t ";

                                //Move the enumerator to point at next entry

                                enumProcessProperties.MoveNext();

                            }

         //Display the Process Information

                            MessageBox.Show(message, Caption, MessageBoxButtons.OK);

                            //Get the threads for the process shell.exe

                            GetThreadInformation(process);

                        }

                        else

                        {

                            //Move to the next process as the current process is not the one we are looking for

                            enumProcess.MoveNext();

  }

                    }

                }

            }

            catch (Exception e)

            {

            MessageBox.Show(e.Message, Caption, MessageBoxButtons.OK, MessageBoxIcon.Error);

            }

}

 /// <summary>

 ///Gets the information about all the threads in the process Proc

 /// </summary>

 /// <param name="Proc"> Process for which we want Thread information</param>

public static void GetThreadInformation(csdProcess Process)

{

            //Messages to be displayed

            string Caption = "View Thread Information for Shell.exe";

            string message = "ThreadNo \tHandle\t\t State\tAccessKey\tCurProcHandle\tCurPrio\tBasePrio\tKernelTime\tUserTime\n";

            //Retrieves the Thread infromation for the process

            csdThreads Threads = (csdThreads)Process.GetThreads();

            int ThreadCounter = 1;

            csdThread Thread = null;

            //Initialize the enumerator to read thread list

            IEnumerator enumThread = (IEnumerator)Threads._NewEnum;

            enumThread.MoveNext();

            //Looping through all the threads

            while ((Thread = (csdThread)enumThread.Current) != null)

            {

                message += "Thread[" + ThreadCounter++ + "]\t";

                //Set the enumerator for the property list of a Thread

                IEnumerator enumThreadProperties = (IEnumerator)Thread.GetProperties();

                enumThreadProperties.MoveNext();

                CProperty threadProperty = null;

                //Looping though all the properties of a thread

                while ((threadProperty = (CProperty)enumThreadProperties.Current) != null)

                {

                    //Generating a message which has all the thread property Values

                    message += threadProperty.Value + "\t";

                    //Move the enumerator to point at next entry

                    enumThreadProperties.MoveNext();

                }

                message += "\n";

                //Move to the next thread

                enumThread.MoveNext();

           }

            MessageBox.Show(message, Caption, MessageBoxButtons.OK);

}