The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,525 other followers

.NET/C# – obtaining information through WMI

Posted by jpluimers on 2009/04/25

WMI (Windows Management Instrumentation) is a way of obtaining information from your PC that otherwise might be hard to find.
WMI is based on Common Information Model (CIM), so you’ll see an example of that too.

There is one drawback: for a lot of the WMI, you need to have enough privileges (like: being an admin, which none of you should be, right?).
So beware!

You can use WMI from C#, but you have to generate the C# classes for the WMI classes first.

I’ll show some examples for WMI Win32 classes, as I needed some of those classes recently myself.

Before I forget:
in order to browse through the WMI Win32 object instances, you can download this nifty WMI Administrative Tools toolset from the Microsoft MSDN site. Note that these are from 2002, and they only reliably work from within Internet Explorer.

The basic process is this:

  1. Start with creating the C# project where you want to have your classes in.
  2. Then generate the C# classes.
  3. Add these C# classes to the C# project.
  4. Make the C# compile by adding a reference to the System.Management.dll assembly
  5. Create some wrapper methods to make the acces to the WMI object instances easier
  6. Use WMI in your programs

So lets go into the C# file generation a bit deeper.

There is a little tool called <code>MgmtClassGen.exe</code> for that.
However, that tool generates your C# files with the name space ROOT.CIMV2 and name them after the WMI class, but for Win32_ prefixed WMI classes, it stripts the Win32_ portion from the filename and to top it up: uses .CS as extension (in stead of .cs).
So… I wrote a little batch-file that generates the C# files the way I want it: using bo.WMI as the base namespace, bo.WMI. as the filename prefix and .cs as the C# filename extension.

@echo off
  if "%1"=="" goto :list
:item
  echo %1 %2
  MgmtClassGen.exe %2 /o bo.WMI.%1 /P bo.WMI.%1.%2.cs
  goto :end
:list
  call %0 CIM CIM_Processor
  call %0 Win32 Win32_Processor
  call %0 Win32 Win32_Processor
  call %0 Win32 Win32_Share
:end

So now we have these files now (note the Win32 and CIM portions are in there twice, but that is just how life goes): 

bo.WMI.CIM.CIM_Processor.cs
bo.WMI.Win32.Win32_Processor.cs
bo.WMI.Win32.Win32_Share.cs

Add a reference to the System.Management.dll assembly (since WMI needs the System.Management namespace).
That assembly is stored in %SystemRoot%\Microsoft.net\Framework\v2.0.50727\System.Management.dll

This is because all the generated C# files

namespace bo.WMI.Win32 {
    using System;
    using System.ComponentModel;
    using System.Management;
    using System.Collections;
    using System.Globalization;

Inconsistent Line Ending warning on generated WMI C# files
And finally you will probably want to fix the line-endings, as the generated C# files do not have consistent line endings. So don’t be surprised if you see a dialog popping up like the image on the right.

WMI is based on classes, that provide access to instances of WMI object.
Since WMI is from the COM era, and now we live in an Object Oriented world (heck, we even undestand generics by now!), writing some wrapper methods like below make your live much easier:

using System;
using System.Collections.Generic;
using bo.WMI.Win32;

namespace bo.WMI.WmiHelpers
{
    public class Win32
    {
        public static List<Share> GetAllShares()
        {
            Share.ShareCollection shares =
                Share.GetInstances();
            return ShareList(shares);
        }
        public static List<Share> ShareList(Share.ShareCollection shares)
        {
            List<Share> result = new List<Share>();
            foreach (Share share in shares)
                result.Add(share);
            return result;
        }
        public static List<Processor> GetAllProcessors()
        {
            Processor.ProcessorCollection Processors =
                Processor.GetInstances();
            return ProcessorList(Processors);
        }
        public static List<Processor> ProcessorList(Processor.ProcessorCollection Processors)
        {
            List<Processor> result = new List<Processor>();
            foreach (Processor Processor in Processors)
                result.Add(Processor);
            return result;
        }
    }
}

Now that we have the wrapper methods, we can finally start making use of the WMI
 

using System;
using System.Collections.Generic;
using bo.WMI.CIM;
using bo.WMI.Win32;
using bo.WMI.WmiHelpers;

namespace bo.WMI.Win32.Test
{
    class Program
    {
        private static void log(string description, string content)
        {
            Console.WriteLine("{0}={1}", description, content);
        }

        static void Main(string[] args)
        {
            {
                Console.WriteLine("CIM.Processors");
                List<bo.WMI.CIM.Processor> Processors = WmiHelpers.CIM.GetAllProcessors();
                foreach (bo.WMI.CIM.Processor Processor in Processors)
                {
                    log("Name", Processor.Name);
                    log("Description", Processor.Description);
                }
            }

            {
                Console.WriteLine("Win32.Processors");
                List<bo.WMI.Win32.Processor> Processors = WmiHelpers.Win32.GetAllProcessors();
                foreach (bo.WMI.Win32.Processor Processor in Processors)
                {
                    log("Name", Processor.Name);
                    log("Description", Processor.Description);
                }
            }

            Console.WriteLine("Shares");
            List<Share> shares = WmiHelpers.Win32.GetAllShares();
            foreach (Share share in shares)
            {
                log("Name", share.Name);
                log("Description", share.Description);
            }
        }
    }
}

And running this, you get a list like this (emphasis added by me):

CIM.Processors
Name=Intel(R) Core(TM)2 CPU          6400  @ 2.13GHz
Description=x86 Family 6 Model 15 Stepping 6
Win32.Processors
Name=Intel(R) Core(TM)2 CPU          6400  @ 2.13GHz
Description=x86 Family 6 Model 15 Stepping 6
Shares
Name=IPC$
Description=Remote IPC
Name=ADMIN$
Description=Remote Admin
Name=C$
Description=Default share

I hope this gives you some inspiration on how to use WMI to your advantage.

–jeroen

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

 
%d bloggers like this: