5,557,174 members and growing! (16,698 online)
Email Password   helpLost your password?
Web Development » Web Services » General     Intermediate License: The Code Project Open License (CPOL)

Create A Web Service Method to Get NT Service Information

By aleksisa

An article on creating a Web service method to get NT service information
C# 1.0, C# 2.0, C#, Windows, .NET, .NET 1.0, .NET 1.1, .NET 2.0VS.NET2003, VS2005, Visual Studio, Dev

Posted: 17 Nov 2006
Updated: 11 Dec 2007
Views: 15,367
Bookmarked: 21 times
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
16 votes for this Article.
Popularity: 5.62 Rating: 4.67 out of 5
1 vote, 6.3%
1
0 votes, 0.0%
2
0 votes, 0.0%
3
1 vote, 6.3%
4
14 votes, 87.5%
5

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# //

...
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# //

...

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):

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:

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:

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" :

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

Then, the actual Web method:

[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)

About the Author

aleksisa



Location: United States United States

Other popular Web Services articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 5 of 5 (Total in Forum: 5) (Refresh)FirstPrevNext
Subject  Author Date 
GeneralNice work, a couple commentsmemberdchrno20:34 12 Dec '07  
GeneralSource code missingmemberTony Bermudez18:24 17 Nov '06  
GeneralRe: Source code missingmemberaleksisa9:29 18 Nov '06  
GeneralRe: Source code missingmemberfawad.malik12:22 12 Jul '07  
GeneralRe: Source code missingmemberaleksisa14:01 12 Jul '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 11 Dec 2007
Editor: Deeksha Shenoy
Copyright 2006 by aleksisa
Everything else Copyright © CodeProject, 1999-2008
Web11 | Advertise on the Code Project