サーバーに同じWindowsサービスの複数のインスタンスをインストールする


96

そこで、クライアントアプリケーションにデータを供給するWindowsサービスを作成し、すべてが順調に進んでいます。クライアントは、同じサーバー上で実行され、別々のデータベースを指すように構成されたこのサービスの2つのインスタンスを必要とする楽しい構成要求を考え出しました。

これまでのところ、これを実現することはできず、仲間のstackoverflowメンバーがその理由についていくつかのヒントを与えることができることを望んでいました。

現在の設定:

Windowsサービスを含むプロジェクトを設定しました。これをAppServiceと呼び、カスタムインストール手順を処理するProjectInstaller.csファイルを使用して、App.configのキーに基づいてサービス名を設定します。 :

this.serviceInstaller1.ServiceName = Util.ServiceName;
this.serviceInstaller1.DisplayName = Util.ServiceName;
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;

この場合、Utilは静的クラスであり、構成ファイルからサービス名をロードします。

ここからは、両方のサービスをインストールするために2つの異なる方法を試しましたが、どちらも同じ方法で失敗しました。

最初の方法は、サービスの最初のコピーをインストールし、インストールされたディレクトリをコピーして名前を変更し、目的のサービス名を変更するようにアプリ構成を変更した後、次のコマンドを実行することでした。

InstallUtil.exe /i AppService.exe

それがうまくいかなかったとき、私は2番目のインストーラープロジェクトを作成しようとし、設定ファイルを編集して、2番目のインストーラーをビルドしました。インストーラーを実行したところ、正常に機能しましたが、services.mscにサービスが表示されなかったため、前のコマンドを2番目にインストールされたコードベースに対して実行しました。

どちらの場合も、InstallUtilから次の出力を受け取りました(関連パーツのみ)。

トランザクションインストールの実行。

インストールのインストール段階を開始します。

Service App Service Twoをインストールしています... Service App Service Twoが正常にインストールされました。EventLog source App Service Twoをログアプリケーションに作成しています...

インストール段階で例外が発生しました。System.NullReferenceException:オブジェクト参照がオブジェクトのインスタンスに設定されていません。

インストールのロールバック段階が始まります。

ソースApp Service 2のイベントログを以前の状態に復元します。Service App Service Twoがシステムから削除されています... Service App Service Twoはシステムから正常に削除されました。

ロールバックフェーズが正常に完了しました。

トランザクションによるインストールが完了しました。インストールが失敗し、ロールバックが実行されました。

投稿が長くてごめんなさい、十分な関連情報があることを確認したかったのです。これまでのところ困惑しているのは、サービスのインストールが正常に完了したことと、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の実行時に必要なサービス名を取得できます。
ビビアンリバー

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
これにより、サービスを複数回インストールできるようになります。ただし、サービスインストーラーによって提供されるすべての情報。Feの説明、ログオンタイプなどは無視される
Noel Widmer

20

次の操作を行うと、同じサービスの複数のバージョンを実行できます。

1)サービスの実行可能ファイルと構成を独自のフォルダーにコピーします。

2)Install.Exeを(.netフレームワークフォルダーから)サービス実行可能フォルダーにコピーします。

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());
            }
        }
    }
}

あなたが説明しているのは多かれ少なかれ、ServiceNameとDisplayNameをサービスのapp.configから設定できるようにしたことですが、残念ながら、私の質問には同じ問題が発生しています。
スウィッターは、2009

私は古くから使用しているテンプレートを使用しているので、おそらく何かを見逃しました。ServiceInstallerクラスはどのように見えますか。使用しているテンプレートの作業用コピーを投稿します。これが役立つことを教えてください。
マークレッドマン

私たちのサービスインストーラーは実際にはほとんど同じです。静的クラスを使用して構成ファイルからサービスと表示名を読み込みますが、それ以外は非常によく似ています。なぜそれが機能しないのかについての私の推測は、私たちのサービスコードに少し変わっているかもしれないということです。残念ながら多くの人がそれに取り組んでいます。しかし私が理解していることから、あなたの答えは大部分のケースで助けになるはずです。
スウィッター、2009

2
大きな助けに感謝します。インストール構成ファイルの名前は、InstallUtil.exeのInstall.exe.configではなくInstallUtil.exe.confgにする必要があると思います
NullReference

完全に機能する素晴らしいアプローチ。これは、インストールフォルダーにコピーするInstallUtil.exeがわかっている場合です(私は個人的に、64ビットのコピーによって悪化する大量のフレームワークバージョンをインストールしています)。これは、ヘルプデスクチームがインストールを行う場合、説明するのが非常に難しくなるでしょう。しかし、開発者主導のインストールの場合は非常にエレガントです。
timmi4sa 14年

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「My Service」という別のサービスはありますか?
Jonathon Watney

はい、質問のように、1つのサービス、同じ実行可能ファイルがありますが、それぞれの構成が異なる2つのインスタンスをインストールします。サービスexeをコピーして貼り付けましたが、これは機能しませんでした。
mkb、2016年

1
/ servicename = "My Service InstanceOne"および/ servicename = "My Service InstanceTwo"名前は一意である必要があります。
granadaCoder 2016

11

カスタム値を指定するもう一つの簡単な方法ServiceNameDisplayName使用しているinstallutilコマンドラインパラメータを。

  1. あなたには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;
        }
    }
  2. プロジェクトを構築する
  3. パラメータinstallutilを使用してカスタム名を追加してサービスをインストールし/servicenameます。

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

/servicenameコマンドラインで指定しない場合、サービスはProjectInstallerプロパティ/構成で指定されたServiceNameとDisplayNameの値でインストールされます。


2
鮮やかさ!!ありがとう-これはまさに必要とされていたものでした。
Iofacture

7

自動展開ソフトウェアを使用してサイドバイサイドの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を使用して値を取得し、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などから構成ファイルを直接読み取ると、インストーラーの実行時にサービスの.exeではなくInstallUtil.exeのコンテキストで実行されるように動作するとは思わない。あなたはConfigurationManager.OpenExeConfigurationで何かをすることができるかもしれませんが、私の場合、読み込まれなかったカスタム構成セクションを取得しようとしていたため、これは機能しませんでした。


こんにちは、クリス・ハウス!私はQuartz.NETスケジューラーの周りに自己ホスト型のOWINベースのWeb APIを構築し、それをWindowsサービスに貼り付けているため、あなたの答えに出くわしました。かなり滑らかです!あなたが元気であることを願っています!
NovaJoe、2015年

こんにちは、クリス・ハウス!私はQuartz.NETスケジューラーの周りに自己ホスト型のOWINベースのWeb 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を2回実行することはできませんでした。新しい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.