Title: Web Method To Get NT Service Info Author: Greg Dubinovskiy Email: [email protected] Environment: C#, VB.NET Keywords: Web Service, NT Service Info, Manage NT Service, Impersonate Web User Level: Intermediate Description: Create a web service method to get NT service information Section Miscellaneous SubSection General
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 a part of management tool, I needed to show some information about selected NT Service, such as path to service executable.
For example, services.msc shows it like this:
In my mobile management tool, I needed to display in similar manner:
The code presented retrieves information about Path to Executable for selected NT Service.
My "managing" web service is hosted under SSL with "Integrated Windows" authentication being set.
Therefore, mobile application required to pass network credentials. And this is needed to be able remote access
to get information from the registry on the remote machine.
So, as we can see Web Services is quickly finding its way into major system integration development efforts.
In back office and legacy systems alike,
Web Servicess has become a main focus in providing mechanisms to make data move more freely between and across long-closed boundaries.
An entire industry has joined forces and pushes forward in overcoming both technical hurdles such as security and market specific hurdles such as the need of standardized message formats.
While these efforts continue, an entirely different market is rapidly maturing: the mobile devices, software and services market.
Very interesting solution options arise in the cross section, where Web Services meets mobility.
The Microsoft® Windows® .NET Compact Framework opens up the world of Web Services to Pocket PCs, and the wireless nature of Pocket PCs opens up the world of mobility to Web Services.
This means that new fuel is added to the expansion of the XML-based Web Services provider market.
In this article, we will see a sample Pocket PC project that uses Web Services to manage Windows (NT) service from Windows Mobile device!
// 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 ) ;}
...
And 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 the 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.
// C# //
...
System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext =
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
...
To retrieve path to executable, we can look under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services,
find the selected service, and get "ImagePath"
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 Path to executable is extracted, then we need to check, if path needs to be "extracted" from something
like "%SystemRoot%\system32\..." to the actual path.
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;
}
And 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 call to ReadRegestryKey() and call to ExpandEnvironmentString() "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 = "" + objStringWriter.ToString();
ErrorInfo = "";
return true;
}
catch (Exception ex_get_service_info)
{
ServiceInfo_XML ="";
ErrorInfo = ex_get_service_info.Message;
return false;
}
}
In this method, GetNTServiceInfo()
, I'm using Dataset class to create XML string using WriteXML() method:
...
DataSet objDataSet = new DataSet("QueryResults");
...
...
System.IO.StringWriter objStringWriter = new System.IO.StringWriter();
objDataSet.WriteXml(objStringWriter, XmlWriteMode.WriteSchema);
...
where WriteXml() Writes the current data, and the schema, for the DataSet using the specified System.IO.Stream and
XmlWriteMode. XmlWriteMode.WriteSchema tells application to Write the current contents of the DataSet as XML data
with the relational structure as inline XSD schema.
ServiceDependOnStringList()
and DependentServicesStringList()
:
public string ServiceDependOnStringList(System.ServiceProcess.ServiceController[] Services)
{
string ServicesList="";
foreach (System.ServiceProcess.ServiceController service in Services)
{
if (ServicesList!=""){ ServicesList +=",";}
ServicesList += service.DisplayName;
}
return ServicesList;
}
public string DependentServicesStringList(System.ServiceProcess.ServiceController[] Services)
{
string ServicesList="";
foreach (System.ServiceProcess.ServiceController service in Services)
{
if (ServicesList!=""){ ServicesList +=",";}
ServicesList += service.DisplayName;
}
return ServicesList;
}
...
objDataSet.Tables[0].Columns.Add("service_display_name", System.Type.GetType("System.String"));
...
Columns.Add() - Creates and adds a DataColumn object to the DataColumnCollection.
Here, Add() creates a DataColumn object with the specified name and type to the DataColumnCollection.
To let Add() know type of column, we need to pass a parameter of type DataColumn.DataType.
To do that, we can use GetType() method which gets Type with the specified name - System.Type.GetType("System.String")
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
No improvements so far. Nearly perfect.