ENTERPRISE

Programming Windows Services with Microsoft Visual Basic 2008 : WMI System Monitoring

2/18/2013 6:21:49 PM
Although WMI is great for gathering information, it can also be a very powerful monitoring and debugging tool. In this section we will use WMI to monitor the processes on our system and then use WMI to shut down an application that we define as behaving poorly. Because we don’t want to negatively affect our system at the moment, we will simulate this sitation by using NotePad.exe.

Updating the Configuration File

We need to be able to store the process or processes that we want to monitor for. Listing 1 shows the updated configuration file with the process element added.

Listing 1. Process monitoring elements.
<Process>Notepad.exe</Process>

We can implement the process element in several ways, depending on the complexity that we want to create. You can choose from the following options:

  • Create a master <Processes> element with subelements called <Process>. You’ll need to modify the <ThreadFunc> method of your Tutorials class so that it can parse all the processes that have been entered. Then you’ll have to update the WMIWorkerOptions class to store an array of process names.

  • Create a single element (as I’ve done in the previous example). I called my element process, and then placed the specific process name that I want to monitor.

  • Create a single element but place a delimited list of processes to monitor.

Any of these options will work, though some require more work than others. For demonstration purposes, I have taken what I consider the simplest approach and created a single element with a single process name, Notepad.

WMI Win32_Process Usage

In this section we will use the Win32_Process class (shown in Listing 2) from WMI to monitor for any application named Notepad.exe. When we find an instance of Notepad, we will use WMI to shut it down.

Listing 2. Win32_Process WMI class implemented in the .NET Framework in C#.
class Win32_Process : CIM_Process
{
  string Caption;
  string CommandLine;
  string CreationClassName;
  datetime CreationDate;
  string CSCreationClassName;
  string CSName;
  string Description;
  string ExecutablePath;
  uint16 ExecutionState;
  string Handle;
  uint32 HandleCount;
  datetime InstallDate;
  uint64 KernelModeTime;
  uint32 MaximumWorkingSetSize;
  uint32 MinimumWorkingSetSize;
  string Name;
  string OSCreationClassName;
  string OSName;
  uint64 OtherOperationCount;
  uint64 OtherTransferCount;
  uint32 PageFaults;
  uint32 PageFileUsage;
  uint32 ParentProcessId;
  uint32 PeakPageFileUsage;
  uint64 PeakVirtualSize;
  uint32 PeakWorkingSetSize;
  uint32 Priority;
  uint64 PrivatePageCount;
  uint32 ProcessId;
  uint32 QuotaNonPagedPoolUsage;
  uint32 QuotaPagedPoolUsage;
  uint32 QuotaPeakNonPagedPoolUsage;
  uint32 QuotaPeakPagedPoolUsage;
  uint64 ReadOperationCount;
  uint64 ReadTransferCount;
  uint32 SessionId;
  string Status;
  datetime TerminationDate;
  uint32 ThreadCount;
  uint64 UserModeTime;
  uint64 VirtualSize;
  string WindowsVersion;
  uint64 WorkingSetSize;
  uint64 WriteOperationCount;
  uint64 WriteTransferCount;
}

					  

We will not use all of the class properties available to us. System administrators can use many things to monitor servers based on process performance: threads, handles, memory, virtual memory, CPU usage, and more. A well-written service can monitor a nearly unlimited number of processes per computer while tracking usage over time so that you can determine whether an application is causing a problem.

Although we will gather several pieces of information, we will only use the Name property to determine whether we should shut down the application. This demonstration is not intended to teach you how to design a monitor, but instead to give you an idea of what you can do within a service. When we find an application that matches our <Processes> element, we will shut down the application. We need to make several changes to support this functionality.

Updating the WMIWorkerOptions Class

We need to update the WMIWorkerOptions to store the new process element value that we created. Listing 3 shows the code required to store this information.

Listing 3. WMIWorkerOptions class process update.
Private m_Process As String

Public Property Process() As String
  Get
    Return m_Process
  End Get
  Set(ByVal value As String)
    m_Process = value
  End Set
End Property

I added a Process property that we will use to determine the process that we want to monitor for.

Updating the <Tutorials.ThreadFunc> Method

We need to read in the new value from our configuration.xml file and use our WMIWorkerOptions class to store it. We will update the <ThreadFunc> method, as shown in Listing 4.

Listing 4. <ThreadFunc> method update to read the process element.
Do
  Try
    children = tmpOptions.SelectSingleNode("Query")
    WWOptions.Query = children.Value

    children = tmpOptions.SelectSingleNode("Server")
    WWOptions.Server = children.Value

    children = tmpOptions.SelectSingleNode("WMIRoot")
    WWOptions.WMIRoot = children.Value

    children = tmpOptions.SelectSingleNode("Process")
							WWOptions.Process = children.Value

    Dim tmpWW As New WMI(m_ThreadAction, WWOptions)
    m_WorkerThreads.Add(tmpWW)
    tmpWW.Start()
  Catch ex As Exception
    WriteLogEvent(ex.ToString(), CONFIG_READ_ERROR, EventLogEntryType.Error,
         My.Resources.Source)
  End Try
Loop While (tmpOptions.MoveToNext)

					  

The bolded code in Listing 4 will now use the new process property to store the name of the process—in our case, Notepad.exe—that we want to monitor for. Now that we have our process name we need to update our worker threads to query the processes and then shut down any Notepad.exe instances.

Updating the <Query> Configuration Value

Aside from adding in the appropriate process element, we also need to update our <Query> element to reflect the new dynamic WMI query. Listing 5 shows the update.

Listing 5. New WMI <Query> element value.
<Query>Select * from Win32_Process Where Name = 'Notepad.exe'</Query>

The new <Query> element value will allow us to simplify the resources required to perform the work that we want to do. Because we only care about the Win32_Process class—and more specifically, the Notepad.exe process—we will use the following query only to validate against our desired process. If you want to use this same query for another process, just change the process name from Notepad.exe to whatever you want to search for.

Updating the <WMI.ProcessWMIRequest> Method

We need to modify the <ProcessWMIRequest> method to reflect our required change to shut down any process called Notepad.exe. Listing 6 shows the modifications.

Listing 6. Updated <ProcessWMIRequest> method.
Private Sub ProcessWMIRequest()
  While Not m_ThreadAction.StopThread
    If Not m_ThreadAction.Pause Then
        Try
            'Now process the file
            Connect(m_WMIWorkerOptions.Server, m_WMIWorkerOptions.WMIRoot)
            Dim pMOC As ManagementObjectCollection
            pMOC = Query(m_WMIWorkerOptions.Query)
            If Not pMOC Is Nothing Then
                Dim Name As String = Nothing
                Dim ProcessId As String = Nothing
                Dim pMO As ManagementObject
                For Each pMO In pMOC
                    Try
							ReadProperty(pMO, "Name", Name)
							ReadProperty(pMO, "ProcessId", ProcessId)
							Catch ex As Exception
							Exit For
							End Try
							'Lets Log This Information
							Dim pszOut As String
							'terminate the process
							If Name.Trim = "Notepad.exe" Then
							pMO.InvokeMethod("Terminate", Nothing)
							End If
							pszOut = "Shut down of process Name: " + Name.Trim + vbCrLf
							+ "Process ID: " + ProcessId
                    WriteLogEvent(pszOut, WMI_INFO,
        EventLogEntryType.Information, "Tutorials")
                    pMO.Dispose()
                Next
            Else
                WriteLogEvent("Thread WMI query Error - " + GetError(),
        WMI_ERROR, EventLogEntryType.Error, "Tutorials")
            End If
            pMOC.Dispose()
        Catch tab As ThreadAbortException
            'Clean up the thread here
            WriteLogEvent("Thread Function Abort Error - " + Now.ToString,
         WMI_ERROR, EventLogEntryType.Error, "Tutorials")
        Catch ex As Exception
            WriteLogEvent("Thread Function Error - " + Now.ToString, WMI_ERROR,
         EventLogEntryType.Error, "Tutorials")
        End Try
    End If

    If Not m_ThreadAction.StopThread Then
        Thread.Sleep(THREAD_WAIT)
    End If
  End While
End Sub

					  

The bolded code shows that we have modified the code to read the Name and ProcessId properties. In reality, because we only queried processes named Notepad.exe, we don’t really need to read the Name property, but it is always better to be safe and validate a process before you terminate it.

After we validate that we are looking at an instance of our queried process, Notepad.exe, we invoke the Terminate method of the WMI Win32_Process class to shut down the process. The last thing we do is write an event into the Application log to show that we accomplished our goal.

Service Function Validation

To ensure that the service is working properly, configure and install the service to monitor for required processes. For each process, add a new entry in the configuration file. Even if the process is on the same computer, you must add one entry per process. For each process that your service finds, you should see the process disappear and then an event appear in the Application log.

You will notice that if a process requires user interaction, such as saving the current file in Notepad, you may be requested to do so before the process shuts down.

Other  
 
Top 10
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
3 Tips for Maintaining Your Cell Phone Battery (part 1) - Charge Smart
OPEL MERIVA : Making a grand entrance
FORD MONDEO 2.0 ECOBOOST : Modern Mondeo
BMW 650i COUPE : Sexy retooling of BMW's 6-series
BMW 120d; M135i - Finely tuned
PHP Tutorials : Storing Images in MySQL with PHP (part 2) - Creating the HTML, Inserting the Image into MySQL
PHP Tutorials : Storing Images in MySQL with PHP (part 1) - Why store binary files in MySQL using PHP?
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS