Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Create A Web Service Method to Get NT Service Information

4.89/5 (17 votes)
11 Dec 2007CPOL2 min read 1   268  
An article on creating a Web service method to get NT service information

Introduction

Recently, I created a mobile application-Siccolo that allows me to manage SQL Servers by using Web services hosted on public domain (see more information here about how to develop a mobile management tool).
As part of a management tool, I needed to show some information about the selected NT Service, such as path to a service executable. For example, services.msc shows it like this:

In my mobile management tool, I needed to display in a similar manner:

The code presented retrieves information about Path to Executable for a selected NT Service.

Background

My "managing" Web service is hosted under SSL with "Integrated Windows" authentication being set. Therefore, a mobile application is required to pass network credentials. And this is needed to be able to remotely access to get information from the registry on the remote machine.

Using the Code

Components used:

  • serviceprocessor.asmx.cs - Web service interface
  • NTServiceInfo.cs - a small "wrapper" to retrieve NT Service information

.NET provides just the tool for this task - System.ServiceProcess.ServiceController class. To create an instance of System.ServiceProcess.ServiceController:

C#
// C# //

...
System.ServiceProcess.ServiceController Service;
if (this.m_MachineName!="")
    {Service = new ServiceController(this.m_ServiceName, this.m_MachineName ) ;}
else
    {Service = new ServiceController(this.m_ServiceName ) ;}
...

The fact that authentication (Integrated Windows authentication or Basic) is in place on IIS, actually helps here. In order to be able to access service(s) on a different machine than Web service host, Web service needs to "assume" an identity of authenticated user. Normally, Web service is running under ASP.NET user with minimum privileges and I needed to impersonate authenticated user with the Web service.

On the server side, to retrieve an authenticated user, we need to use System.Web.Services.WebService.User and then impersonate: and code does just that - "Impersonates the user represented by the WindowsIdentity object."

C#
// C# //

...

System.Security.Principal.WindowsImpersonationContext impersonationContext;
        impersonationContext =
            ((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
...    

To retrieve the path to the executable, we can look under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services, find the selected service, and get ImagePath:

To read the value of a registry key (on the remote machine, or on the local machine):

C#
private string ReadRegestryKey(string RegistryKey, out string ErrorInfo)
{
    try
    {
        string Value="";
        ErrorInfo ="";

        RegistryKey Key;
        RegistryKey KeyHKLM = Registry.LocalMachine;
        try
        {
            if (this.m_MachineName !="" )    //open on remote machine

                Key = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(
                RegistryHive.LocalMachine, this.m_MachineName
                ).OpenSubKey(RegistryKey);
            else
                Key = KeyHKLM.OpenSubKey(RegistryKey);

            Value = Key.GetValue("ImagePath").ToString();
            Key.Close();
        }
        catch (Exception ex_open_key)
        {
            ErrorInfo = "Error Accessing Registry [" + ex_open_key.ToString() + "]";
            return "";
        }
        return Value;
    }
    catch (Exception ex_read_registry)
    {
        ErrorInfo =   ex_read_registry.Message;
        return "";
    }
}

Once the path to the executable is extracted, we need to check if the path needs to be "extracted" from something like %SystemRoot%\system32\... to the actual path.
Value of %SystemRoot% can be found in the registry:

C#
private string ExpandEnvironmentString(string Path)
{
    string SystemRootKey = "Software\\Microsoft\\Windows NT\\CurrentVersion\\";
    RegistryKey Key;

    if (this.m_MachineName !="" )
        Key = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(
        RegistryHive.LocalMachine, this.m_MachineName
        ).OpenSubKey(SystemRootKey);
    else
        Key = Registry.LocalMachine.OpenSubKey(SystemRootKey);

        string ExpandedSystemRoot ="";
        ExpandedSystemRoot = Key.GetValue("SystemRoot").ToString();
        Key.Close();

        Path = Path.Replace ("%SystemRoot%", ExpandedSystemRoot);

        return Path;
}

Finally:

C#
public string PathToExecutable(WindowsPrincipal User)
{
    //HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services.

    string RegistryKey = "SYSTEM\\CurrentControlSet\\Services\\" +
    this.m_ServiceName;

    string ErrorInfo="";
    System.Security.Principal.WindowsImpersonationContext impersonationContext;
    impersonationContext =
        ((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();

    string Path= this.ReadRegestryKey(RegistryKey, out ErrorInfo);
    if ( Path.IndexOf("%")>0)
    {
        Path = ExpandEnvironmentString(Path);
    }
    impersonationContext.Undo();
    return Path;
}

Notice, how the calls to ReadRegestryKey() and ExpandEnvironmentString() are "wrapped in" :

C#
impersonationContext =
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
...
...
impersonationContext.Undo();

Then, the actual Web method:

C#
[WebMethod]
public bool GetNTServiceInfo(string RemoteServerAddress ,
    string NTServiceName,
    out string ServiceInfo_XML ,
    out string ErrorInfo )
{
    try
    {
        string ToDebugSetting
            = System.Configuration.ConfigurationSettings.AppSettings.Get("DebugMode");
        bool ToDebug = (ToDebugSetting!="");

        ErrorInfo="";
        ServiceInfo_XML ="";
        System.ServiceProcess.ServiceController Service;
        if (RemoteServerAddress!="")
            {Service = new ServiceController(NTServiceName, RemoteServerAddress ) ;}
        else
            {Service = new ServiceController(NTServiceName ) ;}

        DataSet objDataSet = new DataSet("QueryResults");
        objDataSet.Tables.Add("ServiceInfo");
        objDataSet.Tables[0].Columns.Add("service_display_name",
            System.Type.GetType("System.String"));
        objDataSet.Tables[0].Columns.Add("status",
            System.Type.GetType("System.String"));
        objDataSet.Tables[0].Columns.Add("service_name",
            System.Type.GetType("System.String"));
        objDataSet.Tables[0].Columns.Add("path_to_executable",
            System.Type.GetType("System.String"));
        objDataSet.Tables[0].Columns.Add("can_stop",
            System.Type.GetType("System.Boolean"));
        objDataSet.Tables[0].Columns.Add("can_pause_and_continue",
            System.Type.GetType("System.Boolean"));
        objDataSet.Tables[0].Columns.Add("services_depend_on",
            System.Type.GetType("System.String"));
        objDataSet.Tables[0].Columns.Add("dependent_services",
            System.Type.GetType("System.String"));

        NTServiceInfo si  = new NTServiceInfo(NTServiceName,
                            RemoteServerAddress);

        Object[] r    = new Object[8] {Service.DisplayName,
                        Service.Status.ToString(),
                        Service.ServiceName,
                        si.PathToExecutable((WindowsPrincipal) this.User),
                        Service.CanStop.ToString(),
                        Service.CanPauseAndContinue.ToString(),
                        si.ServiceDependOnStringList(Service.ServicesDependedOn),
                        si.DependentServicesStringList(Service.DependentServices)
                        };

        objDataSet.Tables[0].Rows.Add(r);

        Service.Close();

        System.IO.StringWriter objStringWriter =new System.IO.StringWriter();
        objDataSet.WriteXml(objStringWriter, XmlWriteMode.WriteSchema);

        ServiceInfo_XML = "<?xml version='1.0' ?>" + objStringWriter.ToString();

        ErrorInfo = "";
        return true;
    }
    catch (Exception ex_get_service_info)
    {
        ServiceInfo_XML ="";
        ErrorInfo = ex_get_service_info.Message;
        return false;
    }
}

Points of Interest

If you would like to read more on this story, please take a look at Siccolo - Free Mobile Management Tool For SQL Server and full article at How to Develop Mobile Management Tool.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)