ENTERPRISE

Programming Windows Services with Microsoft Visual Basic 2008 : Extending the WMI Implementation

2/18/2013 6:20:31 PM

The current WMI implementation isn’t a very scalable solution, so we’ll implement the same strategies that we used for file monitoring.

Extending the WMI Class

We need to create a more robust and scalable WMI class that allows for multiple instances of monitored servers. In addition, we want to be able to predefine a query that we can use to retrieve specific information.

Because of the extensive nature of changes to the WMI class, I will reprint the code in Listing 1 and then review the changes.

Listing 1. Extended WMI class.
Public Class WMI
  Private m_Error As String = Nothing
  Private m_Scope As ManagementScope = Nothing
  Private m_Path As ManagementPath = Nothing
  Private m_Outgoing As Thread = Nothing
  Private m_ThreadAction As ThreadActionState
  Private m_WMIWorkerOptions As New WMIWorkerOptions

  Public Sub New(ByRef threadaction As ThreadActionState,
                         ByVal wwo As WMIWorkerOptions)
      m_ThreadAction = threadaction
      WMIWorkerOptions.Copy(m_WMIWorkerOptions, wwo)
      m_Path = New ManagementPath
      m_Scope = New ManagementScope
  End Sub

  Public Function Connect(ByVal pszServer As String,
                  ByVal pszRoot As String) As Boolean
      Try
          If Not m_Scope Is Nothing Then
              Try
                  If m_Scope.IsConnected Then
                      Return True
                End If
            Catch ex As Exception
                m_Error = ex.ToString
            End Try
        End If
        m_Path.Path = "\\" + pszServer + pszRoot
        m_Scope.Path = Me.Path
        m_Scope.Connect()
        Return True
    Catch ex As Exception
        m_Error = ex.ToString
        Return False
    End Try
End Function

Public Function Query( _
                    ByVal pszQuery As String) _
                    As ManagementObjectCollection
    Try
        If (String.IsNullOrEmpty(pszQuery)) Then
            Throw New ArgumentException("pszQuery")
        End If
        Dim tmpQuery As New ObjectQuery
        tmpQuery.QueryString = pszQuery
        Dim MOS As ManagementObjectSearcher = New
      ManagementObjectSearcher(Me.Scope, tmpQuery)
        Dim MOC As ManagementObjectCollection = MOS.Get()
        tmpQuery = Nothing
        MOS.Dispose()
        Return MOC
    Catch ex As Exception
        m_Error = ex.ToString
        Return Nothing
    End Try
End Function

Public Function GetError() As String
    Try
        Return m_Error
    Catch ex As Exception
        Return ex.ToString
    End Try
End Function

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 Domain As String = Nothing
                      Dim Status As String = Nothing
                      Dim NumberOfProcessors As String = Nothing
                      Dim TotalPhysicalMemory As String = Nothing
                      Dim pMO As ManagementObject
                      For Each pMO In pMOC
                          Try
                              ReadProperty(pMO, "Name", Name)
                              ReadProperty(pMO, "Domain", Domain)
                              ReadProperty(pMO, "Status", Status)
                              ReadProperty(pMO, "NumberOfProcessors",
                                NumberOfProcessors)
                                ReadProperty(pMO, "TotalPhysicalMemory",
                                TotalPhysicalMemory)
                          Catch ex As Exception
                              Exit For
                          End Try
                          'Lets Log This Information
                          Dim pszOut As String
                          pszOut = "Name: " + Name.Trim
                          pszOut += ",Domain: " + Domain.Trim
                          pszOut += ",Status: " + Status.Trim
                          pszOut += ",NumberOfProcessors: " +
                          NumberOfProcessors.Trim
                          pszOut += ",TotalPhysicalMemory: " +
                            TotalPhysicalMemory.Trim + vbCrLf
                          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

  Private Sub ReadProperty(ByRef pObject As ManagementObject, ByRef pProp As
String, ByRef pszStr As String)
      Try
          pszStr = pObject.Properties(pProp).Value.ToString
      Catch ex As Exception
          pszStr = ""
      End Try
  End Sub

  Public ReadOnly Property Outgoing() As Thread
      Get
          Return m_Outgoing
      End Get
  End Property

  Public Sub Start()
      m_Outgoing = New Thread(AddressOf ProcessWMIRequest)
      m_Outgoing.Priority = ThreadPriority.Normal
      m_Outgoing.IsBackground = True
      m_Outgoing.Name = m_WMIWorkerOptions.Server + "-" +
                     m_WMIWorkerOptions.Query
      m_Outgoing.Start()
  End Sub

  Private Shared Sub WriteLogEvent(ByVal pszMessage As String, ByVal dwID As
     Long, ByVal iType As EventLogEntryType, ByVal pszSource As String)
      Try
          Dim eLog As EventLog = New EventLog("Application")
          eLog.Source = pszSource
          Dim eInstance As EventInstance = New EventInstance(dwID, 0, iType)
          Dim strArray() As String
          ReDim strArray(1)
          strArray(0) = pszMessage
          eLog.WriteEvent(eInstance, strArray)
          eLog.Dispose()
      Catch ex As Exception
          'Do not Catch here as it doesn't do any good for now
      End Try
  End Sub

  Public ReadOnly Property Scope() As ManagementScope
      Get
          Return m_Scope
      End Get
  End Property

  Public ReadOnly Property Path() As ManagementPath
      Get
          Return m_Path
      End Get
  End Property
End Class

					  

New WMI Member Variables

As shown in Listing 2, we are going to add several new WMI member variables, which will hold the pointers to our internal WMI implementation, and our new thread-state management class instance.

Listing 2. WMI class member variables.
Private m_Error As String = Nothing
Private m_Scope As ManagementScope = Nothing
Private m_Path As ManagementPath = Nothing
Private m_Outgoing As Thread = Nothing
Private m_ThreadAction As ThreadActionState
Private m_WMIWorkerOptions As New WMIWorkerOptions

As you can see, we have only one thread per WMI instance because no e-mail or incoming file processing is required.

New and Updated WMI Class Member Methods

The new WMI class implementation has several properties that are used to relate to the WMI Management namespace in Microsoft Visual Studio. The following list includes these changes to the WMI class and a brief description of how they correlate to their WMI representations.

The <New> Constructor

Because we are now using thread state management, I have updated the <New> constructor so that, we can pass in a shared instance of the thread state management object, as well as the newly created WMIWorkerOptions class.

The WMIWorkerOptions Class

Note that the WMIWorkerOptions class is very close in design and detail to the FileWorkerOptions class. It is used for the same purpose—to store WMI specific configuration information on a per-class instance basis.

The WMI Properties

As with the FileWorker class, we need to store certain pieces of information outside the scope of our WMIWorkerOptions class, so we will implement it directly in our WMI class.

  • The WMI Path The WMI path represents the physical and logical connection to the local or remote computer where the WMI classes that you want to work with are implemented. ManagementPath is given a string to represent the FQDN of the root node and object you want to query. In our class, we defined this as the property Path.

  • The WMI Scope The WMI scope represents the connection to the root node as defined in the ManagementPath variable associated with the scope. This is where you will implement the underlying details of the connection to the computer and therefore to the WMI objects you want to interface with. In our class, we define this as the property Scope.

  • The <ProcessWMIRequest> Method The <ProcessWMIRequest> is like the <ProcessFiles> method in our FileWorker class. It defines the entry point for the thread created on a per-WMI instance basis. This is where we connect to the local or remote WMI services, query the data we are looking for, and then process the data.

Creating the WMIWorkerOptions Class

We need to be able to pass into our new WMI class the properties that we will define in our configuration file, which we will discuss in the next section. For this I have created the class shown in Listing 3.

Listing 3. WMIWorkerOptions class.
Public Class WMIWorkerOptions
  Private m_WMIRoot As String
  Private m_Query As String
  Private m_Server As String

  Public Property Server() As String
      Get
          Return m_Server
      End Get
      Set(ByVal value As String)
          m_Server = value
      End Set
  End Property

  Public Property Query() As String
      Get
          Return m_Query
      End Get
      Set(ByVal value As String)
          m_Query = value
      End Set
  End Property

  Public Property WMIRoot() As String
      Get
          Return m_WMIRoot
      End Get
      Set(ByVal value As String)
          m_WMIRoot = value
      End Set
  End Property

  Public Shared Sub Copy(ByVal wwo1 As WMIWorkerOptions,
                         ByVal wwo2 As WMIWorkerOptions)
      wwo1.Query = wwo2.Query
      wwo1.Server = wwo2.Server
      wwo1.WMIRoot = wwo2.WMIRoot
  End Sub
End Class

					  

The Query Property

The Query property represents the WMI query that will be passed to this instance from our configuration file. The query may differ slightly based on the operating system.

The WMIRoot Property

The WMIRoot property represents the root node of the local or remote WMI class instance that you want to query. From here you can query or use other subordinate classes or methods.

The Server Property

The Server property represents the server that we want to connect to. It can also represent the local computer by either using the local computer name or by using a period, which also represents the local computer.

Creating the Configuration File

We will create a configuration file that we can use to identify the preceding properties to our WMI class instances. Listing 4 shows the XML file that we are using. 

Listing 4. WMI configuration XML file.
<?xml version="1.0" encoding="utf-8" ?>
<Configuration>
<WMIWorkerOptions>
  <WMIWorkerProperties>
    <Query>select * from Win32_ComputerSystem</Query>
    <WMIRoot>\root\cimv2</WMIRoot>
    <Server>mgern-D820</Server>
  </WMIWorkerProperties>
  <WMIWorkerProperties>
    <Query>select * from Win32_ComputerSystem</Query>
    <WMIRoot>\root\cimv2</WMIRoot>
    <Server>TestServer</Server>
    </WMIWorkerProperties>
  </WMIWorkerOptions>
</Configuration>

The configuration file shown previously in Listing 3 contains the matching properties from our WMIWorkerOptions class. You will notice that I have included the WMIWorkerOptions section twice. This is to test that we can get multiple threads to perform the work. Future implementations of the service can either point to different servers or use different queries to customize the service usage.

WMI Service Account

WMI communicates over RPC to the local and remote servers. You need to ensure that on the computer you want to communicate with, the service or the threads using WMI are running in the context of a user with the proper privileges.

After installing the service and implementing your own configuration file, you will see at least one Application log entry for each entry in the configuration file. The intent here is to show that you can use WMI to query both local and remote information.

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