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.
- I named my project ViewProcessAndThreadInfo (in Visual Studio’s New Project dialog).
- 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);
}
Comments
- Anonymous
March 25, 2007
Posted by: Sankar Ramasubbu I am going to show you how to automate the operation of setting and deleting - Anonymous
April 05, 2007
Today I am going to talk about how to automate the opertaion of running target control command and retrieveing - Anonymous
April 19, 2007
This would be a lot more useful if the source files were attached, rather than requiring the iteration of several posts and multiple cut / copy / paste cycles. - Anonymous
April 25, 2007
We would like to post the source files too as it would make it more simpler. However, there are certain legalities in this that we would need to take care before. we are looking into this