C# 如何制作带参数的Windows服务?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2243753/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make a Windows service with parameters?
提问by Shaul Behr
I have written a Windows service, of which I want to have 1 instance running per customer. This is because the customers each have their own DB with identical schemas; the only difference between the Windows services is that they will each have a different parameter corresponding to the customer DB that they're designated to serve. (And I can't have one service with multiple worker threads, because the DB connection uses a static variable, which I can't fiddle with across threads.)
我编写了一个 Windows 服务,我希望每个客户运行 1 个实例。这是因为每个客户都有自己的具有相同架构的数据库;Windows 服务之间的唯一区别是,它们每个都有一个不同的参数,对应于它们指定要服务的客户数据库。(而且我不能有一个具有多个工作线程的服务,因为数据库连接使用一个静态变量,我无法跨线程处理。)
I found this neat little tutorialabout how to make a Windows Service, but it only shows me how to set it up for a single service. I want to set up ninstances of the service, each one with a display name that includes the customer name, running with the command line parameter that denotes the customer ID.
我发现了这个关于如何制作 Windows 服务的简洁小教程,但它只向我展示了如何为单个服务设置它。我想设置该服务的n 个实例,每个实例都有一个包含客户名称的显示名称,并使用表示客户 ID 的命令行参数运行。
The tutorial linked above has a class called MyWindowsServiceInstaller
, which installs the windows service on the local system, and I'm guessing this would be a logical place to set up a foreach
loop through all my customers, setting up one service for each. But I can't see anywhere on the interfaces provided that would allow me to set up a command line parameter for the new service.
上面链接的教程有一个名为 的类MyWindowsServiceInstaller
,它在本地系统上安装 Windows 服务,我猜这将是在foreach
我的所有客户之间设置循环的合乎逻辑的地方,为每个客户设置一个服务。但是我在提供的接口上看不到任何允许我为新服务设置命令行参数的地方。
How do you do it?
你怎么做呢?
采纳答案by Jeff Sternal
Wil Peck wrote a good articleabout how to install multiple instances of a windows service on a single box. The basic idea is that you have to trick the installer into thinking they are different services by giving them different names.
Wil Peck 写了一篇关于如何在单个机器上安装多个 Windows 服务实例的好文章。基本思想是,您必须通过给它们不同的名称来欺骗安装程序,使它们认为它们是不同的服务。
Having said that, it seems like it would be easier (and more maintainable) to redesign your database connection code so that it can support multiple worker threads.
话虽如此,重新设计数据库连接代码使其可以支持多个工作线程似乎更容易(也更易于维护)。
回答by Binary Worrier
You basically need to install the service several times, and customise it with it's exe.config file.
您基本上需要多次安装该服务,并使用它的 exe.config 文件对其进行自定义。
Alternatively, you can have one service that runs different worker threads for each client.
或者,您可以让一个服务为每个客户端运行不同的工作线程。
Update
更新
exe.Config is an Application Configuration File
exe.Config 是一个应用程序配置文件
I have no idea how to use that installer component to install several instances of the service, I wasn't aware you could.
我不知道如何使用该安装程序组件来安装该服务的多个实例,我不知道您可以。
Where we need several instances of one of our services to run on one machine, we actually only install it once, then literally copy the installed folder and change the exe name for the second instance. The second instance is then configured in it's own Application Configuration File.
如果我们需要在一台机器上运行我们的一项服务的多个实例,我们实际上只安装一次,然后直接复制已安装的文件夹并更改第二个实例的 exe 名称。然后在它自己的应用程序配置文件中配置第二个实例。
回答by Ronald Wildenberg
As far as I known it is impossible to provide startup parameters using either ServiceInstaller
, ServiceProcessInstaller
or installutil
. However, it is possible to provide startup parameters using some COM api's from advapi.dll(check the left menu). A complete collection of the required calls can be found here. It's a class (also) called ServiceInstaller
that contains the required external methods and some utility methods.
据我所知,不可能使用ServiceInstaller
,ServiceProcessInstaller
或提供启动参数installutil
。但是,可以使用advapi.dll 中的某些 COM api 提供启动参数(检查左侧菜单)。可以在此处找到所需调用的完整集合。它是一个(也)被调用的类ServiceInstaller
,它包含所需的外部方法和一些实用程序方法。
You'd want to use the utility method InstallAndStart
. It accepts a service name, a display name and a path to the executable that represents your Windows service. You can call it like this:
你会想要使用实用方法InstallAndStart
。它接受服务名称、显示名称和代表 Windows 服务的可执行文件的路径。你可以这样称呼它:
InstallAndStart("MyService", "My Service For User 1",
"c:\pathtoexe\MyService.exe user1");
If you have the following service the parameter startupParam
will receive the value 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;
}
...
}
回答by Ricardo Appleton
All I wanted was to send one parameter to the service I have created. As it turns out, all you have to do is (carefully!) edit the registry at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ and add the parameter in ImagePath, after the quotes.
我想要的只是向我创建的服务发送一个参数。事实证明,您所要做的就是(小心!)编辑 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ 中的注册表,并在 ImagePath 中的引号后添加参数。
Eg. ImagePath Value Data: "C:\Program Files\myservice\myservice.exe" param1
例如。ImagePath 值数据:“C:\Program Files\myservice\myservice.exe”param1
I found the solution in this link http://social.msdn.microsoft.com/Forums/is/csharpgeneral/thread/38242afa-7e40-4c06-975e-aa97d3cc782f
我在此链接中找到了解决方案http://social.msdn.microsoft.com/Forums/is/csharpgeneral/thread/38242afa-7e40-4c06-975e-aa97d3cc782f
回答by Derrick Moeller
You can pass parameters to your installer using installutil
, for example ServiceName
and DisplayName
.
您可以通过使用参数,您的安装installutil
,例如ServiceName
和DisplayName
。
ProjectInstaller.cs
项目安装程序.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);
}
}
This will append a parameter to the path stored with the service, for example:
这会将参数附加到与服务一起存储的路径,例如:
Before: "C:\Service.exe"
之前:“C:\Service.exe”
After: "C:\Service.exe" -s"Instance 1"
之后:"C:\Service.exe" -s"实例 1"
You can then read this parameter when you start the service and pass to your services constructor.
然后,您可以在启动服务时读取此参数并将其传递给您的服务构造函数。
Program.cs
程序.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
服务.cs
public partial class Service : ServiceBase
{
public Service(string serviceName)
{
InitializeComponent();
ServiceName = serviceName;
}
}
Usage
用法
installutil /ServiceName="Instance 1" /DisplayName="Instance 1 Service" "C:\Service.exe"
installutil /ServiceName="Instance 2" /DisplayName="Instance 2 Service" "C:\Service.exe"