简体   繁体   English

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

[英]Windows service - how to make name configurable

I have written a windows service in C#. 我用C#写了一个windows服务。 The requirement at the beginning was that I should be running only one instance of that service, but that has changed and now I need multiple instances. 一开始的要求是我应该只运行该服务的一个实例,但是已经改变了,现在我需要多个实例。 This is why I need to change the service name according to the configuration file. 这就是我需要根据配置文件更改服务名称的原因。

What would be the best way to make it register with the correct name ? 使用正确的名称注册的最佳方法是什么? Should I write another tool to do it? 我应该写另一个工具来做吗? Can I just read the name from App.config file and set it in the service and installer classes accordingly ? 我可以从App.config文件中读取名称并在服务和安装程序类中相应地设置它吗?

PS> I do not really understand how that thing with names work - one should set names in service and installer classes, but then when installing with installutil.exe or even powershell new-service the name also should be specified. PS>我真的不明白名称是如何工作的 - 应该在服务和安装程序类中设置名称,但是当使用installutil.exe甚至PowerShell new-service进行安装时,也应该指定名称。 Does that have to be the same? 那必须是一样的吗? Or one overrides another? 或者一个覆盖另一个?

You can simply read it from the app.config and set it in the installer classes. 您只需从app.config中读取它并在安装程序类中进行设置即可。
Normally, a class that inherits from Installer is automatically created. 通常,会自动创建一个继承自Installer的类。 It contains a member of type System.ServiceProcess.ServiceInstaller , most likely named serviceProcessInstaller1 . 它包含System.ServiceProcess.ServiceInstaller类型的成员,很可能名为serviceProcessInstaller1 This has a property ServiceName you can set. 这有一个您可以设置的属性ServiceName Additionally, you need to set the ServiceName property of the ServiceBase derived class to the same value. 此外,您需要将ServiceBase派生类的ServiceName属性设置为相同的值。
In a default implementation, these are set to constant values in the respective InitializeComponent methods, but there is no reason to stick with this. 在默认实现中,这些在相应的InitializeComponent方法中设置为常量值,但没有理由坚持这一点。 It can be done dynamically without problems. 它可以动态完成而不会出现问题。

I though I'd add my 2 cents since I ran into this. 我虽然遇到了这个问题,但我加了2美分。 I have a file called "ProjectInstaller.cs" with designer and resources under it. 我有一个名为“ProjectInstaller.cs”的文件,其下有设计者和资源。 Opening it up in design shows MyServiceInstaller and MyProjectInstaller as items on the design surface. 在设计中打开它会将MyServiceInstaller和MyProjectInstaller显示为设计图面上的项目。 I was able to change the names in the ProjectInstaller() constructor, and manually loaded the config file from the module directory: 我能够在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;
    }
}

In the same vein as Jason Goemaat's answer, this is how you would loop through all available installers in your project, which saves you the time of being sure you added each new service to this class. 与Jason Goemaat的回答一样,这就是你如何遍历项目中所有可用的安装程序,这样可以节省您确保将每个新服务添加到此类的时间。 In my project, I have a total of 12 services (and we add a new one here and there), and we wanted them grouped together by the instance name, so SVC (Instance 1) Service XX and SVC (Instance 1) Service YY were next to each other when viewed in the services snap-in console. 在我的项目中,我总共有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 + ") ");
                }
            }
        }

    }

HOWEVER , there is something else that you need to do - When initializing the service, you must also change the service name, otherwise you'll get an error along the lines of "The executable doesn't implement the service". 但是 ,您还需要做其他事情 - 初始化服务时,您还必须更改服务名称,否则您将收到“可执行文件未实现服务”的错误。 I did this by implementing the following code in Program.cs : 我通过在Program.cs实现以下代码来做到这一点:

    internal static void HandleCustomServiceName(ServiceBase sbase)
    {
        if (!string.IsNullOrWhiteSpace(customInstanceName))
        {
            sbase.ServiceName = sbase.ServiceName + "-" + customInstanceName;
        }
    }

Then, in the constructor of each service: 然后,在每个服务的构造函数中:

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

I've upvoted Jason's answer for paving the way to implementing this in my own project. 我赞成Jason的答案,为在我自己的项目中实现这一点铺平了道路。 Thanks! 谢谢!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM