WMI can be a treasure trove of system information, yet accessing it programmatically is cumbersome and error-prone. Using LINQ can ease these pain points by providing a now-familiar common interface to accessing data with the benefits of strong typing and IntelliSense that we have become accustomed to. Microsoft doesn’t provide such a provider but the open source project Linq to WMI does. As a caveat, please be aware that the provider implementation is limited. Writing a robust IQueryable implementation is not for the faint of heart and the implementation currently could be better. Working within its limitations, the project is still quite usable.
For this article, I have created a simple GUI that makes generating classes from WMI schemas easier. I’ll run through the code generation process using the GUI and then provide examples of how to use this. Using Linq to WMI requires code generation for the schemas that are used. There is a command-line based generator with the project. I have augmented that with a simple WPF-based GUI available as part of the sample code. The form in the GUI maps pretty directly to the command-line arguments. The GUI does allow multiple classes to be generated at one time. Each generated class contains all necessary properties from within this WMI class.
Let me go through a simple example that I worked through in LinqPad. WMI has extensive information about threads and processes available. If I look through the thread class, it has a process handle but not a process name. Finding the process name requires identifying info from the process class.
With Linq to WMI we can perform a join on these tables and gather the complete set of information we want. As a prerequisite, I have set up my LinqPad query by referencing the assemblies with the generated WMI entities and the LinqToWmi.Core assembly. I create a WMI context that refers, in this case with the local machine. Replacing the “.” with a remote computer followed by “\Root\CimV2” will access the remote machine. From there it is just standard linq. I have two from clauses for the two classes Win32_Thread and Win32_Process that we want to query on. The where clause joins based on the actual process ID. Finally, the projection in the select statement covers just the fields that we care about.
WmiContext context = new WmiContext(@"\\.");
var query = from threadStuff in context.Source<Win32_Thread>()
join processStuff in context.Source<Win32_Process>()
on threadStuff.ProcessHandle equals processStuff.Handle
select new
{
threadStuff.Handle,
threadStuff.ProcessHandle,
processStuff.Caption
};
query.Dump();
context.Dispose();
There is a viable workaround to the limitations of the Linq to WMI provider. You can enter a simpler query against the provider, obtaining an IEnumerable<T>. From there, you can execute any Linq to Objects query you want, without worrying about the limitations. Here is an example of what that would look like.
WmiContext context = new WmiContext(@"\\.");
var query = from processStuff in context.Source<Win32_Process>)
select processStuff;
var filteredQuery = from proc in query
group proc by proc.Caption into procGroup
let recordCount = procGroup.Count()
orderby recordCount descending
select new
{
Name = procGroup.Key,
Count = recordCount
};
Context.Dispose();
Here are the primary links for this project