Working with Windows Services using C# and WMI

I’ve been trying out various ways of intracting with Windows Services from C# code. From what I’ve seen, basically you have the following methods:

  • WMI Queries via classes in the System.Management namespace,
  • Strongly-typed WMI proxy classes generated using MgmtClassGen.exe, and
  • The System.ServiceProcess.ServiceController class.

WMI queries

WMI has a query language called WQL, which is similar to to SQL. You can execute these in C# using the System.Management classes.

To work with WMI you need to add a reference to the System.Management assembly. Then you can set up a connection (i.e. ManagementScope) to the WMI Provider as follows:

ConnectionOptions options = new ConnectionOptions();

// If we are connecting to a remote host and want to
// connect as a different user, we need to set some options
//options.Username = 
//options.Password =
//options.Authority = 
//options.EnablePrivileges = 

// If we are connecting to a remote host, we need to specify the hostname:
//string providerPath = @"\\Hostname\root\CIMv2";
string providerPath = @"root\CIMv2";

ManagementScope scope = new ManagementScope(providerPath, options);
scope.Connect();

You can now use that scope to run various WMI queries and perform WMI operations on the returned objects.

Note that the Connect() method can throw exceptions. I’m not catching any of that just to keep the examples clean.

To get a list of services you can create an ObjectQuery, and run a ManagementObjectSearcher. For example, below we are getting a list of services which have names that start with “SQL”:

ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Service WHERE Name LIKE 'SQL%'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);

foreach (ManagementObject o in searcher.Get())
{
    string name = o["Name"];
    ...
}

In the above code the returned ManagementObjects are of type Win32_Service. For a full list of fields in this type, check the Win32_Service reference.

If you want to call a method on a single service, then the following may be more suitable:

ManagementPath path = new ManagementPath("Win32_Service.Name='SomeWindowsService'");
ManagementObject obj = new ManagementObject(scope, path, new ObjectGetOptions());
ManagementBaseObject outParams = obj.InvokeMethod("StartService", (ManagementBaseObject)null, null);
uint returnCode = System.Convert.ToUInt32(outParams.Properties["ReturnValue"].Value);

As purely an academic excercise you can get the return code using an observer. You need to provide a ManagementOperationObserver to the InvokeMethod() method. You would replace the last two lines in the above example with the following:

ManagementOperationObserver observer = new ManagementOperationObserver();
// The ObjectReadyHandler method will need a prototype of
//    public void ObjectReadyHandler(object sender, ObjectReadyEventArgs e) { ... }
observer.ObjectReady += ObjectReadyHandler;
obj.InvokeMethod(observer, "StartService", new object[0]);

The StartService and StopService methods notify both the ObjectReady and the Completed listeners, so you can use either.

Note that the Observer is notified as soon as the requested method has been either successfully invoked, or there is an immediate failure in the invocation. For example, if you request a StartService, it will not wait for the service start operation to complete before notifying the observer. The service may still fail to start even after a Success notification, e.g. if an error occurs in the service it self while it is starting.

WMI proxy classes

The .NET Framework includes a tool to generate strongly typed proxy classes for the WMI classes. You can generate the proxy for the Win32_Service class using the following:

MgmtClassGen.exe  Win32_Service  /L CS  /O some.namespace  /N root\cimv2  /P ServiceProxy.cs

This gives you the following new class – some.namespace.ServiceProxy which you can use to get a list of service objects that contain the WMI fields. You can refresh these fields, all call methods on the class while you hold a reference to the class.

You still need to define a ManagementScope.

string condition = "Name LIKE 'SQL%'";
ServiceProxy.ServiceCollection sc = ServiceProxy.GetInstances(scope, condition);
foreach (ServiceProxy s in sc)
{
    string name = s.Name;
    ...
    s.StartService();
}

The condition string above is just the where-clause you would use in WQL.

Service Controller

The only non-System.Management method I tried was the ServiceController. It does not provide any way of getting Service fields, but it lets you perform operations like start/stop etc. You just need to provide the service-name, and the hostname if it is not the localhost.

ServiceController controller = new ServiceController("servicename", "hostname");
if (ServiceControllerStatus.Stopped == controller.Status)
{
    controller.Start();
    // You can use the following line to wait for service to finish starting
    // you can supply a TimeSpan object to this method to timeout after that period.
    controller.WaitForStatus(ServiceControllerStatus.Running);
}

The other difference between this method and the WMI method is that you cannot make the calls as a different user, so your application needs to be running as a user that has the necessary permissions.

Author: Musaul Karim

Software Engineer, Hobbyist Photographer, and a bit of a gadget geek.

Leave a Reply

Your email address will not be published. Required fields are marked *