interrogating a machine while writing your code, the
true power comes with the component creation you
get when you drag a resource node onto a Windows
Form. For example, if you drag the Application node
onto a Windows Form, you get an instance of the
System.Diagnostics.EventLog class added to
the nonvisual area of the designer. You can then write
an entry to this event log using the following code:
c#
this.eventLog1.Source = "My Server Explorer App";
this.eventLog1.WriteEntry("Something happened",
System.Diagnostics.EventLogEntryType.Information);
Code snippet Form1.cs
fiGure 9-3
server Connections .
161
Vb
Me.EventLog1.Source = "My Server Explorer App"
Me.EventLog1.WriteEntry("Something happened",
System.Diagnostics.EventLogEntryType.Information)
Code snippet Form1.vb
Because the preceding code creates a new Source in the Application Event Log, it
requires administrative rights to execute. If you are running Windows Vista or
Windows 7 with User Account Control enabled, you should create an
application manifest. This is discussed in Chapter 6.
After you run this code, you can view the results directly in the Server Explorer. Click the Refresh
button on the Server Explorer toolbar to ensure that the new Event Source is displayed under the
Application Event Log node.
For Visual Basic programmers, an alternative to adding an EventLog class to your code is to use
the built-in logging provided by the My namespace. For example, you can modify the previous code
snippet to write a log entry using the My.Application.Log.WriteEntry method:
Vb
My.Application.Log.WriteEntry("Button Clicked", TraceEventType.Information)
You can also write exception information using the My.Application.Log.WriteException
method, which accepts an exception and two optional parameters that provide additional
information.
Using the My namespace to write logging information has a number of additional benefits.
In the following configuration file, an EventLogTraceListener is specified to route log
information to the event log. However, you can specify other trace listeners — for example,
the FileLogTraceListener, which writes information to a log file by adding it to the
SharedListeners and Listeners collections:
< ?xml version="1.0" encoding="utf-8" ? >
< configuration >
< system.diagnostics >
< sources >
< source name="DefaultSource" switchName="DefaultSwitch" >
< listeners >
< add name="EventLog"/ >
< /listeners >
< /source >
< /sources >
< switches >
< add name="DefaultSwitch" value="Information"/ >
< /switches >
162 .
chaPter 9 SerVer explorer
<sharedListeners>
<add name="EventLog"
type="System.Diagnostics.EventLogTraceListener"
initializeData="ApplicationEventLog"/>
</sharedListeners>
</system.diagnostics>
</configuration>
This configuration also specifies a switch called DefaultSwitch. This switch is associated with the
trace information source via the switchName attribute and defines the minimum event type that will
be sent to the listed listeners. For example, if the value of this switch were Critical, events with the
type Information would not be written to the event log. The possible values of this switch are shown
in Table 9-1.
table 9-1: Values for DefaultSwitch
defaultswitch eVent tyPes written to loG
Off No Events
Critical Critical Events
Error Critical and Error Events
Warning Critical, Error, and Warning Events
Information Critical, Error, Warning, and Information events
Verbose Critical, Error, Warning, Information, and Verbose events
ActivityTracing Start, Stop, Suspend, Resume, and Transfer events
All All Events
Note that there are overloads for both WriteEntry and
WriteException that do not require an event type to be
specified. In this case the event types will default to Information
and Error, respectively.
Management classes
Figure 9-4 shows the full list of management classes
available via the Server Explorer. Each node exposes a set
of functionalities specific to that device or application. For
example, right-clicking the Printers node enables you to add
a new printer connection, whereas right-clicking the named
node under My Computer enables you to add the computer to
a domain or workgroup. The one thing common to all these
nodes is that they provide a strongly typed wrapper around the
Windows Management Instrumentation (WMI) infrastructure.
In most cases, it is simply a matter of dragging the node
fiGure 9-4
server Connections .
163
representing the information in which you’re
interested across to the form. From your code
you can then access and manipulate that
information.
To give you an idea of how these wrappers can
be used, this section walks through how you
can use the management classes to retrieve
information about a computer. Under the My
Computer node, you will see a node with the
name of the local computer. Selecting this
node and dragging it onto the form gives you a
ComputerSystem component in the nonvisual
area of the form. Also add a Label control, a
TextBox control, a Button, and a PropertyGrid
control from the All Windows Forms tab on
the Toolbox and arrange them on the Form as
shown in Figure 9-5.
If you look in the Solution Explorer, you will see that it has also added a custom component called
root.CIMV2.Win32_ComputerSystem (or similar depending on the computer configuration).
This custom component is generated by the Management Strongly Typed Class Generator
(Mgmtclassgen.exe) and includes the ComputerSystem and other classes, which will enable you to
expose WMI information.
If you click the computerSystem1 object on the form, you can see the information about that
computer in the Properties window. In this application, however, you’re not that interested in that
particular computer; that computer was selected as a template to create the ComputerSystem class.
The ComputerSystem1 object can be deleted, but before deleting it, take note of the Path property
of the object. The Path is used, combined with the computer name entered in the form in Figure 9-5,
to load the information about that computer. You can see this in the following code that is added to
the button click event handler for the Load button:
c#
const string compPath = "\\\\{0}\\root\\CIMV2:Win32_ComputerSystem.Name=\"{0}\"";
if (!string.IsNullOrEmpty(this.textBox1.Text))
{
string computerName = this.textBox1.Text;
string pathString = string.Format(compPath, computerName);
var path = new System.Management.ManagementPath(pathString);
ROOT.CIMV2.ComputerSystem cs = new ROOT.CIMV2.ComputerSystem(path);
this.propertyGrid1.SelectedObject = cs;
}
Code snippet Form2.cs
fiGure 9-5
164 .
chaPter 9 SerVer explorer
Vb
Const compPath As String = "\\{0}\root\CIMV2:Win32_ComputerSystem.Name=""{0}"""
If Not Me.TextBox1.Text = "" Then
Dim computerName As String = Me.TextBox1.Text
Dim pathString As String = String.Format(compPath, computerName)
Dim path As New System.Management.ManagementPath(pathString)
Dim cs As New ROOT.CIMV2.ComputerSystem(path)
Me.PropertyGrid1.SelectedObject = cs
End If
Code snippet Form2.vb
In this example, the Path property, which was
obtained earlier from the computerSystem1 object,
has been used in a string constant with the string
replacement token {0} where the computer name
should go. When the button is clicked, the computer
name entered into the textbox is combined with this
path using String.Format to generate the full WMI
path. The path is then used to instantiate a new
ComputerAccount object, which is in turn passed to
the PropertyGrid control. The result of this at run
time is shown in Figure 9-6.
Though most properties are read-only, for those fields
that are editable, changes made in this PropertyGrid are
immediately committed to the computer. This behavior can
be altered by changing the AutoCommit property on the
ComputerSystem class.
Management events
In the previous section you learned how you can drag a
management class from the Server Explorer onto the form and
then work with the generated classes. The other way to work
with the WMI interface is through the Management Events
node. A management event enables you to monitor any WMI
data type and have an event raised if an object of that type is
created, modified, or deleted. By default, this node is empty,
but you can create your own by right-clicking the Management
Events node and selecting Add Event Query, which invokes the
dialog shown in Figure 9-7.
Use this dialog to locate the WMI data type in which you are
interested. Because there are literally thousands of these, it
is useful to use the Find box. In Figure 9-7, the search term
“process” was entered, and the class CIM Processes was found
fiGure 9-6
fiGure 9-7
server Connections .
165
under the root\CIMV2 node. Each instance of this class represents a single process running on
the system. You are only interested in being notified when a new process is created, so ensure that
Object Creation is selected from the drop-down menu.
After clicking OK, a CIM Processes Event Query node is
added to the Management Events node. If you open a new
instance of an application on your system, such as
Notepad, you will see events being progressively added
to this node. In the Build Management Event Query
dialog shown in Figure 9-7, the default polling interval
was set to 60 seconds, so you may need to wait up to
60 seconds for the event to show up in the tree once
you have made the change.
When the event does finally show up, it appears along
with the date and time in the Server Explorer, and it
also appears in the Output window, as shown in the
lower pane of Figure 9-8. If you select the event, you
will notice that the Properties window is populated
with a large number of properties that don’t really
make any sense. However, once you know which of
the properties to query, it is quite easy to trap, filter,
and respond to system events.
To continue the example, drag a CheckBox control and a ListBox control from the Toolbox onto a
new Windows Form.
Next drag the CIM Processes Event Query node from the Server Explorer onto a new form. This
generates an instance of the System.Management.ManagementEventWatcher class, with properties
configured so it will listen for the creation of a new process. The actual query can be accessed via
the QueryString property of the nested ManagementQuery object. As with most watcher classes, the
ManagementEventWatch class triggers an event when the watch conditions are met — in this case,
the EventArrived event. To handle this event, add the following code:
c#
private void managementEventWatcher1_EventArrived(System.Object sender,
System.Management.EventArrivedEventArgs e)
{
foreach (System.Management.PropertyData p in e.NewEvent.Properties)
{
if (p.Name == "TargetInstance")
{
var mbo = (System.Management.ManagementBaseObject)p.Value;
string[] sCreatedProcess = {(string)mbo.Properties["Name"].Value,
(string)mbo.Properties["ExecutablePath"].
Value };
this.BeginInvoke(new LogNewProcessDelegate(LogNewProcess),
sCreatedProcess);
}
}
}
fiGure 9-8
166 .
chaPter 9 SerVer explorer
delegate void LogNewProcessDelegate(string ProcessName, string ExePath);
private void LogNewProcess(string ProcessName, string ExePath)
{
this.listBox1.Items.Add(string.Format("{0}—{1}", ProcessName, ExePath));
}
private void checkBox1_CheckedChanged(System.Object sender, System.EventArgs e)
{
if (this.checkBox1.Checked)
{
this.managementEventWatcher1.Start();
}
else
{
this.managementEventWatcher1.Stop();
}
}
Code snippet Form3.cs
Vb
Private Sub ManagementEventWatcher1_EventArrived(ByVal sender As System.Object, _
ByVal e As System.Management.EventArrivedEventArgs)
For Each p As System.Management.PropertyData In e.NewEvent.Properties
If p.Name = "TargetInstance" Then
Dim mbo As System.Management.ManagementBaseObject = _
CType(p.Value, System.Management.ManagementBaseObject)
Dim sCreatedProcess As String() = {mbo.Properties("Name").Value, _
mbo.Properties("ExecutablePath").Value}
Me.BeginInvoke(New LogNewProcessDelegate(AddressOf LogNewProcess), _
sCreatedProcess)
End If
Next
End Sub
Delegate Sub LogNewProcessDelegate(ByVal ProcessName As String, _
ByVal ExePath As String)
Private Sub LogNewProcess(ByVal ProcessName As String, ByVal ExePath As String)
Me.ListBox1.Items.Add(String.Format("{0}—{1}", ProcessName, ExePath))
End Sub
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles CheckBox1.CheckedChanged
If Me.CheckBox1.Checked Then
Me.ManagementEventWatcher1.Start()
Else
Me.ManagementEventWatcher1.Stop()
End If
End Sub
Code snippet Form3.vb
server Connections .
167
In the event handler, you need to iterate through the Properties collection on the NewEvent object.
Where an object has changed, two instances are returned: PreviousInstance, which holds the state
at the beginning of the polling interval, and TargetInstance, which holds the state at the end of the
polling interval. It is possible for the object to change state multiple times within the same polling