19.1. The Servers Node

The Servers node would be better named Computers, because it can be used to attach to and interrogate any computer to which you have access, regardless of whether it is a server or a desktop workstation. Each computer is listed as a separate node under the Servers node. Below each computer node is a list of the hardware, services, and other components that belong to that computer. Each of these contains a number of activities or tasks that can be performed. Several software vendors have components that plug into and extend the functionality provided by the Server Explorer.

To access Server Explorer, select Server Explorer on the View menu. By default, the local computer appears in the Servers list. To add computers, right-click the Servers node and select Add Server from the context menu. This opens the Add Server dialog shown in Figure 19-2.

Figure 19.2. Figure 19-2

Entering a computer name or IP address will initiate an attempt to connect to the machine using your credentials. If you do not have sufficient privileges, you can elect to connect using a different user name by clicking the appropriate link. The link appears to be disabled, but clicking it does bring up a dialog in which you can provide an alternative user name and password.

You will need Administrator privileges on any server that you want to access through the Server Explorer.

19.1.1. Event Logs

The Event Logs node gives you access to the machine event logs. You can launch the Event Viewer from the right-click context menu. Alternatively, as shown in Figure 19-3, you can drill into the list of event logs to view the events for a particular application. Clicking on any of the events displays information about the event in the Properties window.

Figure 19.3. Figure 19-3

Although the Server Explorer is useful for 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, in this case, if you drag the Application node onto a Windows Form, you get an instance of the System.Diagnostic.EventLog class added to the nonvisual area of the designer. You can then write an entry to this event log using the following code:

Private Sub btnLogEvent_Click(ByVal sender As Object, ByVal e As EventArgs) _
   Handles btnLogEvent.Click
    Me.EventLog1.Source = "My Server Explorer App"
    Me.EventLog1.WriteEntry("Button Clicked", EventLogEntryType.Information)
End Sub

Because the preceding code creates a new Source in the Application Event Log, it will require administrative rights to execute. If you are running Windows Vista with User Account Control enabled, then you should create an application manifest. This is discussed Chapter 6.

You can also write exception information using the WriteException method, which accepts an exception and a string that may provide additional debugging information. Unfortunately, you still have to manually set the Source property before calling the WriteEntry method. Of course, this could also have been set using the Properties window for the EventLog1 component.

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 Application.Log property:

Private Sub btnLogMyEvent_Click(ByVal sender As Object, ByVal e As EventArgs) _
   Handles btnLogMyEvent.Click
    My.Application.Log.WriteEntry("Button Clicked", EventLogEntryType.Information)
End Sub

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>
        <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, then events with the type Information would not be written to the event log. The possible values of this switch are shown in Table 19-1.

Table 19.1. Values for DefaultSwitch
DefaultSwitchEvent Types Written to Log
OffNo events
CriticalCritical events
ErrorCritical and Error events
WarningCritical, Error, and Warning events
InformationCritical, Error, Warning, and Information events
VerboseCritical, Error, Warning, Information, and Verbose events
ActivityTracingStart, Stop, Suspend, Resume, and Transfer events
AllAll events

Note that there are overloads for both WriteEntry and WriteException that do not require an event type to be specified. These methods will default to Information and Error.

19.1.2. Management Classes

Figure 19-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 representing the information in which you're interested across to the form. From your code you can then access and manipulate that information.

Figure 19.4. Figure 19-4

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 will give you a ComputerSystem component in the nonvisual area of the form, as shown in Figure 19-5.

Figure 19.5. Figure 19-5

If you look in the Solution Explorer, you will see that it has also added a custom component called root.CIMV2.Win32_ComputerSystem.vb (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 ComputerSystem 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 19-5, to load the information about that computer. You can see this in the following code to handle the button click event for the "Load Details" button:

Public Class Form2
    Private Const CComputerPath As String = _

   "\{0}
ootCIMV2:Win32_ComputerSystem.Name=""{0}"""

    Private Sub btnComputerDetails_Click(ByVal sender As System.Object, _
                                         ByVal e As System.EventArgs) _
                                                   Handles btnComputerDetails.Click
        If Not Me.txtComputerName.Text = "" Then
            Dim computerName As String = Me.txtComputerName.Text
            Dim pathString As String = String.Format(CComputerPath, computerName)
            Dim path As New System.Management.ManagementPath(pathString)
            Dim cs As New ROOT.CIMV2.ComputerSystem(path)

            Me.ComputerPropertyGrid.SelectedObject = cs
        End If
    End Sub
End Class

In this example, the Path property is taken from the ComputerSystem1 object and the computer name component is replaced with a string replacement token, {0}. 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 a PropertyGrid called ComputerPropertyGrid. This is shown in Figure 19-6.

Figure 19.6. Figure 19-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.

19.1.3. 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 will be empty, but you can create your own by selecting Add Event Query, which will invoke the dialog shown in Figure 19-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 19-7, the search term "process" was entered, and the class CIM Processes was found under the rootCIMV2 node. Each instance of this class represents a single process running on the system. We are only interested in being notified when a new process is created, so ensure that the "Object creation" is selected from the drop-down menu.

Figure 19.7. Figure 19-7

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 19-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 will appear along with the date and time in the Server Explorer, and it will also appear in the Output window, as shown in the lower pane of Figure 19-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.

Figure 19.8. Figure 19-8

To continue the example, drag the CIM Processes Event Query node onto a 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 generate an event handler, add the following code:

Private Sub ManagementEventWatcher1_EventArrived(ByVal sender As System.Object, _
                            ByVal e As System.Management.EventArrivedEventArgs) _
                            Handles ManagementEventWatcher1.EventArrived
    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.lbProcesses.Items.Add(String.Format("{0} - {1}", ProcessName, ExePath))
End Sub

Private Sub chkWatchEvent_CheckedChanged(ByVal sender As System.Object, _
                                         ByVal e As System.EventArgs) _
                                         Handles chkWatchEvent.CheckedChanged
    If Me.chkWatchEvent.Checked Then
        Me.ManagementEventWatcher1.Start()
    Else
        Me.ManagementEventWatcher1.Stop()
    End If
End Sub

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 period. If this is the case, an event will only be triggered when the state at the end of the period differs from the state at the beginning of the period. For example, no event is raised if a process is started and then stopped within a single polling interval.

The event handler constructs a new ManagementBaseObject from a value passed into the event arguments to obtain the display name and executable path of the new process. Because the event is called on a background thread, we cannot directly update the ListBox. Instead we must call Invoke to execute the LogNewProcess function on the UI thread. Figure 19-9 shows the form in action.

Figure 19.9. Figure 19-9

Notice also the addition of a checkbox to the form to control whether the form is watching for user events. The generated code for the event watcher does not automatically start the watcher.

19.1.4. Message Queues

The Message Queues node, expanded in Figure 19-10, gives you access to the message queues available on your computer. You can use three types of queues: private, which will not appear when a foreign computer queries your computer; public, which will appear; and system, which is used for unsent messages and other exception reporting. In order for the Message Queues node to be successfully expanded, you need to ensure that MSMQ is installed on your computer. This can be done via the Turn Windows Features On or Off task menu item accessible from Start Settings Control Panel Programs and Features. Some features of MSMQ are available only when a queue is created on a computer that is a member of a domain.

Figure 19.10. Figure 19-10

In Figure 19-10, the samplequeue has been added to the Private Queues node by selecting Create Queue from the right-click context menu. Once you have created a queue, you can create a properly configured instance of the MessageQueue class by dragging the queue onto your form. To demonstrate the functionality of the MessageQueue object, use the following code to add a couple of textboxes and a "Send" button. The "Send" button is wired up to use the MessageQueue object to send the message entered in the first textbox. In the Load event for the form, a background thread is created that continually polls the queue to retrieve messages, which will populate the second textbox:

Public Class Form4
    Private Sub btnSend_Click(ByVal sender As System.Object, _
                              ByVal e As System.EventArgs) Handles btnSend.Click
        Me.MessageQueue1.Send(Me.txtSendMsg.Text, "Message: " & _
                               Now.ToShortDateString & " " & Now.ToShortTimeString)
    End Sub

    Private Sub Form4_Load(ByVal sender As System.Object, _
                                   ByVal e As System.EventArgs) Handles MyBase.Load
        Dim monitorThread As New Threading.Thread(AddressOf MonitorMessageQueue)
        monitorThread.IsBackground = True
        monitorThread.Start()
    End Sub

    Private Sub MonitorMessageQueue()
        Dim m As Messaging.Message
        While True
            Try
                m = Me.MessageQueue1.Receive(New TimeSpan(0, 0, 0, 0, 50))
                Me.ReceiveMessage(m.Label, m.Body)
            Catch ex As Messaging.MessageQueueException
                If Not ex.MessageQueueErrorCode = _
                                     Messaging.MessageQueueErrorCode.IOTimeout Then
                    Throw ex

End If
            End Try
            Threading.Thread.Sleep(10000)
        End While
    End Sub

    Private Delegate Sub MessageDel(ByVal lbl As String, ByVal msg As String)
    Private Sub ReceiveMessage(ByVal lbl As String, ByVal msg As String)
        If Me.InvokeRequired Then
            Me.Invoke(New MessageDel(AddressOf ReceiveMessage), lbl, msg)
            Return
        End If
        Me.txtReceiveMsg.Text = msg
        Me.lblMessageLabel.Text = lbl
    End Sub
End Class

Note in this code snippet that the background thread is never explicitly closed. Because the thread has the IsBackGround property set to True, it will automatically be terminated when the application exits. As with the previous example, because the message processing is done in a background thread, you need to switch threads when you update the user interface using the Invoke method. Putting this all together, you get a form like the one shown in Figure 19-11.

Figure 19.11. Figure 19-11

As messages are sent to the message queue, they will appear under the appropriate queue in Server Explorer. Clicking the message will display its contents in the Properties window.

19.1.5. Performance Counters

One of the most common things developers forget to consider when building an application is how it will be maintained and managed. For example, consider an application that was installed a year ago and has been operating without any issues. All of a sudden, requests start taking an unacceptable amount of time. It is clear that the application is not behaving correctly, but there is no way to determine the cause of the misbehavior. One strategy for identifying where the performance issues are is to use performance counters. Windows has many built-in performance counters that can be used to monitor operating system activity, and a lot of third-party software also installs performance counters so administrators can identify any rogue behavior.

The Performance Counters node in the Server Explorer tree, expanded in Figure 19-12, has two primary functions. First, it enables you to view and retrieve information about the currently installed counters. You can also create new performance counters, as well as edit or delete existing counters. As you can see in Figure 19-12, under the Performance Counters node is a list of categories, and under those is a list of counters.

Figure 19.12. Figure 19-12

You must be running Visual Studio with Administrator rights in order to view the Performance Counters under the Server Explorer.

To edit either the category or the counters, select Edit Category from the right-click context menu for the category. To add a new category and associated counters, right-click the Performance Counters node and select Create New Category from the context menu. Both of these operations use the dialog shown in Figure 19-13. Here, a new performance counter category has been created that will be used to track a form's open and close events.

Figure 19.13. Figure 19-13

The second function of the Performance Counters section is to provide an easy way for you to access performance counters via your code. By dragging a performance counter category onto a form, you gain access to read and write to that performance counter. To continue with this chapter's example, drag the new .My Application performance counters, Form Open and Form Close, onto your form. Also add a couple of textboxes and a button so you can display the performance counter values. Finally, rename the performance counters so they have a friendly name. This should give you a form similar to the one shown in Figure 19-14.

Figure 19.14. Figure 19-14

In the properties for the selected performance counter, you can see that the appropriate counter — in this case, Form Close — has been selected from the .My Application category. You will also notice a MachineName property, which is the computer from which you are retrieving the counter information, and a ReadOnly property, which needs to be set to False if you want to update the counter. (By default, the ReadOnly property is set to True.) To complete this form, add the following code to the "Retrieve Counters" button:

Private Sub btnRetrieveCounters_Click(ByVal sender As System.Object, _
                                     ByVal e As System.EventArgs) _
                                                  Handles btnRetrieveCounters.Click
    Me.txtFormOpen.Text = Me.PerfCounterFormOpen.RawValue
    Me.txtFormClose.Text = Me.PerfCounterFormClose.RawValue
End Sub

You also need to add code to the application to update the performance counters. For example, you might have the following code in the Load and FormClosing event handlers:

Private Sub Form5_Closing(ByVal sender As Object, _
                            ByVal e As System.Windows.Forms.FormClosingEventArgs) _
                                                             Handles Me.FormClosing
    Me.PerfCounterFormClose.Increment()
End Sub

Private Sub Form5_Load(ByVal sender As Object, _
                         ByVal e As System.EventArgs) Handles Me.Load
    Me.PerfCounterFormOpen.Increment()
End Sub

When you dragged the performance counter onto the form, you may have noticed a smart tag on the performance counter component that had a single item, Add Installer. When the component is selected, as in Figure 19-14, you will notice the same action at the bottom of the Properties window. Clicking this action in either place adds an Installer class to your solution that can be used to install the performance counter as part of your installation process. Of course, for this installer to be called, the assembly it belongs to must be added as a custom action for the deployment project. (For more information on custom actions, see Chapter 49.)

In the previous version of Visual Studio, you needed to manually modify the installer to create multiple performance counters. In the current version, you can simply select each additional performance counter and click Add Installer. Visual Studio 2008 will direct you back to the first installer that was created and will have automatically added the second counter to the Counters collection of the PerformanceCounterInstaller component, as shown in Figure 19-15.

Figure 19.15. Figure 19-15

You can also add counters in other categories by adding additional PerformanceCounterInstaller components to the design surface. You are now ready to deploy your application with the knowledge that you will be able to use a tool such as perfmon to monitor how your application is behaving.

19.1.6. Services

The Services node, expanded in Figure 19-16, shows the registered services for the computer. Each node indicates the state of that service in the bottom-right corner of the icon. Possible states are stopped, running, or paused. Selecting a service will display additional information about the service, such as other service dependencies, in the Properties window.

Figure 19.16. Figure 19-16

As with other nodes in the Server Explorer, each service can be dragged onto the design surface of a form. This generates a ServiceController component in the nonvisual area of the form. By default, the ServiceName property is set to the service that you dragged across from the Server Explorer, but this can be changed to access information and control any service. Similarly, the MachineName property can be changed to connect to any computer to which you have access. The following code shows some of the methods that can be invoked on a ServiceController component:

Private Sub Form6_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
                                                          Handles Me.Load
    Me.pgServiceProperties.SelectedObject = Me.ServiceController1
End Sub

Private Sub btnStopService_Click(ByVal sender As System.Object, _
                                 ByVal e As System.EventArgs) _
                                 Handles btnStopService.Click
    Me.ServiceController1.Refresh()
    If Me.ServiceController1.CanStop Then
        If Me.ServiceController1.Status = _
                 ServiceProcess.ServiceControllerStatus.Running Then
            Me.ServiceController1.Stop()
            Me.ServiceController1.Refresh()
            MessageBox.Show("Service stopped", "Services")
        Else
            MessageBox.Show("This service is not currently running", "Services")
        End If
    Else
        MessageBox.Show("This service cannot be stopped", "Services")

End If
End Sub

Private Sub btnStartService_Click(ByVal sender As System.Object, ByVal e As
   System.EventArgs) Handles btnStartService.Click
    Me.ServiceController1.Refresh()
    If Me.ServiceController1.Status = _
             ServiceProcess.ServiceControllerStatus.Stopped Then
        Me.ServiceController1.Start()
        Me.ServiceController1.Refresh()
        MessageBox.Show("Service started", "Services")
    Else
        MessageBox.Show("This service is not currently stopped", "Services")
    End If
End Sub

In addition to the three main states — running, paused, or stopped — there are additional transition states: ContinuePending, PausePending, StartPending, and StopPending. If you are about to start a service that may be dependent on another service that is in one of these transition states, you can call the WaitForStatus method to ensure that the service will start properly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.254.118