Установка нескольких экземпляров одной и той же службы 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
когда это не сработало, я попытался создать второй проект установщика, отредактировал файл конфигурации и построил второй установщик. Когда я запустил установщик, он работал нормально, но сервис не сервис.msc поэтому я побежал предыдущая команда против второй установленной базы кода.
оба раза я получил следующие выходные данные от InstallUtil (только соответствующие части):
выполнение операции установки.
начало этапа установки установки.
установить приложение два...
Служба приложение служба Два была успешно установлена.
Создание службы приложений-источников EventLog два в приложении журнала...
на этапе установки произошло исключение.
Система.Исключение NullReferenceException: ссылка на объект не указывает на экземпляр объекта.
начинается этап отката установки.
восстановление журнала событий в предыдущее состояние для исходной службы приложений два.
Служба приложение служба два удаляется из системы...
Service App Service Two была успешно удалена из системы.
фаза отката успешно.
транзакционная установка завершена.
Установка завершилась неудачно, и откат был выполнен.
извините за длинный скучный пост, хотел убедиться, что есть достаточно информации. Часть, которая до сих пор меня поставила в тупик, заключается в том, что она утверждает, что установка службы завершается успешно и ее только после того, как она идет на создание источника журнала событий, что NullReferenceException, похоже, выбрасывается. Так что если кто знает, что я делаю неправильно или имеет лучший подход, это было бы очень ценно.
10 ответов:
вы пробовали контроллер sc / service util? Типа
sc createв командной строке, и это даст вам запись помочь. Я думаю, что я сделал это в прошлом для подрывной деятельности и использовать в этой статье для справки:
http://svn.apache.org/repos/asf/subversion/trunk/notes/windows-service.txt
вы можете запустить несколько версий одной и той же службы, выполнив следующие действия:
1) скопируйте исполняемый файл службы и конфигурацию в свою собственную папку.
2) Скопировать Установить.Exe в папку с исполняемым файлом (от .чистая Framework папку)
3) Создайте конфигурационный файл с именем Install.исполняемый.конфигурация в папке исполняемого файла службы со следующим содержимым (уникальные имена служб):
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="ServiceName" value="The Service Name"/> <add key="DisplayName" value="The Service Display Name"/> </appSettings> </configuration>4) Создайте пакетный файл для установки службы с помощью следующее содержание:
REM Install InstallUtil.exe YourService.exe pause5) пока вы там, создайте пакетный файл удаления
REM Uninstall InstallUtil.exe -u YourService.exe pauseEDIT:
Примечание конечно, если я что-то пропустил, вот класс ServiceInstaller (настроить по мере необходимости):
using System.Configuration; namespace Made4Print { partial class ServiceInstaller { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; private System.ServiceProcess.ServiceInstaller FileProcessingServiceInstaller; private System.ServiceProcess.ServiceProcessInstaller FileProcessingServiceProcessInstaller; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.FileProcessingServiceInstaller = new System.ServiceProcess.ServiceInstaller(); this.FileProcessingServiceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller(); // // FileProcessingServiceInstaller // this.FileProcessingServiceInstaller.ServiceName = ServiceName; this.FileProcessingServiceInstaller.DisplayName = DisplayName; // // FileProcessingServiceProcessInstaller // this.FileProcessingServiceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem; this.FileProcessingServiceProcessInstaller.Password = null; this.FileProcessingServiceProcessInstaller.Username = null; // // ServiceInstaller // this.Installers.AddRange(new System.Configuration.Install.Installer[] { this.FileProcessingServiceInstaller, this.FileProcessingServiceProcessInstaller }); } #endregion private string ServiceName { get { return (ConfigurationManager.AppSettings["ServiceName"] == null ? "Made4PrintFileProcessingService" : ConfigurationManager.AppSettings["ServiceName"].ToString()); } } private string DisplayName { get { return (ConfigurationManager.AppSettings["DisplayName"] == null ? "Made4Print File Processing Service" : ConfigurationManager.AppSettings["DisplayName"].ToString()); } } } }
старый вопрос, я знаю, но мне повезло использовать параметр /servicename на InstallUtil.исполняемый. Однако я не вижу его в списке встроенной справки.
InstallUtil.exe /servicename="My Service" MyService.exeЯ не совсем уверен, где я впервые прочитал об этом, но я не видел с тех пор. МММ.
мне не очень повезло с вышеуказанными методами при использовании нашего программного обеспечения для автоматического развертывания для частой установки / удаления параллельных служб windows, но в конечном итоге я придумал следующее, что позволяет мне передать параметр для указания суффикса имени службы в командной строке. Он также позволяет дизайнеру функционировать должным образом и может быть легко адаптирован для переопределения всего имени, если это необходимо.
public partial class ProjectInstaller : System.Configuration.Install.Installer { protected override void OnBeforeInstall(IDictionary savedState) { base.OnBeforeInstall(savedState); SetNames(); } protected override void OnBeforeUninstall(IDictionary savedState) { base.OnBeforeUninstall(savedState); SetNames(); } private void SetNames() { this.serviceInstaller1.DisplayName = AddSuffix(this.serviceInstaller1.DisplayName); this.serviceInstaller1.ServiceName = AddSuffix(this.serviceInstaller1.ServiceName); } private string AddSuffix(string originalName) { if (!String.IsNullOrWhiteSpace(this.Context.Parameters["ServiceSuffix"])) return originalName + " - " + this.Context.Parameters["ServiceSuffix"]; else return originalName; } }имея это в виду, я могу сделать следующий: Если я назвал сервис "Awesome Service", то я могу установить UAT-версию сервиса следующим образом:
InstallUtil.exe /ServiceSuffix="UAT" MyService.exeэто создаст сервис с именем "Awesome Service-UAT". Мы использовали это для запуска версий DEVINT, тестирования и принятия одной и той же службы, работающей бок о бок на одной машине. Каждая версия имеет свой собственный набор файлов / конфигураций - я не пробовал это, чтобы установить несколько служб, указывающих на один и тот же набор файлы.
Примечание: Вы должны использовать то же самое
/ServiceSuffixпараметр для удаления службы, поэтому вы должны выполнить следующее Для удаления:
InstallUtil.exe /u /ServiceSuffix="UAT" MyService.exe
что я сделал, чтобы сделать эту работу, чтобы сохранить имя и отображаемое имя в приложении.конфигурация для моей службы. Затем в моем классе установщика я загружаю приложение.сконфигурируйте как XmlDocument и используйте xpath, чтобы получить значения и применить их к ServiceInstaller.ServiceName и ServiceInstaller.DisplayName, перед вызовом метода InitializeComponent (). Это предполагает, что вы еще не устанавливаете эти свойства в InitializeComponent (), и в этом случае параметры из вашего файла конфигурации будут проигнорированы. Следующий код-это то, что я вызываю из моего конструктора класса установщика, перед InitializeComponent ():
private void SetServiceName() { string configurationFilePath = Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, "exe.config"); XmlDocument doc = new XmlDocument(); doc.Load(configurationFilePath); XmlNode serviceName = doc.SelectSingleNode("/xpath/to/your/@serviceName"); XmlNode displayName = doc.SelectSingleNode("/xpath/to/your/@displayName"); if (serviceName != null && !string.IsNullOrEmpty(serviceName.Value)) { this.serviceInstaller.ServiceName = serviceName.Value; } if (displayName != null && !string.IsNullOrEmpty(displayName.Value)) { this.serviceInstaller.DisplayName = displayName.Value; } }Я не верю в чтение файла конфигурации непосредственно из ConfigurationManager.AppSettings или что-то подобное будет работать, как при запуске установщика, он запускается в контексте InstallUtil.exe, а не ваш сервис .исполняемый. Вы можете быть в состоянии сделать что-то с конфигурации.OpenExeConfiguration, однако в моем случае это не сработало, поскольку я пытался добраться раздел пользовательской конфигурации, который не был загружен.
просто чтобы улучшить идеальный ответ @chris.дом.00 этой, вы можете рассмотреть следующие функции для чтения из настроек вашего приложения:
public void GetServiceAndDisplayName(out string serviceNameVar, out string displayNameVar) { string configurationFilePath = Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, "exe.config"); XmlDocument doc = new XmlDocument(); doc.Load(configurationFilePath); XmlNode serviceName = doc.SelectSingleNode("//appSettings//add[@key='ServiceName']"); XmlNode displayName = doc.SelectSingleNode("//appSettings//add[@key='DisplayName']"); if (serviceName != null && (serviceName.Attributes != null && (serviceName.Attributes["value"] != null))) { serviceNameVar = serviceName.Attributes["value"].Value; } else { serviceNameVar = "Custom.Service.Name"; } if (displayName != null && (displayName.Attributes != null && (displayName.Attributes["value"] != null))) { displayNameVar = displayName.Attributes["value"].Value; } else { displayNameVar = "Custom.Service.DisplayName"; } }
еще один быстрый способ задать пользовательское значение
ServiceNameиDisplayNameиспользуетinstallutilпараметры командной строки.
в своем
ProjectInstallerкласс переопределяет виртуальные методыInstall(IDictionary stateSaver)иUninstall(IDictionary savedState)public override void Install(System.Collections.IDictionary stateSaver) { GetCustomServiceName(); base.Install(stateSaver); } public override void Uninstall(System.Collections.IDictionary savedState) { GetCustomServiceName(); base.Uninstall(savedState); } //Retrieve custom service name from installutil command line parameters private void GetCustomServiceName() { string customServiceName = Context.Parameters["servicename"]; if (!string.IsNullOrEmpty(customServiceName)) { serviceInstaller1.ServiceName = customServiceName; serviceInstaller1.DisplayName = customServiceName; } }- создайте свой проект
установите сервис с помощью
installutilдобавление пользовательского имени с помощью :installutil.exe /servicename="CustomServiceName" "c:\pathToService\SrvcExecutable.exe"обратите внимание, что если не указать
/servicenameв командной строке служба будет установлена со значениями ServiceName и DisplayName, указанными в свойствах ProjectInstaller / config
У меня была аналогичная ситуация, когда мне нужно было иметь предыдущую службу и обновленную службу, работающую бок о бок на одном сервере. (Это было больше, чем просто изменение базы данных, это были изменения кода, а также). Так что я не мог просто бежать так же .exe в два раза. Мне нужна была новая .exe, который был скомпилирован с новыми DLL, но из того же проекта. Просто изменение имени службы и отображаемого имени службы не сработало для меня, я все еще получил "уже существующую ошибку службы", которую я считаю потому что я использую проект развертывания. Что, наконец, сработало для меня, в моих свойствах проекта развертывания есть свойство под названием "ProductCode", которое является Guid.
после этого перестроить проект установки на новый .exe или .msi установлен успешно.
самый простой подход заключается в том, что имя службы основано на имени dll:
string sAssPath = System.Reflection.Assembly.GetExecutingAssembly().Location; string sAssName = System.IO.Path.GetFileNameWithoutExtension(sAssPath); if ((this.ServiceInstaller1.ServiceName != sAssName)) { this.ServiceInstaller1.ServiceName = sAssName; this.ServiceInstaller1.DisplayName = sAssName; }

Comments