C# Windows服务-如何使名称可配置

C# Windows服务-如何使名称可配置,c#,windows-services,C#,Windows Services,我用C#编写了一个windows服务。一开始的要求是,我应该只运行该服务的一个实例,但情况已经改变,现在我需要多个实例。这就是为什么我需要根据配置文件更改服务名称 用正确的名字注册最好的方法是什么?我应该写另一个工具来做吗?我可以从App.config文件中读取名称并在服务和安装程序类中相应地设置它吗 PS>我真的不明白名称是如何工作的-应该在服务和安装程序类中设置名称,但是当使用installutil.exe甚至powershell新服务安装时,也应该指定名称。那必须是一样的吗?还是一个凌驾于

我用C#编写了一个windows服务。一开始的要求是,我应该只运行该服务的一个实例,但情况已经改变,现在我需要多个实例。这就是为什么我需要根据配置文件更改服务名称

用正确的名字注册最好的方法是什么?我应该写另一个工具来做吗?我可以从App.config文件中读取名称并在服务和安装程序类中相应地设置它吗


PS>我真的不明白名称是如何工作的-应该在服务和安装程序类中设置名称,但是当使用installutil.exe甚至powershell新服务安装时,也应该指定名称。那必须是一样的吗?还是一个凌驾于另一个之上

您只需从app.config读取并在安装程序类中进行设置即可。
通常,会自动创建从
Installer
继承的类。它包含类型为
System.ServiceProcess.ServiceInstaller
的成员,很可能名为
serviceprocesinstaller1
。这有一个可以设置的属性。此外,您需要将
ServiceBase
派生类的属性设置为相同的值。

在默认实现中,在相应的
InitializeComponent
方法中将这些值设置为常量,但没有理由坚持这样做。它可以动态地完成,没有任何问题。

我想我应该加上我的2美分,因为我遇到了这个问题。我有一个名为“ProjectInstaller.cs”的文件,下面有设计器和资源。在设计中打开它会将MyServiceInstaller和MyProjectInstaller显示为设计图面上的项目。我能够更改
ProjectInstaller()
构造函数中的名称,并从模块目录手动加载配置文件:

public ProjectInstaller()
{
    InitializeComponent();

    var config = ConfigurationManager.OpenExeConfiguration(this.GetType().Assembly.Location);

    if (config.AppSettings.Settings["ServiceName"] != null)
    {
        this.MyServiceInstaller.ServiceName = config.AppSettings.Settings["ServiceName"].Value;
    }
    if (config.AppSettings.Settings["DisplayName"] != null)
    {
        this.MyServiceInstaller.DisplayName = config.AppSettings.Settings["DisplayName"].Value;
    }
}

与Jason Goemaat的回答相同,这是您在项目中循环所有可用安装程序的方式,这节省了您确保将每个新服务添加到此类的时间。在我的项目中,我总共有12个服务(我们在这里和那里添加了一个新服务),我们希望它们按实例名称分组在一起,因此在服务管理单元控制台中查看时,SVC(实例1)服务XX和SVC(实例1)服务YY彼此相邻

    public ProjectInstaller()
    {
        InitializeComponent();

        var config = ConfigurationManager.OpenExeConfiguration(this.GetType().Assembly.Location);

        string instanceName = config.AppSettings.Settings["Installer_NamedInstanceName"].Value;
        string instanceID = config.AppSettings.Settings["Installer_NamedInstanceID"].Value;
        bool usesNamedInstance = !string.IsNullOrWhiteSpace(instanceName) && !string.IsNullOrWhiteSpace(instanceID);

        if (usesNamedInstance)
        {
            foreach (var installer in this.Installers)
            {
                if (installer is ServiceInstaller)
                {
                    var ins = (ServiceInstaller)installer;
                    ins.ServiceName = ins.ServiceName + "-" + instanceID;
                    // Want the service to be named SVC (Instance Name) Audit Log Blah Blah Service
                    ins.DisplayName = ins.DisplayName.Replace("SVC ", "SVC (" + instanceName + ") ");
                }
            }
        }

    }
但是您还需要做一些其他事情-初始化服务时,还必须更改服务名称,否则会出现“可执行文件未实现服务”的错误。为此,我在
Program.cs
中实现了以下代码:

    internal static void HandleCustomServiceName(ServiceBase sbase)
    {
        if (!string.IsNullOrWhiteSpace(customInstanceName))
        {
            sbase.ServiceName = sbase.ServiceName + "-" + customInstanceName;
        }
    }
然后,在每个服务的构造函数中:

    public SystemEventWatcher()
    {
        InitializeComponent();
        Program.HandleCustomServiceName(this);
    }

我对Jason的回答投了赞成票,因为他为在我自己的项目中实现这一点铺平了道路。谢谢

让同一个服务运行多个线程不是一个选项吗?不是,因为这需要我更改代码,我不想这样做。更重要的是,这些服务有单独的控制面板,可以启动/停止它们,这也是出于不同的客户和许多其他原因。服务默认目录设置为%windir%\System32,这会导致错误-无法读取应用程序设置<代码>System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory)解决了这个问题。我对这个解决方案的乐观有点过早。虽然这解决了服务类的问题,但对安装程序却不一样。。不知道该怎么做..什么安装程序?该服务的MSI安装程序?不,实际上是System.Configuration.Install.Installer类,但问题已经在这里解决了:工作起来像个champ!不过,我想遍历一个服务列表,所以我发布了一个单独的答案。然而,您应该为近乎复制粘贴的解决方案赢得荣誉。