如何使用参数创建Windows服务?

时间:2010-02-11 10:20:33

标签: c# windows-services

我已经编写了一个Windows服务,我希望每个客户运行一个实例。这是因为每个客户都有自己的DB具有相同的模式; Windows服务之间的唯一区别是,它们每个都有一个与他们被指定服务的客户数据库相对应的不同参数。 (而且我不能拥有一个具有多个工作线程的服务,因为数据库连接使用的是一个静态变量,我无法通过线程进行操作。)

我找到了关于如何制作Windows服务的this neat little tutorial,但它只向我展示了如何为单个服务设置它。我想设置服务的 n 实例,每个实例都有一个包含客户名称的显示名称,并使用表示客户ID的命令行参数运行。

上面链接的教程有一个名为MyWindowsServiceInstaller的类,它在本地系统上安装了Windows服务,我猜这将是一个合理的位置来设置foreach循环遍历所有我的客户,为每个客户设置一项服务。但我无法在提供的接口上看到任何可以让我为新服务设置命令行参数。

你是怎么做到的?

5 个答案:

答案 0 :(得分:15)

我想要的只是将一个参数发送到我创建的服务。 事实证明,您所要做的就是(小心!)在HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \中编辑注册表,并在引号后面的ImagePath中添加参数。

EG。 ImagePath值数据:“C:\ Program Files \ myservice \ myservice.exe”param1

我在此链接中找到了解决方案http://social.msdn.microsoft.com/Forums/is/csharpgeneral/thread/38242afa-7e40-4c06-975e-aa97d3cc782f

答案 1 :(得分:9)

Wil Peck wrote a good article关于如何在一个盒子上安装多个Windows服务实例。基本的想法是你必须欺骗安装人员认为他们是不同的服务,给他们不同的名字。

话虽如此,重新设计数据库连接代码似乎更容易(也更易于维护),以便它可以支持多个工作线程。

答案 2 :(得分:1)

您基本上需要多次安装该服务,并使用它的exe.config文件对其进行自定义。

或者,您可以拥有一个为每个客户端运行不同工作线程的服务。

<强>更新

exe.Config是Application Configuration File

我不知道如何使用该安装程序组件来安装该服务的多个实例,我不知道你可以。

如果我们需要在一台机器上运行我们的一个服务的多个实例,我们实际上只安装一次,然后逐字复制已安装的文件夹并更改第二个实例的exe名称。然后在其自己的应用程序配置文件中配置第二个实例。

答案 3 :(得分:1)

据我所知,使用ServiceInstallerServiceProcessInstallerinstallutil无法提供启动参数。但是,可以使用advapi.dll中的某些COM api提供启动参数(请参阅左侧菜单)。可以找到所需呼叫的完整集合here。它是一个名为ServiceInstaller的类(也称为),它包含所需的外部方法和一些实用方法。

您想要使用实用程序方法InstallAndStart。它接受服务名称,显示名称和代表Windows服务的可执行文件的路径。你可以这样称呼它:

InstallAndStart("MyService", "My Service For User 1",
                "c:\\pathtoexe\MyService.exe user1");

如果您有以下服务,则参数startupParam将收到值user1

class Program : ServiceBase
{
    private string startupParam;

    static void Main(string[] args)
    {
        string arg = args[0];
        ServiceBase.Run(new Program(arg));
    }

    public Program(string startupParam)
    {
        this.ServiceName = "MyService";
        this.startupParam = startupParam;
    }
    ...
}

答案 4 :(得分:0)

您可以使用installutil,例如ServiceNameDisplayName将参数传递给安装程序。

ProjectInstaller.cs

public partial class ProjectInstaller : Installer
{
    protected override void OnBeforeInstall(IDictionary savedState)
    {
        SetServiceName();
        base.OnBeforeInstall(savedState);
    }

    protected override void OnBeforeUninstall(IDictionary savedState)
    {
        SetServiceName();
        base.OnBeforeUninstall(savedState);
    }

    private string AppendParameter(string path, char parameter, string value)
    {
        if (!path.StartsWith("\""))
            path = $"\"{path}\"";

        if (value.Contains(" "))
            value = $"\"{value}\"";

        return $"{path} -{parameter}{value}";
    }

    private void SetServiceName()
    {
        if (Context.Parameters.ContainsKey("ServiceName"))
            serviceInstaller.ServiceName = Context.Parameters["ServiceName"];

        if (Context.Parameters.ContainsKey("DisplayName"))
            serviceInstaller.DisplayName = Context.Parameters["DisplayName"];

        Context.Parameters["assemblypath"] = AppendParameter(Context.Parameters["assemblypath"], 's', serviceInstaller.ServiceName);
    }
}

这会将参数附加到服务存储的路径,例如:

之前:“ C:\ Service.exe”

之后:“ C:\ Service.exe” -s“实例1”

然后,当您启动服务并将其传递给服务构造函数时,可以读取此参数。

Program.cs

static void Main(string[] args)
{
    string serviceName = args.Single(x => x.StartsWith("-s")).Substring("-s".Length);

    ServiceBase service = new Service(serviceName);
    ServiceBase.Run(service);
}

Service.cs

public partial class Service : ServiceBase
{
    public Service(string serviceName)
    {
        InitializeComponent();

        ServiceName = serviceName;
    }
}

用法

installutil /ServiceName="Instance 1" /DisplayName="Instance 1 Service" "C:\Service.exe"
installutil /ServiceName="Instance 2" /DisplayName="Instance 2 Service" "C:\Service.exe"