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

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

我找到了关于如何制作Windows服务的这个简洁的小教程 ,但它只告诉我如何为单个服务设置它。 我想设置n个服务实例,每个实例都有一个包含客户名称的显示名称,并使用表示客户ID的命令行参数运行。

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

你怎么做呢?

Wil Peck写了一篇关于如何在一个盒子上安装多个Windows服务实例的文章 。 基本思想是你必须通过给安装程序提供不同的名称来诱骗安装程序认为它们是不同的服务。

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

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

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

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

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

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

更新

exe.Config是一个应用程序配置文件

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

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

据我所知,使用ServiceInstallerServiceProcessInstallerinstallutil提供启动参数是不可能的。 但是,可以使用advapi.dll中的某些COM api提供启动参数(请查看左侧菜单)。 可以在此处找到所需呼叫的完整集合。 它是一个名为ServiceInstaller的类(也称为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; } ... } 

您可以使用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“Instance 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"