We will use
WMI to gather information about the local system and then use that
information to create an entry in the Application log. Your service can
also use the data collected to perform actions, such as terminating
processes and rebooting the system, or even on remote systems to start
processes or log events.
You can use WMI and Microsoft Visual Basic 2008
to create self-monitoring services that report information to
administrators, or log events that are being monitored for by other
enterprise applications. You can create these services, or agents,
that run on the local computer, perform tasks, report results, and act
as a very powerful tool. In some cases you can handle these situations
with an out-of-the-box solution. However, sometimes these solutions are
costly and require maintenance, licensing, and external training.
Self-produced solutions can help you create a rich set of operational
tools.
Suppose you want to
monitor a system’s available memory and report an error if that memory
drops below a certain point. You could easily do this with other
existing applications, but the steps taken in reaction by those
applications may not fit with your needs. Some solutions have both
client and server pieces: You can use the server piece to monitor for
SNMP trap messages or NT Events logged by your service and then have it
react to those messages. In some
of these instances, purchasing client or agent licenses may cost extra.
If you could produce this agent yourself, knowing that it is a required
component of your monitoring, you could save your company time and
money. Because the code is yours, you could more easily distribute it or
share and reuse it elsewhere. Documentation and support is also more
readily available with internal resource knowledge of the application.
The great thing about WMI is that it comes with
Windows XP, the Microsoft Windows 2000 server series, the Microsoft
Windows 2003 server series, and other Microsoft operating systems. In
some cases, you must install specific service packs to obtain this, but
it is a default for Windows XP, Windows 2000, and Windows Server 2003.
WMI Architecture
The WMI implementation architecture is fairly
simple in terms of how it is exposed. WMI creates computer- and
application-level classes that provide access to built-in system and
application-defined objects.
To access WMI you have to know the fully
qualified domain name (FDQN) of the object you are attempting to access
in WMI terms. Everything starts at the root node or namespace for a
given object. You first connect to that root node and then use that
interface to connect, add, retrieve, update, and execute methods and
properties that are exposed by classes that exist in the given node or
namespace.
Whether you are using the Management namespace
from .NET or the COM interfaces exposed by Windows and WMI, the usage
and syntax are very similar. The technology used to connect to WMI
doesn’t necessarily create any WMI syntax differences, but it does
create code-specific differences because the objects you are using to
talk to WMI are implemented differently.
Adding the WMI Class Reference
When you use the WMI object model in non-.NET
languages, it is used through COM interfaces and classes. When you use
WMI with .NET, you can use the System.Management namespace. If the
Management namespace is not part of your project you will need to add
its reference in your project. Select Project, select Add Reference, and
then select the System.Management .NET DLL. Now we can create the WMI
class.
Once you have added the System.Management
reference to the project you will need to add an Imports
System.Management declaration to the top of the Tutorials.vb class.
Creating the Generic WMI Class
We are going to use a
generic WMI class to query information about the current processes
running on our system and then record that information into the
Application log. In later sections we will monitor for certain
applications and then take actions based on those applications. Listing 8-1 shows the code required for our WMI generic class.
Listing 1. Generic class with Microsoft WMI support.
Imports System.Management
Imports System.Threading
Public Class WMI
Private m_Error As String = Nothing
Private m_Scope As ManagementScope = Nothing
Private m_Path As ManagementPath = Nothing
Private m_MOC As ManagementObjectCollection = Nothing
Public Sub New()
End Sub
Private Const Processes As String = "Select * from Win32_Process"
Private Const BootInfo As String = "select * from Win32_BootConfiguration"
Private Const LogicalDisk As String = "select * from Win32_LogicalDisk"
Private Const SystemInfo As String = "select * from Win32_ComputerSystem"
Public Enum Management_Query_Type
Processes = 1
LogicalDisks = 2
BootInfo = 3
SystemINfo = 4
End Enum
Public Function Connect(ByVal pszServer As String) As Boolean
Try
If Not m_Path Is Nothing Then
Try
m_Path = Nothing
Catch ex As Exception
m_Error = ex.ToString
End Try
End If
If Not m_Scope Is Nothing Then
Try
m_Scope = Nothing
Catch ex As Exception
m_Error = ex.ToString
End Try
End If
m_Path = New ManagementPath("\\" + pszServer + "\root\cimv2")
m_Scope = New ManagementScope(m_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, _
Optional ByVal iType As Management_Query_Type =
Management_Query_Type.Processes) _
As ManagementObjectCollection
Try
Dim tmpQuery As New ObjectQuery
If Not pszQuery Is Nothing Then
tmpQuery.QueryString = pszQuery
Else
Select Case iType
Case Management_Query_Type.BootInfo
tmpQuery.QueryString = BootINfo
Case Management_Query_Type.LogicalDisks
tmpQuery.QueryString = LogicalDisk
Case Management_Query_Type.Processes
tmpQuery.QueryString = Processes
Case Management_Query_Type.SystemINfo
tmpQuery.QueryString = SystemInfo
Case Else
m_Error = "Invalid Query Type"
Return Nothing
End Select
End If
Dim MOS As ManagementObjectSearcher = New
ManagementObjectSearcher(m_Scope, tmpQuery)
m_MOC = MOS.Get()
tmpQuery = Nothing
Return m_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
End Class
|
The code in Listing 1
will connect to the local or remote server and then run the predefined
query of your choice, returning the result set to the caller. The class
exposes a simple <Connect> method
that will connect to the local or remote WMI RPC services at the root
specified and return an error if it fails. Then the class provides a
simple method called <Query>, which can retrieve information about a query, either passed into the method or from the predefined WMI queries.
You can use WMI for monitoring and reporting in
many ways. We are going to use WMI to look at specific system
information. In our WMI class we don’t have specific methods to process
the data that we retrieve. This is because although we could create a
generic wrapper around all the WMI object and property data collections,
we are only focusing on a small subset of specific information. First
let’s review how WMI works so that you can better understand what you
can do with it.
Understanding WMI Classes and Their Uses
WMI is a set of classes that represent system
information in the form of objects, such as a logical disk, a CD-ROM, or
even your physical memory. This data that WMI retrieves comes from
another interface that WMI inherits from. However, although WMI lists
nearly all of the properties of an object from its base or parent class,
in some cases the data or property is not supported. This means that
you can query for that data, but when you try to access the property
data you get what WMI calls a DbNull. This is similar to what you would
get in a SQL database query when a field is set to NULL.
Specific WMI and Custom Classes
The WMI classes are not just those that come
with the base operating system. Developers can create a WMI interface
for an application and expose those methods and properties. This means
that our generic WMI class can pull data from any WMI-compatible object.
Listing 2 shows the Win32_ComputerSystem class definition.
Listing 2. WMI Win32_ComputerSystem root node implementation.
class Win32_ComputerSystem : CIM_UnitaryComputerSystem
{
uint16 AdminPasswordStatus;
boolean AutomaticResetBootOption;
boolean AutomaticResetCapability;
uint16 BootOptionOnLimit;
uint16 BootOptionOnWatchDog;
boolean BootROMSupported;
string BootupState;
string Caption;
uint16 ChassisBootupState;
string CreationClassName;
sint16 CurrentTimeZone;
boolean DaylightInEffect;
string Description;
string DNSHostName;
string Domain;
uint16 DomainRole;
boolean EnableDaylightSavingsTime;
uint16 FrontPanelResetStatus;
boolean InfraredSupported;
string InitialLoadInfo;
datetime InstallDate;
uint16 KeyboardPasswordStatus;
string LastLoadInfo;
string Manufacturer;
string Model;
string Name;
string NameFormat;
boolean NetworkServerModeEnabled;
uint32 NumberOfProcessors;
uint8 OEMLogoBitmap[];
string OEMStringArray[];
boolean PartOfDomain;
sint64 PauseAfterReset;
uint16 PowerManagementCapabilities[];
boolean PowerManagementSupported;
uint16 PowerOnPasswordStatus;
uint16 PowerState;
uint16 PowerSupplyState;
string PrimaryOwnerContact;
string PrimaryOwnerName;
uint16 ResetCapability;
sint16 ResetCount;
sint16 ResetLimit;
string Roles[];
string Status;
string SupportContactDescription[];
uint16 SystemStartupDelay;
string SystemStartupOptions[];
uint8 SystemStartupSetting;
string SystemType;
uint16 ThermalState;
uint64 TotalPhysicalMemory;
string UserName;
uint16 WakeUpType;
string Workgroup;
};
|
The
great thing about this class is that we can use it to query every
computer on our network and gather information directly from the system,
then use that information to compare it to information that
administrators or the purchasing department has compiled. You could use
this information, such as TotalPhysicalMemory, to verify that the
equipment has what it is supposed to. It will also help to show what
memory the operating system sees compared to how much memory is
installed in the system
Note
It
is possible to modify the boot.ini to tell the Windows operating system
to only use a certain amount of the total memory installed on the
server motherboard. |
We will use WMI to query
Name, Domain, Status, NumberOfProcessors, and TotalPhysicalMemory and
then report this information to the Application log.