65.9K
CodeProject 正在变化。 阅读更多。
Home

包含可执行文件路径的 ServiceController 类

starIconstarIconstarIconstarIconstarIcon

5.00/5 (10投票s)

2008年5月31日

CPOL

2分钟阅读

viewsIcon

62940

downloadIcon

525

扩展 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 日:首次发布
© . All rights reserved.