Skip to content

A simple and light wmi framework that supports native AOT.

License

Notifications You must be signed in to change notification settings

MartinKuschnik/WmiLight

Repository files navigation

WmiLight Build NuGet Status

What is WmiLight?

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.

In which case should you use WmiLight?

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.

Installation

This project is being distributed as a NuGet package, so open your Package Manager Console window and execute the following command:

NuGet

How to use?

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;
    }
}

Native AOT deployment

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>

Trimming issue with .NET Standard libraries (Native AOT only)

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.

Other benefits:

  • easy usage

  • support for Native AOT deployment

  • no distinction between local and remote queries

  • Debugger Preview

    Debugger_Preview

About

A simple and light wmi framework that supports native AOT.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Languages