Mon, Dec 12 2005 11:32 AM
Emile Bosch
Query your WMI with ease using WMILinq!
When I first played with Linq I didnt move away from my computer for two days, neglecting my friends and family, while gazing at Linqs tremendous power to take over the world. I was particulary interrested in its sexy feature to convert an Linq expression into an so called expression tree, as you can experience with DLinq. As you probably already know, when you write an linq query the default compiler behaviour converts the query to an delegate. But this behaviour radically changes when you assign your query to an Expression<T> class. In this way the compiler doesn’t convert your query into an delegate but into an expression tree. It is then up to you on how to process this tree in something useful and come up with the right output. There is an excellent article on this behaviour from Ian Griffiths you can find its link below.
Making life easier with WmiLinq
To show the use and power of expression trees I wrote a basic WMILinq library which makes it possible to query directly against WMI store using an LINQ like syntax. Internally it works somewhat similar as Dlinq, it translates an LINQ query to an expression tree and feeds it to an Query Builder. This analyzes the expression tree and generates an WMI WQL compatible query, which is fed to the WMI library of .NET. When this query is executed the results are mapped back using reflection into entities. I kept it all a bit simple by only supporting the basic WHERE clauses, thus joins are not supported. And for clarity i havent included any query validation, optimization or whatsoever.
Its all pretty beta and purely intended as an expression tree sample and educational project. But it sure is interesting to see what is possible with expression trees with such a limited implementation. If microsoft isnt building the same thing right now i might continue developing it though.
Lets say we want to query some process information. For instance, query the processes which have more than 20 kernel threads:
using (WmiContext context = new WmiContext(@"\\localhost"))
{
var query = from process in context.Source<Win32_Process>() where
process.ThreadCount > 20 && process.Name.Contains(".exe") select process;
foreach (Win32_Process process in query)
{
Console.WriteLine(process.Name + process.CreationDate);
}
//The above results in the following WQL Query:
//SELECT * FROM Win32_Process WHERE ThreadCount > 20
}
Or start doing things which make totally no sense but to show off our cross linq languages.
For example: Query the desktops and save the result in xml.
using (WmiContext context = new WmiContext(@"\\localhost"))
{
var doc = new XDocument(
new XElement("Desktops", (from desktop in context.Source<Win32_Desktop>()
where desktop.Name.Contains("service") select
new XElement("Desktop", new XAttribute("name", desktop.Name)))));
//The above rusults in the folllowing WQL Query:
//SELECT * FROM Win32_Desktop WHERE Name Like ‘%service%’
}
As you can see its easy to query every piece of information that is available in WMI. Hardware information, network information, event logs, performance counters, installed software and so on. I never had much field experience on WMI, and i am not sure how long WMI is going to last, but WMI seemed like an easy enough query language to create an sample for.
Create your prototype entities with WmiClassGen
As you can see in the examples, im querying the WMI store using the prototype entities like Win32_Process and Win32_Desktop , these are used for specifying the WHERE clause and for mapping the flat data back into entities. Which are somewhat similar as the table classes you use with Dlinq. Al this neatness isnt much worth if you have to write the WMI entities yourself. Its not much fun, error prone and way too much work for us lazy developers. Therefor I made little tool which generates the WMI entity for you using codedom. The tool does the same thing for WMI as SQLMetal does for databases. Creating an prototype class is as easy like this.
> WmiClassGen.exe /wmi:Win32_UserAccount /out:”c:\Win32_UserAccount.cs”
The first switch here tells the tool to create a prototype class for the Win32_UserAccount object. The out switch tells the tool where to save the result. There are a couple more switches
which you can easily query by passing no parameters at all.
And yes, I made the same mistake as Dlinq I used attributes for mapping :) If I can find the time the next version of WMILinq it will have support for namespaces, WMI events, method invocations, associations and probably XML based mapping.
Remember you'll need VS2005 RTM and Linq RTM.
Some related reading material on this subject
The folder documentation contains a (dutch) walktrough. I will write an english one tonight.
Filed under: C# 3.0