WMI (Windows Management Instrumentation) is a component of the Microsoft operating system that allows you to monitor virtually every piece of the system (either locally or remotely) as well as control the windows operating system. This technology is particularly useful to professionals such as system administrators and database administrators who need to monitor and change client machines on a daily basis.
WMI consists of several pieces that allow it to manage systems. All parts of the operating system are managed through COM by a provider that talks to WMI. WMI also has a repository and a Windows Management service that manages reading and writing information between the provider and the repository. And WMI has this cool query language (WQL) that allows you to query the Windows Management service about different providers. WQL looks a heck of a lot like SQL so it is easy to learn.
If you look at the WMI API you probably will fall off your seat when you see how vast and complicated it seems. Also, it's not obvious from raw COM how to use it. Luckily, Microsoft decided to simplify the process by creating several namespaces and RAD solutions to ease the use of this Goliath (yet powerful) feature. One of the RAD facilities provided by the net is the server explorer WMI extensions. You'll need to
download the server explorer extensions if they are not already installed). These extensions allow you to drag and drop WMI Classes into your .NET project and create wrappers to work with these objects directly. (Believe me, you don't want to create these wrappers yourself). Once the wrapper is created, using these classes is simple and painless.
Reading the Printer Driver through WMI
I thought I would step through a quick example of using the Server Explorer Extensions before showing you some of the cool System.Management namespace classes. Begin by opening the server explorer in Visual Studio.NET and expanding the Management Classes node. Then expand the Printers node and drag the printer into your form. This will generate the wrapper class you will need to query and control the printer.
Figure 1 - Viewing WMI classes in the Server Explorer
To use this class simply construct the wrapper class object and utilize its methods and properties. Below is the C# sample code that shows the printer's name and resolution:
Listing 1 - Querying the printer through WMI
- Printer p = new TestWMI.ROOT.CIMV2.Printer("Epson 880C");
- MessageBox.Show(p.Caption);
- MessageBox.Show(p.HorizontalResolution.ToString());
- MessageBox.Show(p.VerticalResolution.ToString());
.NET also provides some great classes for easing the pain in working with WMI. The System.Management namespace allows you to work with the WMI API while hiding all the complex architectural details. Below is a summary of some of the useful classes in this namespace:
Class Name |
Description |
ManagementBaseObject |
The base Windows Management Object containing the basic elements of a Windows Management Object |
ManagementObject |
Represents a Windows Management Object. Allows you to access properties and methods of a particular WMI Object. This inherits properties of ManagementBaseObject |
ManagementClass |
Represents a Windows Management Class and can be used to access the properties of a particular class. This inherits from ManagementObject. |
ManagementObjectCollection |
Contains a collection of ManagementBaseObjects (which can either be ManagementObjects or ManagementClasses) |
ManagementQuery |
Base class for queries |
ObjectQuery |
query object for querying instances and classes. Inherits Management Query. |
EventQuery |
Query object for querying events in WMI. Used with event watchers. |
ManagementScope |
Describes the scope of which management operations are performed. Generally, the scope is a WMI namespace such as root\cimv2 |
ManagementEventWatcher |
Used to monitor events coming through the WMI service. This object can be constructed using a certain ManagementScope and filtered on an EventQuery. |
ManagementObjectSearcher |
Used to retrieve a collection of ManagementObjects based on a particular query. |
Monitoring the Event Log
Using the WMI classes, we can now monitor the event log. The strategy we will take to monitor the event log will be to construct a ManagementEventWatcher and subscribe to an event that queries whenever a particular event log file changes. In order to monitor the event log remotely, we will specify the WMI scope on a remote machine on our network.
Listing 2 is the class we will use to monitor the event log. This class contains a ManagementEventWatcher to do the monitoring. The ManagementEventWatcher is constructed with a ManagementScope object to allow us to connect to the WMI repository on another machine. It also is constructed with an EventQuery object that specifies the Win32_NTLogEvent class in order to look for changes in the log file. The EventQuery is passed a WQL string that filters only events occurring in the "CaptureLog" file in our Event Log. The event watcher subscribes to the eventlog event and handles the event in an eventhandler delegate.
Listing 2 - Class to watch events on the remote event log
- namespace WMIRemoteEventLogDetection {
-
-
-
- [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name = "FullTrust")]
- [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
- public class WMIEventLogWatcher {
- EventLogHandler _handler = null;
- System.Management.ManagementEventWatcher _watcher = null;
- public MyWMIWatcher() {
-
-
- ConnectionOptions oConn = new ConnectionOptions();
- oConn.Username = "sa";
- oConn.Password = "hockeypuck";
-
- System.Management.ManagementScope oMs = new System.Management.ManagementScope(@\\ RemoteMachine\ root\ cimv2);
-
- oMs.Connect();
-
- _watcher = new ManagementEventWatcher(oMs, new EventQuery("SELECT * FROM __InstanceCreationEvent WHERE TargetInstance ISA 'Win32_NTLogEvent' and TargetInstance.LogFile = 'CaptureLog'"));
-
- _handler = new EventLogHandler();
- _watcher.EventArrived += new EventArrivedEventHandler(_handler.Arrived);
- }
- public void Start() {
-
- _watcher.Start();
- Console.WriteLine("Started...");
- }
- public class EventLogHandler {
- public void Arrived(object sender, EventArrivedEventArgs e) {
- Console.WriteLine("Event Log Changed = ");
-
- PropertyData pd;
- if ((pd = e.NewEvent.Properties["TargetInstance"]) != null) {
- ManagementBaseObject mbo = pd.Value as ManagementBaseObject;
- if (mbo.Properties["Message"].Value != null) {
- Console.WriteLine(mbo.Properties["Message"].Value);
- }
- }
- }
- }
- }
- }
In order to run the class shown above used to monitor the event logger, simply construct the class and call Start. When the event log changes in the CaptureLog file, the event handler Arrived will be entered. You can access the ManagementBaseObject for the event logger on the remote machine through the event argument in the event handler. This object allows you to pull information out of the event log entry such as Message or EventCode. Below is the complete Log Event class that shows you all the properties you can access from the ManagementBaseObject.
Listing 3 - Log Event Class and its properties
- class Win32_NTLogEvent {
- uint16 Category;
- string CategoryString;
- string ComputerName;
- uint8 Data[];
- uint16 EventCode;
- uint32 EventIdentifier;
- uint8 EventType;
- string InsertionStrings[];
- string Logfile;
- string Message;
- uint32 RecordNumber;
- string SourceName;
- datetime TimeGenerated;
- datetime TimeWritten;
- string Type;
- string User;
- };
You can get documentation for all the classes and properties in WMI from
MSDN. This is a useful reference, because otherwise, short of generating the classes from the server explorer extensions, it's difficult to know how to access information inside the Management Objects.
In order to access WMI on a remote machine, you'll need to alter the WMI security on the remote computer and restart the WMI service. This can all be accomplished through the Computer Management Console in the control panel shown in Figure 2. Right-clicking on the WMI Control of the Services and Application section allows you to access the WMI Properties.
Figure 2- Computer Management Console in the System Administration Tools of the Control Panel
This will bring up the WMI Control Properties dialog shown in Figure 3. This dialog will allow you to change the security on the particular WMI node you are trying to access. In this case, we want to change the security on the CIMV2 node to enable remoting.
Figure 3- WMI Control Properties Dialog (Security Tab)
Clicking the security button opens a dialog that allows us to change security on the computer to enable remoting as shown in figure 4. We will give remoting access to everyone in this case.
Figure 4 - Allow Everyone Remoting Access to WMI
Now we simply restart the WMI service in the computer management console as shown in figure 5 and then we are ready to run our event log event trapper.
Figure 5 - Restarting the WMI Service
The event log trapper is initially started through a button in our form that simply calls start and sits and watches for events triggered by the query. If an event occurs, the event handler in the MyHandler class writes out the message to the console associated with the event written in the event log.
Listing 4 - Starting the EventLog Watcher with a button click
- MyWMIWatcher _watcher = new MyWMIWatcher();
- private void button1_Click(object sender, System.EventArgs e)
- {
- _watcher.Start();
- }
Conclusion
WMI is a powerful interface that can be used to monitor event logs, check disk space, or monitor peripherals. Through .NET, implementing WMI becomes a lot easier through Server Explorer Extensions and the System.Management and System.Management.Instrumentation interfaces. If you use WMI remotely, you may need to enable some security settings in the WMI properties window using the Computer Management Console. Have fun experimenting with WMI, another instrument at your disposal in .NET.