在服务器上安装同一 Windows 服务的多个实例
因此,我们创建了一个 Windows 服务来将数据提供给我们的客户端应用程序,一切都很顺利。 客户端提出了一个有趣的配置请求,该请求需要在同一服务器上运行该服务的两个实例,并配置为指向不同的数据库。
到目前为止,我还没有能够实现这一点,并希望我的 stackoverflow 成员能够给出一些关于原因的提示。
当前设置:
我已经设置了包含 Windows 服务的项目,从现在起我们将其称为 AppService,以及处理自定义安装步骤以根据应用程序中的键设置服务名称的 ProjectInstaller.cs 文件。像这样配置:
this.serviceInstaller1.ServiceName = Util.ServiceName;
this.serviceInstaller1.DisplayName = Util.ServiceName;
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
在这种情况下,Util 只是一个静态类,它从配置文件加载服务名称。
从这里开始,我尝试了两种不同的方法来安装这两个服务,但都以相同的方式失败了。
第一种方法是简单地安装服务的第一个副本,复制安装的目录并重命名它,然后在修改应用程序配置后运行以下命令以更改所需的服务名称:
InstallUtil.exe /i AppService.exe
当这不起作用时,我尝试创建第二个安装程序项目,编辑配置文件并构建第二个安装程序。 当我运行安装程序时,它工作正常,但该服务没有显示在 services.msc 中,因此我针对第二个安装的代码库运行了上一个命令。
两次我都收到了 InstallUtil 的以下输出(仅相关部分):
运行事务安装。
开始安装的安装阶段。
正在安装服务应用服务二... 服务应用服务二已成功安装。 在日志应用程序中创建EventLog源应用程序服务二...
安装阶段发生异常。 System.NullReferenceException:未将对象引用设置为对象的实例。
安装的回滚阶段即将开始。
将源应用服务二的事件日志恢复到之前的状态。 正在从系统中删除服务应用程序服务二... 服务应用服务二已成功从系统中删除。
回滚阶段成功完成。
事务安装已完成。 安装失败,已回滚。
很抱歉这篇冗长的文章,想确保有足够的相关信息。 到目前为止让我感到困惑的是,它指出服务的安装成功完成,并且只有在创建 EventLog 源之后,NullReferenceException 似乎才会被抛出。 因此,如果有人知道我做错了什么或有更好的方法,我将不胜感激。
So we've produced a windows service to feed data to our client application and everything is going great. The client has come up with a fun configuration request that requires two instances of this service running on the same server and configured to point at separate databases.
So far I haven't been able to get this to happen and was hoping my fellow stackoverflow members might be able to give some hints as to why.
Current setup:
I've set up the project that contains the windows service, we'll call it AppService from now on, and the ProjectInstaller.cs file that handles custom installation steps to set the service name based on a key in the App.config like so:
this.serviceInstaller1.ServiceName = Util.ServiceName;
this.serviceInstaller1.DisplayName = Util.ServiceName;
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
In this case Util is just a static class tha tloads the service name from the config file.
From here forward I have tried two different ways to get both services installed and both have failed in an identical way.
The first way was to simply install the first copy of the service, copy the installed directory and renamed it, and then ran the following command after modifying the app config to change the desired service name:
InstallUtil.exe /i AppService.exe
When that didn't work I tried to create a second installer project, edited the config file and built the second installer. When I ran the installer it worked fine but the service did not show up in services.msc so I ran the previous command against the second installed code base.
Both times i received the following output from InstallUtil (relevant parts only):
Running a transacted installation.
Beginning the Install phase of the installation.
Installing service App Service Two...
Service App Service Two has been successfully installed.
Creating EventLog source App Service Two in log Application...An exception occurred during the Install phase.
System.NullReferenceException: Object reference not set to an instance of an object.The Rollback phase of the installation is beginning.
Restoring event log to previous state for source App Service Two.
Service App Service Two is being removed from the system...
Service App Service Two was successfully removed from the system.The Rollback phase completed successfully.
The transacted install has completed.
The installation failed, and the rollback has been performed.
Sorry for the long winded post, wanted to make sure there is enough relevant information. The piece that so far has me stumped is that it states that the installation of the service completes successfully and its only after it goes to create the EventLog source that the NullReferenceException seems to get thrown. So if anyone knows what I'm doing wrong or has a better approach it would be much appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
您尝试过 sc / 服务控制器 util 吗? 在命令行中键入
,它将为您提供帮助条目。 我想我过去已经为 Subversion 做过这个并使用 本文作为参考:
http: //svn.apache.org/repos/asf/subversion/trunk/notes/windows-service.txt
Have you tried the sc / service controller util? Type
at a command line, and it will give you the help entry. I think I've done this in the past for Subversion and used this article as a reference:
http://svn.apache.org/repos/asf/subversion/trunk/notes/windows-service.txt
这个解决方案对我有用。
This solution worked for me.
您可以通过执行以下操作来运行同一服务的多个版本:
1) 将服务可执行文件和配置复制到其自己的文件夹中。
2) 将 Install.Exe 复制到服务可执行文件夹(来自 .net Framework 文件夹)
3) 在服务可执行文件夹中创建一个名为 Install.exe.config 的配置文件
包含以下内容(唯一的服务名称):
4)创建一个批处理文件来安装包含以下内容的服务:
5)当您在那里时,创建一个卸载批处理文件
编辑:
请注意,如果我错过了一些东西,这是 ServiceInstaller 类(根据需要调整):
You can run multiple versions of the same service by doing the following:
1) Copy the Service executable and config to its own folder.
2) Copy Install.Exe to the service executable folder (from .net framework folder)
3) Create a config file called Install.exe.config in the service executable folder
with the following contents (unique service names):
4) Create a batch file to install the service with the following contents:
5) While your there, create an uninstall batch file
EDIT:
Note sure if I missed something, here is the ServiceInstaller Class (adjust as required):
为
ServiceName
和DisplayName
指定自定义值的另一种快速方法是使用installutil
命令行参数。在您的
ProjectInstaller
类中重写虚拟方法Install(IDictionary stateSaver)
和Uninstall(IDictionary savingState)
使用
installutil
安装服务,并使用/servicename
参数添加您的自定义名称:请注意,如果您不指定
/servicename< /code> 在命令行中,服务将使用 ProjectInstaller 属性/配置中指定的 ServiceName 和 DisplayName 值进行安装
Another quick way to specify a custom value for
ServiceName
andDisplayName
is usinginstallutil
command line parameters.In your
ProjectInstaller
class override virtual methodsInstall(IDictionary stateSaver)
andUninstall(IDictionary savedState)
Install the service with
installutil
adding your custom name using/servicename
parameter:Please note that if you do not specify
/servicename
in the command line the service will be installed with ServiceName and DisplayName values specified in ProjectInstaller properties/config老问题,我知道,但我很幸运地使用了 InstallUtil.exe 上的 /servicename 选项。 但我没有看到它在内置帮助中列出。
我不太确定我第一次读到这篇文章的地方,但从那以后我就再也没有见过它。 YMMV。
Old question, I know, but I've had luck using the /servicename option on InstallUtil.exe. I don't see it listed in the built-in help though.
I'm not entirely sure where I first read about this but I haven't seen it since. YMMV.
当使用我们的自动化部署软件频繁安装/卸载并行 Windows 服务时,我对上述方法没有太多运气,但我最终想出了以下方法,它允许我传入一个参数来指定后缀命令行上的服务名称。 它还允许设计者正常工作,并且可以在必要时轻松修改以覆盖整个名称。
考虑到这一点,我可以执行以下操作:
如果我将服务称为“Awesome Service”,那么我可以安装该服务的 UAT 版本,如下所示:
InstallUtil.exe /ServiceSuffix="UAT" MyService.exe
这将使用以下内容创建服务:名称“很棒的服务-UAT”。 我们使用它来运行在一台机器上并行运行的同一服务的 DEVINT、TESTING 和 ACCEPTANCE 版本。 每个版本都有自己的一组文件/配置 - 我还没有尝试安装指向同一组文件的多个服务。
注意:您必须使用相同的
/ServiceSuffix
参数来卸载服务,因此您需要执行以下命令来卸载:InstallUtil.exe /u /ServiceSuffix="UAT" MyService.exe
I didn't have much luck with the above methods when using our automated deployment software to frequently install/uninstall side-by-side windows services, but I eventually came up with the following which allows me to pass in a parameter to specify a suffix to the service name on the command line. It also allows the designer to function properly and could easily be adapted to override the entire name if necessary.
With this in mind, I can do the following:
If I've called the service "Awesome Service" then I can install a UAT verison of the service as follows:
InstallUtil.exe /ServiceSuffix="UAT" MyService.exe
This will create the service with the name "Awesome Service - UAT". We've used this to run DEVINT, TESTING and ACCEPTANCE versions of the same service running side-by-side on a single machine. Each version has its own set of files/configs - I haven't tried this to install multiple services pointing at the same set of files.
NOTE: you have to use the same
/ServiceSuffix
parameter to uninstall the service, so you'd execute the following to uninstall:InstallUtil.exe /u /ServiceSuffix="UAT" MyService.exe
为了完成这项工作,我所做的是将服务名称和显示名称存储在我的服务的 app.config 中。 然后在我的安装程序类中,我将 app.config 作为 XmlDocument 加载,并使用 xpath 获取值并将它们应用到 ServiceInstaller.ServiceName 和 ServiceInstaller.DisplayName,然后再调用 InitializeComponent()。 这假设您尚未在 InitializeComponent() 中设置这些属性,在这种情况下,配置文件中的设置将被忽略。 以下代码是我在 InitializeComponent() 之前从安装程序类构造函数中调用的代码:
我不相信直接从 ConfigurationManager.AppSettings 或类似的内容读取配置文件会像安装程序运行时一样工作,它是在上下文中运行的InstallUtil.exe 的,而不是服务的 .exe。 您也许可以使用 ConfigurationManager.OpenExeConfiguration 执行某些操作,但是就我而言,这不起作用,因为我试图获取未加载的自定义配置部分。
What I've done to make this work is to store the service name and display name in an app.config for my service. Then in my installer class, I load the app.config as an XmlDocument and use xpath to get the values out and apply them to ServiceInstaller.ServiceName and ServiceInstaller.DisplayName, before calling InitializeComponent(). This assumes you're not already setting these properties in InitializeComponent(), in which case, the settings from your config file will be ignored. The following code is what I'm calling from my installer class constructor, before InitializeComponent():
I don't believe reading the configuration file directly from ConfigurationManager.AppSettings or something similar will work as when the installer runs, it's run in the context of InstallUtil.exe, not your service's .exe. You may be able to do something with ConfigurationManager.OpenExeConfiguration, however in my case, this didn't work as I was trying to get at a custom configuration section that was not loaded.
为了提高@chris.house.00 这个的完美答案,您可以考虑使用以下函数来从您的应用设置中读取:
Just to improve perfect answer of @chris.house.00 this, you can consider following function to read from your app settings:
我遇到了类似的情况,我需要有一个以前的服务,以及一个更新的服务在同一台服务器上并行运行。 (这不仅仅是数据库更改,也是代码更改)。 所以我不能只运行同一个 .exe 两次。 我需要一个新的 .exe,它是用新的 DLL 编译的,但来自同一个项目。 仅更改服务名称和服务的显示名称对我来说不起作用,我仍然收到“服务已存在错误”,我认为这是因为我正在使用部署项目。 最终对我有用的是我的部署项目属性中有一个名为“ProductCode”的属性,它是一个 Guid。
之后,将安装项目重建为新的 .exe 或 .msi,安装成功。
I had a similar situation, where i to needed have a previous service, and an updated service running side by side on the same server. (It was more than just a database change, it was code changes as well). So I couldn't just run the same .exe twice. I needed a new .exe that was compiled with new DLLs but from the same project. Just changing the service name and display name of the service did not work for me, I still received the "service already existed error" which I believe is because I am using a Deployment Project. What finally did work for me is within my Deployment Project Properties there is a property called "ProductCode" which is a Guid.
After that, rebuilding the Setup Project to a new .exe or .msi installed successfully.
最简单的方法是基于 dll 名称的服务名称:
The simplest approach is is based the service name on the dll name: