Home > Articles > Operating Systems, Server > Microsoft Servers

  • Print
  • + Share This

Implementing WMI

If you're still unclear as to how you could use WMI, press on. As is true of many distributed applications, the example discussed in this article was implemented with server products from multiple vendors; the application also included custom NT services and processes that ran on multiple machines. The end result was a system comprised of twelve NT services and two executable processes running across two NT servers. After the new system was in production, the support team realized that, with all the dependencies, it was going to be difficult to track down problems without a road map that showed the basic information about the core application services. To alleviate the support problem, the development team created a simple COM component in VB that could be called from an ASP page. In order for support personnel to be able to quickly call up the page and determine which components (if any) were having difficulty, the ASP page displays the current status of each of the 14 processes that are required for the system.

A second benefit to using a custom component such as this is that it abstracts the WMI code from the client application, thereby simplifying your client application. In this particular example, the component also allows you to query multiple servers without executing a great deal of WMI code in order to connect to multiple servers.

The WMIProcCheck.Process component developed in VB gathers information about the services and processes to monitor through a simple interface. Then it makes calls to the WMI objects to query for information about each service or process defined in the ASP page. Next the component returns an array or an XML string that contains information about each process, such as its running state, executable path, and security context. In addition, the component contains methods to start and stop the process if it is a service.

To implement the collection of information, the component contains an AddProcess method that takes as arguments the display name for the process, the actual executable file or service name, a flag to determine if the process is a service, and the server name on which to find the process. With each call to this method, the information about the process is saved in a module level multidimensional Variant array.

Public Sub AddProcess(ByVal DisplayName As String, _
    ByVal ProcName As String, ByVal IsService As Boolean, _
    ByVal ServerName As String)

' Add the process to the Variant array

    On Error GoTo RedimErr
    ReDim Preserve mProcesses(6, UBound(mProcesses, 2) + 1)
    
    On Error GoTo 0
    mProcesses(0, UBound(mProcesses, 2)) = DisplayName 'Name to display
    mProcesses(1, UBound(mProcesses, 2)) = ProcName 'Actual process or service name
    mProcesses(2, UBound(mProcesses, 2)) = False 'Running?
    mProcesses(3, UBound(mProcesses, 2)) = IsService  'Service?
    mProcesses(4, UBound(mProcesses, 2)) = vbNullString  'Path or startmode
    mProcesses(5, UBound(mProcesses, 2)) = vbNullString  'Start name
    mProcesses(6, UBound(mProcesses, 2)) = ServerName  'Server name

Exit Sub
RedimErr:
    ReDim mProcesses(6, 0)
    Resume Next
End Sub

Querying WMI for the status of the process is fairly trivial. The component exposes a method called CheckProcesses, which first sorts the array by server name in order to economize on server connections, and then makes a call to a private GetProcs procedure (shown in the following listing).

Check the Processes. The GetProcs private procedure of the component queries for each process or service using WMI and stores the results in the array.

Private Sub GetProcs()

Dim objLocator As SWbemLocator
Dim objEnumerator As SWbemObjectSet
Dim objObject As SWbemObject
Dim objServices As SWbemServices
Dim strUser As String
Dim strDomain As String
Dim i As Integer
Dim strServer As String

    Set objLocator = New SWbemLocator
    
    ' Loop through the processes
    For i = 0 To UBound(mProcesses, 2)
                
        ' Connect to the server using WMI
        If objServices Is Nothing Or _
                mProcesses(6, i) <> strServer Then
            Set objServices = Nothing
            strServer = mProcesses(6, i)
            Set objServices = objLocator.ConnectServer(strServer)
        End If
                
        If mProcesses(3, i) = False Then
            ' Process
            Set objEnumerator = objServices.ExecQuery( _
"Select ExecutablePath From Win32_Process Where Name = '" & _
                mProcesses(1, i) & "'")
            
            ' See if it's there
            If objEnumerator.Count = 0 Then
                mProcesses(2, i) = False
                mProcesses(4, i) = vbNullString
                mProcesses(5, i) = vbNullString
            Else
                For Each objObject In objEnumerator
                    ' Should only be 1
                    mProcesses(2, i) = True
                    mProcesses(4, i) = objObject.ExecutablePath
                    objObject.GetOwner strUser, strDomain
                    mProcesses(5, i) = strDomain & "\" & strUser
                Next
            End If
        Else
            ' Service
            Set objEnumerator = objServices.ExecQuery( _
               "Select State, StartMode, StartName From Win32_Service Where Name = '" & _
                mProcesses(1, i) & "'")
            
            For Each objObject In objEnumerator
                ' Should only be 1
                mProcesses(4, i) = objObject.StartMode
               mProcesses(5, i) = objObject.StartName
                If objObject.state = "Running" Or objObject.state = "Start Pending" Then
                    mProcesses(2, i) = True
                Else
                    mProcesses(2, i) = False
                End If
            Next
            
        End If
    Next
    
' Clean up
Set objLocator = Nothing
Set objEnumerator = Nothing
Set objObject = Nothing
Set objServices = Nothing
End Sub

GetProcs traverses the array of processes and uses the ConnectServer method of the SWbemLocator object to connect to the server each time the server name changes. The ConnectServer method returns an object reference of type SWbemServices that represents the available connection to the CIMOM and exposes the CIM classes within the namespace on the server. The core WMI code used to connect to the namespace is as follows:

Dim objServices As SWbemServices
Dim objLocator As SWbemLocator

Set objLocator = New SWbemLocator
    
If objServices Is Nothing Then
     Set objServices = objLocator.ConnectServer(strServer)
End If

Note that because the ConnectServer call does not contain user name or password information, the security identity of the parent process is used. Once connected to the namespace, you can use the ExecQuery method of the SWbemServices object to pass a WMI Query Language (WQL) string to the server:

Set objEnumerator = objServices.ExecQuery(strWQL)

The query is then parsed, and a collection of SWbemObject object instances is returned in a SWbemObjectSet object. Although ExecQuery is only one of the methods by which to retrieve object instances from WMI, it is a natural interface both for those that are familiar with relational database technology, and in situations such as this where we want to query for a particular set of object instances.

WQL is a SQL-like syntax that contains SELECT, FROM, and WHERE clauses that allow you to specify the properties and classes you would like to retrieve. This component constructs two WQL queries—one to be executed if the process is an executable, and the other to be used if the process is an NT service. For example, to query for an executable process, the component uses the following query:

Select ExecutablePath From Win32_Process Where Name = ?

To query for a service, the component uses the following:

Select State, StartMode, StartName From Win32_Service Where Name = ? 

You'll notice that the properties are specified in the SELECT clause, and that the class name is specified in the FROM clause. With each iteration of the loop, GetProcs executes one of these queries and returns a collection of SWbemObject object instances. The collection can then be iterated using standard For Each syntax to inspect each object instance. In this case, GetProcs calls the dynamic properties of the object to retrieve information about the process, such as its State, StartMode, and StartName (for the Win32_Service class, at least). This information is then placed back in the array to be returned to the client as shown below.

Dim objEnumerator As SWbemObjectSet
Dim objObject As SWbemObject

For Each objObject In objEnumerator
     mProcesses(4, i) = objObject.StartMode
     mProcesses(5, i) = objObject.StartName
     If objObject.State = "Running" Or objObject.State = "Start Pending" Then
          mProcesses(2, i) = True
     Else
          mProcesses(2, i) = False
     End If
Next

You'll notice that although the SWbemObject does not intrinsically support the State, StartMode, and StartName properties (because they are implemented by a specific class), WMI allows you to call them as if they were through automation. After GetProcs has queried for each process, the client can access the updated process information (Variant array) through the Processes method of the component.

  • + Share This
  • 🔖 Save To Your Account

Related Resources

There are currently no related titles. Please check back later.