Friday, October 10, 2008

Using WMI with C#

Windows Management Instrumentation (WMI) is the infrastructure for data management and operations on Windows-based operating systems. It is a set of extensions to the Windows Driver Model that provides an operating system interface through which instrumented components provide information and notification. WMI is Microsoft's implementation of the Web-Based Enterprise Management (WBEM) and Common Information Model (CIM) standards. It is pre-installed in Windows 2000 and newer OSs.

We can write WMI scripts or applications to automate administrative tasks on remote computers but WMI also supplies management data to other parts of the operating system and products. WMI allows scripting languages like VBScript or Windows PowerShell to manage Microsoft Windows personal computers and servers, both locally and remotely.

WMI has its on query language - Windows Query Language, or WQL. WQL allows developers to query WMI providers using a SQL-like syntax. If you know the provider classes and the fields available, then you can get the info very easily.  For instance, if you want to get a list of logical drives from a system you would use the following query:

SELECT * FROM Win32_LogicalDisk

You can, however, refine the search by using where clauses and getting specific "fields" in the query. The following query gets the amount of freespace, the size, and the name of all fixed disk drives:

SELECT FreeSpace,Size,Name FROM Win32_LogicalDisk WHERE DriveType=3

.NET provides very good support to WMI, through the System.Management namespace. It has a number of classes that can be used for accessing WMI services.

ConnectionOptions
ManagementScope
ObjectQuery
ManagementObjectSearcher
ManagementObjectCollection
ManagementObject

Code snippet for collecting drive details (using the previous query) is given below. First of all, we need to add a reference to the System.Management .NET component.

using System.Management;

private void DiskStatisticsUsingWMI()
{
    // Connection credentials for the remote computer
    ConnectionOptions conn = new ConnectionOptions();

    conn.Username = "username";
    conn.Password = "password";

    // The machine to be connected (the root namespace)
    ManagementScope ms = new ManagementScope(@"\\192.168.100.12\root\cimv2", conn);

   // Get Fixed disk stats
    ObjectQuery query = new ObjectQuery("SELECT FreeSpace,Size,Name FROM Win32_LogicalDisk WHERE DriveType=3");

    // Execute the query 
    ManagementObjectSearcher searcher = new ManagementObjectSearcher(ms, query);

    // Get results
    ManagementObjectCollection objectCollection = searcher.Get();

    // Loop through found drives and write out info
    foreach (ManagementObject mgmtObject in objectCollection)
    {
        Console.WriteLine("Name : " + mgmtObject["Name"].ToString());
        Console.WriteLine("FreeSpace: " + mgmtObject["FreeSpace"].ToString());
        Console.WriteLine("Size: " + mgmtObject["Size"].ToString());
    }
}

An important point: if Windows firewall is enabled in the remote system, we need to define an exception for WMI to access that machine from outside. Otherwise, the firewall will block calls to the system. In the machine to be connected, we need to define a policy for allowing remote administration. To do this, launch gpedit.msc from the Start -> Run and move to Computer Configuration -> Administrative Templates -> Network -> Network Connections -> Windows Firewall. Expand Domain Profile is the computer is in a domain, otherwise expand Standard Profile (the machine is in a workgroup). Double click on Windows Firewall: Allow remote administration exception and select Enabled to enable the exception. Check the screenshot given below.

WMI_Policy

Now this machine is ready to accept WMI queries. This should allow the calling application to work. Check the below links, for more troubleshooting details.

http://msdn.microsoft.com/en-us/library/aa389286.aspx
http://support.microsoft.com/kb/875605

No comments: