Setting Windows Service Account - C# and WMI

Tags: .NET, C#, CodeSnippets, WMI

I've been busting my brains for over two hours trying to accomplish a relatively simple task - changing the username and password of a currently running Windows Service. Should be trivial enough, but the managed ServiceController class doesn't give me that ability, so I had to resort to more esoteric solutions.

Assuming I wish to avoid direct registry manipulation - an easy enough choice to make - I can either P/Invoke the ChangeServiceConfig method (from advapi32.dll) or use System.Management and call the Win32_Service WMI class. I decided to use as little interop as possible and headed down the WMI path.

Now, Win32_Service doesn't have a property for the user password, which makes sense for security reasons. What I can do is invoke the Change method on it, passing it the username and password. Unfortunately, Change accepts 11 different parameters that can be changed, most of which I wish to leave untouched.

I tried using MgmtClassGen.exe to generate a strongly-typed wrapper around Win32_Service, but the Change method is created for me needed work - it asked for an ErrorControl parameter as a Byte, for instance, but the ErrorControl property was strongly typed as a String - I had to start tweaking the parameters myself, receiving strange exceptions and potentially modifying properties I didn't want to.

 Most code samples I could find refer to VBscript, which can simply not pass the parameters it doesn't want, but C# isn't as flexible. Ultimately, though, I decided to simulate this approach by simply passing null for any parameters I want untouched. This requires me to work without the MgmtClassGen class, and invoke the Change method untyped, like this. I can't say it's code I'm especially proud of, but it gets the work done.

string objPath = string.Format("Win32_Service.Name='{0}'", serviceName);
using (ManagementObject service = new ManagementObject(new ManagementPath(objPath)))
{
   object[] wmiParams = new object[11];
   wmiParams[6] = username;
   wmiParams[7] = password;
   service.InvokeMethod("Change", wmiParams);
}

16 Comments

  • bschuth said

    This came along just in time, and works like a charm. I had just gotten to the same point you did (hey, why are the properties not the same type as the Change parameters?). I don't like using Invoke if I can avoid it, but you're right, it works. On to the next problem.

  • asbf said

    I don't suppose there is an easy way to find the account of the service running? Like "Local System Account" versus the name of a specific user's domain account?

  • asbf said

    This is the only way I can find to do it. Not very elegant. string serviceName = "eventLog"; System.Management.SelectQuery query = new System.Management.SelectQuery(string.Format("select name, startname from Win32_Service where name = '{0}'", serviceName)); using (System.Management.ManagementObjectSearcher searcher = new System.Management.ManagementObjectSearcher(query)) { foreach (System.Management.ManagementObject service in searcher.Get()) { Console.WriteLine(string.Format("Name: {0} - Logon : {1} ", service["Name"], service["startname"])); } }

  • asbf said

    Ah, my original question didn't post. I wanted to know if there was a way to query the service account after the service was installed.

  • Jason said

    Thanks for the code. I am trying to change the user account of a windows service but could not get it to work. The code seems to run without exception but when i check the "Log on As" column of my service i don't see the new account set up. Am i missing anything here?

  • Bhargav said

    Thank you very much for the code. I have spent hours looking for this solution. Hope it works fine... Thanks a lot.

  • Console.WriteLine(string.Format("Name: {0} - Logon : {1} ", service["Name"], service["startname"])); said

    Console.WriteLine(string.Format("Name: {0} - Logon : {1} ", service["Name"], service["startname"]));

  • Johann said

    Hi Thanks, the code works great. I need to however ask more f you if possible: I want to change the username and password of the service on a remote system, how do I access this property in code, using your method? Thanks in advance Johann

  • nitins said

    Thanks for great code, i am also inspire how i can call the methods by the help of WMI as well as oprations.But one query regarding the imporsination in c#, how easily we can get that.

  • Jeou said

    Hi, I encountered a problem on the line: service.InvokeMethod("Change", wmiParams); It says: "Not Found". What is "Change" anyway? is it a local method or what? please help me. Thanks in advanced.

  • Burcuco said

    It's not working with this path =>"Win32_Service.Name='{0}'" Open cmd and type wbemtest.exe to query wmi for services. Click query button on the opened window and type "Select * from Win32_Service". Services will be listed below and select the service you want to set account details programmatically. Double click to service and look at the parameters. The path above is relpath in the parameters.So you should get Path parameter. Copy the path parameter value and past your code's path. So it will be working ;)

Comments have been disabled for this content.