서버에 동일한 Windows 서비스의 여러 인스턴스 설치


96

그래서 우리는 클라이언트 응용 프로그램에 데이터를 공급하는 Windows 서비스를 생성했으며 모든 것이 잘 진행되고 있습니다. 클라이언트는 동일한 서버에서 실행되고 별도의 데이터베이스를 가리 키도록 구성된이 서비스의 두 인스턴스가 필요한 재미있는 구성 요청을 내놓았습니다.

지금까지 나는 이런 일이 일어나지 않았고 동료 stackoverflow 구성원이 이유에 대한 힌트를 줄 수 있기를 바랐습니다.

현재 설정 :

Windows 서비스가 포함 된 프로젝트를 설정했습니다. 이제부터는 AppService라고 부르겠습니다. App.config의 키를 기반으로 서비스 이름을 설정하는 사용자 지정 설치 단계를 처리하는 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에서 다음 출력을 받았습니다 (관련 부품 만 해당).

트랜잭션 된 설치 실행.

설치의 설치 단계를 시작합니다.

서비스 App Service Two 설치 중 ... Service App Service Two가 성공적으로 설치되었습니다. 로그 애플리케이션에서 EventLog 소스 App Service Two를 만드는 중 ...

설치 단계 중에 예외가 발생했습니다. System.NullReferenceException : 개체 참조가 개체의 인스턴스로 설정되지 않았습니다.

설치의 롤백 단계가 시작됩니다.

소스 App Service Two의 이벤트 로그를 이전 상태로 복원합니다. 서비스 앱 서비스 2가 시스템에서 제거되고 있습니다 ... 서비스 앱 서비스 2가 시스템에서 성공적으로 제거되었습니다.

롤백 단계가 성공적으로 완료되었습니다.

거래 된 설치가 완료되었습니다. 설치에 실패했으며 롤백이 수행되었습니다.

긴 게시물에 대해 죄송합니다. 관련 정보가 충분한 지 확인하고 싶었습니다. 지금까지 저를 당황하게 만든 부분은 서비스 설치가 성공적으로 완료되고 NullReferenceException이 발생하는 것처럼 보이는 EventLog 소스를 생성 한 후에야 만 서비스 설치가 완료되었다는 것입니다. 그래서 누군가 내가 뭘 잘못하고 있는지 알고 있거나 더 나은 접근 방식을 가지고 있다면 대단히 감사하겠습니다.

답변:


81

sc / service controller util을 사용해 보셨습니까? 유형

sc create

명령 줄에서 도움말 항목을 제공합니다. 이전에 Subversion에 대해이 작업을 수행했으며이 기사 를 참조로 사용했다고 생각합니다 .

http://svn.apache.org/repos/asf/subversion/trunk/notes/windows-service.txt


5
이 페이지가 유용하다는 것을 알았습니다 : http://journalofasoftwaredev.wordpress.com/2008/07/16/multiple-instances-of-same-windows-service/. installutil을 실행할 때 원하는 서비스 이름을 가져 오기 위해 설치 프로그램에 코드를 삽입 할 수 있습니다.
Vivian River

9
wordpress 블로그에 대한 링크가 다음으로 변경되었습니다. journalofasoftwaredev.wordpress.com/2008/07
STLDev

21
  sc create [servicename] binpath= [path to your exe]

이 솔루션은 저에게 효과적이었습니다.


5
지적하기 위해; [path to your exe]전체 경로 여야하며 이후 공간을 잊지 마십시오binpath=
mkb

2
실제로 서비스를 여러 번 설치할 수 있습니다. 그러나 서비스 설치 프로그램에서 제공하는 모든 정보. 등 철 설명, 로그온 유형은 무시됩니다
노엘 년 Widmer을

20

다음을 수행하여 동일한 서비스의 여러 버전을 실행할 수 있습니다.

1) 서비스 실행 파일 및 구성을 자체 폴더에 복사하십시오.

2) Install.Exe를 서비스 실행 폴더 (.net framework 폴더에서)로 복사합니다.

3) 다음 내용 (고유 서비스 이름)으로 서비스 실행 폴더에 Install.exe.config라는 구성 파일을 만듭니다.

<?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
pause

5) 거기에있는 동안 제거 배치 파일을 만듭니다.

REM Uninstall
InstallUtil.exe -u YourService.exe
pause

편집하다:

내가 뭔가를 놓친 경우 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());
            }
        }
    }
}

나는 당신이 설명하는 것이 내 서비스 app.config에서 ServiceName과 DisplayName을 설정하도록 허용함으로써 내가 한 일이라고 생각합니다. 설명 한 것을 시도했지만 불행히도 내 질문에 나열된 것과 동일한 문제가 발생했습니다.
Switters

저는 제가 사용하는 템플릿을 가지고 있는데, 제가 오랫동안 사용해 왔던 템플릿을 놓쳤을 수도 있습니다. ServiceInstaller 클래스는 어떤 모습인지, 제가 사용하는 작업 복사본을 게시 할 것입니다. 도움이 되었습니까?
Mark Redman

우리의 서비스 설치 프로그램은 실제로 거의 동일합니다. 정적 클래스를 사용하여 구성 파일에서 서비스 및 표시 이름을로드하지만 그 외에는 매우 유사합니다. 왜 그것이 나를 위해 작동하지 않는지에 대한 나의 추측은 우리 서비스 코드에 대해 약간 특이한 것이있을 수 있다는 것입니다. 안타깝게도 많은 사람이 손을 댔습니다. 내가 이해하는 바에 따르면 대부분의 경우 도움을 주셔서 감사합니다.
Switters

2
큰 도움 감사합니다. 설치 구성 파일의 이름은 InstallUtil.exe의 Install.exe.config가 아닌 InstallUtil.exe.confg 여야한다고 생각합니다.
NullReference

완전히 작동하는 좋은 접근 방식입니다. 즉, 설치 폴더에 복사 할 InstallUtil.exe를 알고있는 경우입니다 (개인적으로 64 비트 복사본으로 인해 악화되는 수많은 프레임 워크 버전이 설치되어 있습니다). 이로 인해 헬프 데스크 팀이 설치를 수행하는 경우 설명하기가 매우 어렵습니다. 그러나 개발자 주도 설치의 경우 매우 우아합니다.
timmi4sa 2014 년

11

이전 질문은 알고 있지만 InstallUtil.exe에서 / servicename 옵션을 사용하여 운이 좋았습니다. 그래도 기본 제공 도움말에 나와 있지 않습니다.

InstallUtil.exe /servicename="My Service" MyService.exe

나는 이것에 대해 처음 읽은 곳이 확실하지 않지만 그 이후로는 보지 못했습니다. YMMV.


3
이 오류를 반환합니다.An exception occurred during the Install phase. System.ComponentModel.Win32Exception: The specified service already exists
mkb

@mkb "내 서비스"라는 다른 서비스가 있습니까?
Jonathon Watney

예, 질문에서와 같이 하나의 서비스, 동일한 실행 파일이 있지만 각각 다른 구성으로 두 개의 인스턴스를 설치하고 싶습니다. 서비스 exe를 복사하여 붙여 넣었지만 작동하지 않았습니다.
mkb

1
/ servicename = "My Service InstanceOne"및 / servicename = "My Service InstanceTwo"이름은 고유해야합니다.
granadaCoder

11

ServiceName및에 대한 사용자 지정 값을 지정하는 또 다른 빠른 방법 DisplayNameinstallutil명령 줄 매개 변수를 사용하는 것입니다.

  1. 당신의에 ProjectInstaller클래스의 가상 메소드를 오버라이드 (override) 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;
        }
    }
  2. 프로젝트 구축
  3. 매개 변수를 installutil사용하여 사용자 정의 이름 을 추가하여 서비스를 설치하십시오 /servicename.

    installutil.exe /servicename="CustomServiceName" "c:\pathToService\SrvcExecutable.exe"
    

/servicename명령 줄에 지정하지 않으면 ProjectInstaller 속성 / 구성에 지정된 ServiceName 및 DisplayName 값으로 서비스가 설치됩니다.


2
훌륭한!! 감사합니다-이것은 정확히 필요한 것이었고 요점이었습니다.
Iofacture

7

자동 배포 소프트웨어를 사용하여 side-by-side 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, TESTING 및 ACCEPTANCE 버전을 실행했습니다. 각 버전에는 고유 한 파일 / 구성 세트가 있습니다. 동일한 파일 세트를 가리키는 여러 서비스를 설치하려고 시도한 적이 없습니다.

참고 : /ServiceSuffix서비스를 제거 하려면 동일한 매개 변수 를 사용해야 하므로 제거하려면 다음을 실행합니다.

InstallUtil.exe /u /ServiceSuffix="UAT" MyService.exe


훌륭하지만 설치자에게만 해당됩니다. 새 인스턴스 이름이 있으면 Windows 서비스에서이 새 이름을 어떻게 알 수 있습니까? Windows 서비스를 구축 할 때 전달해야합니까?
progLearner

감사! 설치 프로그램은 위의 SetNames () 메서드에 설정된 값을 사용하여 설치하는 동안 Windows 서비스에 이름을 설정합니다.
tristankoffee

물론입니다.하지만 외부 세계에서이 이름을 어떻게 설정할 수 있습니까?
progLearner

내 대답에는 외부 세계에서 서비스를 설치 (및 제거)하기 위해 명령 줄에서 사용되는 명령이 있습니다. 전달한 값 /ServiceSuffix="UAT"은 설치 프로그램에서 서비스에 접미사를 설정하는 데 사용됩니다. 내 예에서 전달 된 값은 UAT입니다. 내 시나리오에서는 서비스의 기존 이름에 접미사를 추가하고 싶었지만 이름을 전달 된 값으로 완전히 대체하도록이를 조정하지 못할 이유가 없습니다.
tristankoffee

감사합니다. 그러나 이것은 코드가 아닌 명령 줄 입력 (= 수동 입력)입니다. 원래 질문에 따르면 : 새 인스턴스 이름이 있으면 Windows 서비스에서이 새 이름에 대해 어떻게 알 수 있습니까? Windows 서비스를 구축 할 때 전달해야합니까?
progLearner

4

이 작업을 수행하기 위해 내가 한 것은 서비스 이름과 표시 이름을 내 서비스의 app.config에 저장하는 것입니다. 그런 다음 설치 프로그램 클래스에서 app.config를 XmlDocument로로드하고 xpath를 사용하여 값을 가져 와서 InitializeComponent ()를 호출하기 전에 ServiceInstaller.ServiceName 및 ServiceInstaller.DisplayName에 적용합니다. 이는 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 등에서 직접 구성 파일을 읽는 것이 설치 프로그램이 실행될 때 서비스의 .exe가 아닌 InstallUtil.exe의 컨텍스트에서 실행되는 것처럼 작동한다고 생각하지 않습니다. ConfigurationManager.OpenExeConfiguration으로 뭔가를 할 수 있지만 제 경우에는로드되지 않은 사용자 지정 구성 섹션을 가져 오려고했기 때문에이 작업이 작동하지 않았습니다.


안녕하세요 크리스 하우스! Quartz.NET 스케줄러를 중심으로 자체 호스팅 OWIN 기반 웹 API를 구축하고 Windows 서비스에 고정하기 때문에 답변을 우연히 발견했습니다. 꽤 매끄러운! 잘 지내길 바래요!
NovaJoe

안녕하세요 크리스 하우스! Quartz.NET 스케줄러를 중심으로 자체 호스팅 OWIN 기반 웹 API를 구축하고 Windows 서비스에 고정하기 때문에 답변을 우연히 발견했습니다. 꽤 매끄러운! 잘 지내길 바래요!
NovaJoe 2015 년

4

@ chris.house.00 this 의 완벽한 답변을 개선하기 위해 앱 설정에서 읽을 다음 기능을 고려할 수 있습니다.

 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";
            }
        }

2

나는 이전 서비스가 필요하고 동일한 서버에서 나란히 실행되는 업데이트 된 서비스가 필요한 비슷한 상황이있었습니다. (데이터베이스 변경 이상이었고 코드 변경도 마찬가지였습니다.) 그래서 동일한 .exe를 두 번 실행할 수 없었습니다. 새 DLL로 컴파일되었지만 동일한 프로젝트에서 생성 된 새 .exe가 필요했습니다. 서비스 이름과 서비스의 표시 이름을 변경하는 것만으로도 효과가 없었습니다. 여전히 배포 프로젝트를 사용하고 있기 때문에 "서비스가 이미 존재했습니다"라는 오류 메시지가 나타납니다. 마침내 나를 위해 일한 것은 내 배포 프로젝트 속성 내에 Guid 인 "ProductCode"라는 속성이 있습니다.

여기에 이미지 설명 입력

그런 다음 설치 프로젝트를 성공적으로 설치된 새 .exe 또는 .msi로 다시 빌드하십시오.


1

가장 간단한 방법은 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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.