Managing Processes
You use the System.Diagnostics.Process class to manage processes on your machine.
This class offers both
shared and instance members so that you can launch an external process
but also get a reference to one or more processes. The following code
shows how to launch an external process via the shared implementation of
the Start method:
Process.Start("Notepad.exe")
Any call to the Process.Start method will return a Process object. You can also specify arguments for the process by specifying the second parameter for the method as follows:
Process.Start("Notepad.exe", "C:\aFile.txt")
One of the most important features of the Start method is that you can also supply the username, password, and domain for launching a process:
Process.Start("Notepad.exe", "C:\aFile.txt",
"Alessandro", Password, "\\MYDOMAIN")
Notice that the password is necessarily an instance of the System.Security.SecureString class, so see the MSDN documentation about this. The Process
class also has an instance behavior that enables getting a reference to
a process instance. This is useful when you want to programmatically
control a process. With regard to this, you first need an instance of
the ProcessStartInfo class that can
store process execution information. The class exposes lots of
properties, but the most important are summarized in the following code
snippet:
Dim procInfo As New ProcessStartInfo
With procInfo
.FileName = "Notepad.exe"
.Arguments = "aFile.txt"
.WorkingDirectory = "C:\"
.WindowStyle = ProcessWindowStyle.Maximized
.ErrorDialog = True
End With
Particularly, the ErrorDialog property makes the Process
instance show up an error dialog if the process cannot be started
regularly. When you have done this, you simply create an instance of the
Process class and assign its StartInfo property; finally you invoke Start as demonstrated in the following code:
Dim proc As New Process
proc.StartInfo = procInfo
proc.Start()
'Alternative syntax:
'Dim proc As Process = Process.Start(procInfo)
Approaching
processes in this fashion is helpful if you need to programmatically
control processes. For example, you can wait until a process exits for
the specified number of milliseconds as follows:
'Waits for two seconds
proc.WaitForExit(2000)
To close a process you write the following code:
Finally, you can kill unresponsive processes by invoking the Kill method as follows:
The Process class also exposes the EnableRaisingEvents boolean property which allows setting if the runtime should raise the Exited
event when the process terminates. Such an event is raised if either
the process terminates normally or because of an invocation to the Kill method. Until now you saw how launching processes but the Process class is also useful when you need to get information on running processes as discussed in next subsection.
Querying Existing Processes
You can easily get information on running processes through some methods from the Process class that provide the ability of getting process instances. For example, GetProcesses returns an array of Process objects, each one representing a running process whereas GetProcessById and GetProcessByName return information on the specified process given the identification number or name, whereas GetCurrentProcess returns an instance of the Process class representing the current process. Then the Process class exposes lots of useful properties for retrieving information, each of them self-explanatory such as ProcessName, Id, ExitCode, Handle, or HasExited but also other advanced information properties, such as PageMemorySize or VirtualMemorySize,
which respectively return the memory size associated with the process
on the page memory or the virtual memory. The Visual Studio’s Object
Browser and IntelliSense can help you with the rest of available
properties. At the moment focus on how you can get information on
running processes. The coolest way for getting process information is
using LINQ to Objects. The following query, and subsequent For..Each loop, demonstrates how to retrieve a list of names of running processes:
Dim processesList = (From p In Process.GetProcesses
Select p.ProcessName).AsEnumerable
For Each procName In processesList
Console.WriteLine(procName)
Next
Notice that the query result is converted into IEnumerable(Of String) so that you can eventually bind the list to a user interface control supporting the type.
Introducing Multithreading
A thread is a unit of work.
The logic of threading-based programming is performing multiple
operations concurrently so that a big operation can be split across
multiple threads. The .NET Framework 4.0 offers support for
multithreading via the System.Threading namespace. But .NET 4.0 also introduces a new important library.
Creating Threads
You create a new thread for performing an operation with an instance of the System.Threading.Thread class. The constructor of this class requires you to also specify an instance of the System.Threading.ThreadStart delegate that simply points to a method that can actually do the work. Then you simply invoke the Thread.Start instance method. The following code snippet demonstrates how you can create a new thread:
Private Sub simpleThread()
Dim newThread As New Thread(New ThreadStart(AddressOf _
executeSimpleThread))
newThread.Start()
End Sub
Private Sub executeSimpleThread()
Console.WriteLine("Running a separate thread")
End Sub
To actually start the new thread, you invoke the method that encapsulates the thread instance, which in this case is simpleThread.
Creating Threads with Lambda Expressions
The following code snippet demonstrates how
you can take advantage of statement lambdas instead of providing an
explicit delegate:
Private Sub lambdaThread()
Dim newThread As New Thread(New _
ThreadStart(Sub()
Console.WriteLine("Thread with lambda")
End Sub))
newThread.Start()
End Sub
Now you can simply invoke the lambdaThread
method to run a secondary thread, and with one method you reach the
same objective of the previous code where two methods were implemented.
Passing Parameters
In many cases you might have the need to pass data to new threads. This can be accomplished by creating an instance of the ParameterizedThreadStart delegate, which requires an argument of type Object that you can use for sharing your data. The following code demonstrates how you create a thread with parameters:
Private Sub threadWithParameters(ByVal parameter As Object)
Dim newThread As New Thread(New _
ParameterizedThreadStart(AddressOf _
executeThreadWithParameters))
newThread.Start(parameter)
End Sub
Notice how the Thread.Start method has an overload that takes the specified parameter as the data. Because such data is of type Object,
you need to convert it into the most appropriate format. The following
code demonstrates how to implement a method that the delegate refers to
and how to convert the data into a hypothetical string:
Private Sub executeThreadWithParameters(ByVal anArgument As Object)
Dim aString = CType(anArgument, String)
Console.WriteLine(aString)
End Sub
Of course you can take
advantage of lambda expressions if you do not want to provide an
explicit delegate also in this kind of scenario.