包含可执行文件路径的 ServiceController 类
扩展 System.ServiceProcess.ServiceController 类以获取进程可执行文件的路径
引言
在使用 Windows 服务时,我需要进程可执行文件的路径,这让我找到了 The Code Project 上 aleksisa 的一篇文章,名为 创建 Web 服务方法以获取 NT 服务信息。
这篇文章很棒,包含了实现该功能所需的所有信息。
浏览 aleksisa 的代码后,我开始认为这种功能应该使用继承来实现,因此开始将其付诸实践。
这就是我写出的这篇文章。
Using the Code
因为该代码是使用继承来实现的,所以您可以像以前一样继续使用 ServiceController
类,只需确保更改对正确的 namespace
的引用即可。
aleksisa 和我的代码之间最重要的区别之一是,ServiceController
类不会在内部处理安全性。 这意味着所有模拟等都需要由调用代码处理。
这是我实现的类(下面我提到了一些细节)
public class ServiceController : System.ServiceProcess.ServiceController
{
private string m_ImagePath;
private ServiceController[] m_DependentServices;
private ServiceController[] m_ServicesDependedOn;
public ServiceController()
: base()
{
}
public ServiceController( string name )
: base( name )
{
}
public ServiceController( string name, string machineName )
: base( name, machineName )
{
}
public string ImagePath
{
get
{
if( m_ImagePath == null )
{
m_ImagePath = GetImagePath();
}
return m_ImagePath;
}
}
public new ServiceController[] DependentServices
{
get
{
if( m_DependentServices == null )
{
m_DependentServices =
ServiceController.GetServices( base.DependentServices );
}
return m_DependentServices;
}
}
public new ServiceController[] ServicesDependedOn
{
get
{
if( m_ServicesDependedOn == null )
{
m_ServicesDependedOn =
ServiceController.GetServices( base.ServicesDependedOn );
}
return m_ServicesDependedOn;
}
}
public static new ServiceController[] GetServices()
{
return GetServices( "." );
}
public static new ServiceController[] GetServices( string machineName )
{
return GetServices( System.ServiceProcess.ServiceController.GetServices
( machineName ) );
}
private string GetImagePath()
{
string registryPath = @"SYSTEM\CurrentControlSet\Services\" + ServiceName;
RegistryKey keyHKLM = Registry.LocalMachine;
RegistryKey key;
if( MachineName != "" )
{
key = RegistryKey.OpenRemoteBaseKey
( RegistryHive.LocalMachine, this.MachineName ).OpenSubKey( registryPath );
}
else
{
key = keyHKLM.OpenSubKey( registryPath );
}
string value = key.GetValue( "ImagePath" ).ToString();
key.Close();
return ExpandEnvironmentVariables( value );
//return value;
}
private string ExpandEnvironmentVariables( string path )
{
if( MachineName == "" )
{
return Environment.ExpandEnvironmentVariables( path );
}
else
{
string systemRootKey = @"Software\Microsoft\Windows NT\CurrentVersion\";
RegistryKey key = RegistryKey.OpenRemoteBaseKey
( RegistryHive.LocalMachine, MachineName ).OpenSubKey( systemRootKey );
string expandedSystemRoot = key.GetValue( "SystemRoot" ).ToString();
key.Close();
path = path.Replace( "%SystemRoot%", expandedSystemRoot );
return path;
}
}
private static ServiceController[] GetServices
( System.ServiceProcess.ServiceController[] systemServices )
{
List<ServiceController> services = new List<ServiceController>
( systemServices.Length );
foreach( System.ServiceProcess.ServiceController service in systemServices )
{
services.Add( new ServiceController
( service.ServiceName, service.MachineName ) );
}
return services.ToArray();
}
}
首先,我添加了一个新的属性 ImagePath
,用于检索可执行文件的路径。
我还隐藏了来自父类 System.ServiceProcess.ServiceController
的两个属性和一个 static
方法。 这些通常会返回 System.ServiceProcess.ServiceController
类型的数组,所以我更改了它们以使用新的 ServiceController
。
请注意,新属性仅在第一次调用时才获取它们的值,而不是在实例化时。 虽然这效率稍高,但也意味着如果在从远程计算机获取数据时连接中断,则可能会出现不可预测的结果。
虽然该代码主要使用 aleksisa 的实现,但我确实更改了一些内容。 例如,对于本地路径,我使用了 Environment.ExpandEnvironmentVariables
方法,该方法在 dchrno 的 评论中提到。
这是它的使用方法
static void Main( string[] args )
{
ServiceController[] services = ServiceController.GetServices( "bwdemo" );
foreach( ServiceController service in services )
{
Console.WriteLine("ServiceName: {0}; DisplayName: {1}",
service.ServiceName, service.DisplayName );
Console.WriteLine("Image Path: {0}\n", service.ImagePath );
}
Console.ReadLine();
}
处理安全性
虽然我尝试针对远程计算机进行测试,但更多的是为了测试环境变量的扩展,而不是为了安全目的。
正如我之前提到的,安全模拟不是在 ServiceController
类中处理的,必须在调用代码中完成。 我无法自己测试,但是,需要它的代码都是从 aleksisa 复制/粘贴的,所以如果该代码有效,那么这个代码也应该有效。
顺便说一句,如果只需要服务列表,只需在远程计算机上设置相同的用户名和密码即可。
关注点
在测试过程中出现的一个有趣的事情是,我不需要展开环境变量。 由于某种原因,从注册表返回的 string
已经以展开的形式包含了完整路径。
另外,我知道没有评论。 我在半小时内完成了这项工作,并决定将其发布到 The Code Project。
历史
- 2008 年 5 月 31 日日:首次发布