Monitoring Remote Log Remotely using WMI in C# and .NET

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.
 

.NET and WMI

 
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.
 
WMIEventDetecting1.jpg
 
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
  1. Printer p = new TestWMI.ROOT.CIMV2.Printer("Epson 880C");  
  2. MessageBox.Show(p.Caption);  
  3. MessageBox.Show(p.HorizontalResolution.ToString());  
  4. MessageBox.Show(p.VerticalResolution.ToString());  

.NET WMI Classes

 
.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
  1. namespace WMIRemoteEventLogDetection {  
  2.     /// <summary>  
  3.     /// Summary description for MyWMIWatcher.  
  4.     /// </summary>///  
  5.     [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name = "FullTrust")]  
  6.     [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]  
  7.     public class WMIEventLogWatcher {  
  8.         EventLogHandler _handler = null;  
  9.         System.Management.ManagementEventWatcher _watcher = null;  
  10.         public MyWMIWatcher() {  
  11.             // Although connection information is not used in this example, I'll show you how to construct it here  
  12.             //Connection credentials to the remote computer - not needed if the logged in account has access  
  13.             ConnectionOptions oConn = new ConnectionOptions();  
  14.             oConn.Username = "sa";  
  15.             oConn.Password = "hockeypuck";  
  16.             // Connect to the remote machine's WMI repository  
  17.             System.Management.ManagementScope oMs = new System.Management.ManagementScope(@\\ RemoteMachine\ root\ cimv2);  
  18.             // connect it  
  19.             oMs.Connect();  
  20.             // construct the watcher object from the scope and the query  
  21.             _watcher = new ManagementEventWatcher(oMs, new EventQuery("SELECT * FROM __InstanceCreationEvent WHERE TargetInstance ISA 'Win32_NTLogEvent' and TargetInstance.LogFile = 'CaptureLog'"));  
  22.             // connect an event handler to the event returned when the event log changed  
  23.             _handler = new EventLogHandler();  
  24.             _watcher.EventArrived += new EventArrivedEventHandler(_handler.Arrived);  
  25.         }  
  26.         public void Start() {  
  27.             //Start watching for events  
  28.             _watcher.Start();  
  29.             Console.WriteLine("Started...");  
  30.         }  
  31.         public class EventLogHandler {  
  32.             public void Arrived(object sender, EventArrivedEventArgs e) {  
  33.                 Console.WriteLine("Event Log Changed = ");  
  34.                 // Get the Event object and display it  
  35.                 PropertyData pd;  
  36.                 if ((pd = e.NewEvent.Properties["TargetInstance"]) != null) {  
  37.                     ManagementBaseObject mbo = pd.Value as ManagementBaseObject;  
  38.                     if (mbo.Properties["Message"].Value != null) {  
  39.                         Console.WriteLine(mbo.Properties["Message"].Value);  
  40.                     }  
  41.                 }  
  42.             }  
  43.         }  
  44.     }  
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
  1. class Win32_NTLogEvent {  
  2.     uint16 Category;  
  3.     string CategoryString;  
  4.     string ComputerName;  
  5.     uint8 Data[];  
  6.     uint16 EventCode;  
  7.     uint32 EventIdentifier;  
  8.     uint8 EventType;  
  9.     string InsertionStrings[];  
  10.     string Logfile;  
  11.     string Message;  
  12.     uint32 RecordNumber;  
  13.     string SourceName;  
  14.     datetime TimeGenerated;  
  15.     datetime TimeWritten;  
  16.     string Type;  
  17.     string User;  
  18. }; 
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.
 

Security

 
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.
 
WMIEventDetecting2.jpg
 
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.
 
WMIEventDetecting3.jpg
 
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.
 
WMIEventDetecting4.jpg
 
 
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.
 
WMIEventDetecting5.jpg
 
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
  1. MyWMIWatcher _watcher = new MyWMIWatcher();  
  2. private void button1_Click(object sender, System.EventArgs e)  
  3. {  
  4. _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.