A simple and lightweight WMI framework for querying WMI, calling WMI methods, and subscribing to WMI events. It's a subset of the System.Management.Instrumentation namespace.
The System.Management framework has some limitations:
-
Memory leaks on .NET Framework: The .NET Framework version of System.Management leaks a small amount of memory on each remote operation. Use WmiLight if your application is a service or runs a long time and you're sending a lot of remote queries.
-
No Native AOT support: System.Management does not support Native AOT deployment. Use WmiLight if you want to build Native AOT applications that interact with WMI.
This project is being distributed as a NuGet package, so open your Package Manager Console window and execute the following command:
Query all running processes for the local machine:
using (WmiConnection con = new WmiConnection())
{
foreach (WmiObject process in con.CreateQuery("SELECT * FROM Win32_Process"))
{
Console.WriteLine(process["Name"]);
}
}Query all partitions for a remote machine with credentials (UPN format recommended):
var opt = new WmiConnectionOptions() { EnablePackageEncryption = true };
var cred = new NetworkCredential("USER@DOMAIN", "PASSWORD");
using (WmiConnection con = new WmiConnection(@"\\MACHINENAME\root\cimv2", cred, opt))
{
foreach (WmiObject partition in con.CreateQuery("SELECT * FROM Win32_DiskPartition"))
{
Console.WriteLine(partition["Name"]);
}
}Query all partitions for a remote machine with Integrated Windows Authentication:
var opt = new WmiConnectionOptions() { EnablePackageEncryption = true };
using (WmiConnection con = new WmiConnection(@"\\MACHINENAME\root\cimv2", opt))
{
foreach (WmiObject partition in con.CreateQuery("SELECT * FROM Win32_DiskPartition"))
{
Console.WriteLine(partition["Name"]);
}
}Calling a static WMI method:
using (WmiConnection connection = new WmiConnection())
using (WmiMethod createMethod = connection.GetMethod("Win32_Process", "Create"))
using (WmiMethodParameters methodParams = createMethod.CreateInParameters())
{
methodParams.SetPropertyValue("CommandLine", "cmd.exe");
uint result = connection.ExecuteMethod<uint>(createMethod, methodParams, out WmiMethodParameters outParams);
if (result != 0)
throw new Exception($"Win32_Process::Create(...) failed with {result}");
uint processId = outParams.GetPropertyValue<uint>("ProcessId");
// ...
}And the following code shows how to call a non-static WMI method:
using (WmiConnection connection = new WmiConnection())
{
foreach (WmiObject process in connection.CreateQuery("SELECT * FROM Win32_Process"))
{
if (process.GetPropertyValue<string>("Name") == "cmd.exe")
{
using (WmiMethod terminateMethod = process.GetMethod("Terminate"))
using (WmiMethodParameters parameters = terminateMethod.CreateInParameters())
{
parameters.SetPropertyValue("Reason", 20);
uint result = process.ExecuteMethod<uint>(terminateMethod, parameters, out WmiMethodParameters terminateOutParameters2);
if (result != 0)
throw new Exception($"Win32_Process::Terminate(...) failed with {result}");
}
}
}
}Get a notification if a process has started:
var opt = new WmiConnectionOptions() { EnablePackageEncryption = true };
using (WmiConnection connection = new WmiConnection(@"\\MACHINENAME\root\cimv2", opt))
{
using (WmiEventSubscription sub = connection.CreateEventSubscription(
"SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'",
x => Console.WriteLine("Process '{0}' started", x.GetPropertyValue<WmiObject>("TargetInstance").GetPropertyValue<string>("Name"))))
{
// ToDo: wait or do some other stuff
}
}Alternative way to get a notification if a process has started:
var opt = new WmiConnectionOptions() { EnablePackageEncryption = true };
using (WmiConnection connection = new WmiConnection(@"\\MACHINENAME\root\cimv2", opt))
{
using (WmiEventWatcher eventWatcher = connection.CreateEventWatcher("SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'"))
{
eventWatcher.EventArrived += EventWatcher_EventArrived;
eventWatcher.Start();
// ToDo: wait or do some other stuff
eventWatcher.Stop();
eventWatcher.EventArrived -= EventWatcher_EventArrived;
}
}WmiLight supports Native AOT deployment since Version 5.0.0.
It's also possible to link WmiLight statically to have only one executable working without the WmiLight.dll.
To link WmiLight statically, add <PublishWmiLightStaticallyLinked>true</PublishWmiLightStaticallyLinked> to your project file.
<PropertyGroup>
<PublishAot>true</PublishAot>
<PublishWmiLightStaticallyLinked>true</PublishWmiLightStaticallyLinked>
</PropertyGroup>When building a Native AOT application that encapsulates your WMI code in a .NET Standard library, the trimmer may remove essential code from WmiLight. In this scenario, the trimmer cannot detect that WmiLight is actually used by the Native AOT application and may remove necessary code.
Note: This issue only affects Native AOT builds.
Solution: Add the following to your Native AOT application's project file to prevent excessive trimming:
<ItemGroup>
<TrimmerRootAssembly Include="WmiLight" />
</ItemGroup>This marks WmiLight as a root assembly for the trimmer, ensuring that all necessary code is preserved during the Native AOT compilation.
