Search code examples
c#.netwindowsservicerobotframework

Start / stop windows services with C# ServiceController and Impersonation


I'm developing remote keyword library for robot framework. This library is running in c# .net.

The remote library is built using NRobot-Server. This is handling xml-rpc server and other stuff, so I can write only keywords for robot framework. FYI this xml-rpc server is multi-threaded.

Based on this answer and this demo I've managed to put something together. However I'm always getting Cannot open <<my service>> service on computer '192.168.0.105'

Cannot open <<my service>> service on computer '192.168.0.105'.
at System.ServiceProcess.ServiceController.GetServiceHandle(Int32 desiredAccess)
   at System.ServiceProcess.ServiceController.Stop()
   at RKL.KeywordLibrary.KeywordLibraryImpl.ControlService(String host, String username, String password, String name, String action, String domain) in C:\Dev\QueueServiceSystemTestRKL\src\RKL\KeywordLibrary\KeywordLibraryImpl.cs:line 115
   at RKL.KeywordLibrary.RklKeywordClass.ControlService(String h
ost, String username, String password, String name, String action) in C:\Dev\RKL\src\RKL\KeywordLibrary\RKLKeywordClass.cs:line 21

Since I should be able to control services remotely, the dev environment looks like this:

-------------------     --------------------                              
|      OS X       |     |      Win 10      |                              
|                 |     |                  |                             
| robot framework | --> |  remote keyword  |                                
|                 |     |   library (C#)   |                           
-------------------     --------------------                             
                                |
                                |
                                v 
                       --------------------- 
                       |  Win Server 2019  | 
                       |                   | 
                       |      service      | 
                       |                   | 
                       --------------------- 

Actual code looks like this (I'm using SimpleImpersonation nuget)

public void ControlService(string host, string username, string password, string name, string action)
{
    var credentials = new UserCredentials(username, password);
    Impersonation.RunAsUser(credentials, SimpleImpersonation.LogonType.Interactive, () =>
    {
        ServiceController sc = new ServiceController(name, host);
        TimeSpan timeout = new TimeSpan(0,0,30);
        switch (action)
        {
            case "start":
                sc.Start();
                sc.WaitForStatus(ServiceControllerStatus.Running, timeout);
                break;
            case "stop":
                sc.Stop();
                sc.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
                break;
            default:
                string msg = String.Format("Unknown action: '{0}'", action);
                throw new Exception(msg);
        }
    });
}

Also to mention, I've created testuser on both windows machines and both users are Administrators.

Currently I'm trying to disable all windows security, however I'm dead in the water. Just trying random stuff.

Does anyone know where could be problem? Any help would be appreciated!


Solution

  • So I've found out about ServiceControllerPermission class and tried it out. And it worked

    public void ControlService(string host, string username, string password, string name, string action)
    {
        var credentials = new UserCredentials(username, password);
        Impersonation.RunAsUser(credentials, SimpleImpersonation.LogonType.Interactive, () =>
        {
            ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, host, name);
            scp.Assert();
    
            ServiceController sc = new ServiceController(name, host);
            TimeSpan timeout = new TimeSpan(0,0,30);
            switch (action)
            {
                case "start":
                    sc.Start();
                    sc.WaitForStatus(ServiceControllerStatus.Running, timeout);
                    break;
                case "stop":
                    sc.Stop();
                    sc.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
                    break;
                default:
                    string msg = String.Format("Unknown action: '{0}'", action);
                    throw new Exception(msg);
            }
        });
    }