102

クライアント アプリケーションにデータをフィードするための Windows サービスを作成しました。クライアントは、同じサーバー上で実行され、別々のデータベースを指すように構成されたこのサービスの 2 つのインスタンスを必要とする楽しい構成要求を思いつきました。

これまでのところ、私はこれを実現することができず、仲間のスタックオーバーフロー メンバーがその理由についていくつかのヒントを提供できることを期待していました。

現在のセットアップ:

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 から次の出力を受け取りました (関連部分のみ)。

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

インストールのインストール フェーズの開始。

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

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

インストールのロールバック フェーズが開始されます。

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

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

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

十分な関連情報があることを確認したかったので、長々とした投稿で申し訳ありません。これまでのところ私が困惑しているのは、サービスのインストールが正常に完了し、NullReferenceException がスローされたように見える EventLog ソースを作成した後であると記載されていることです。だから、誰かが私が間違っていることを知っているか、より良いアプローチを持っているなら、それは大歓迎です。

4

10 に答える 10

84

sc / service controller util を試しましたか? タイプ

sc create

コマンド ラインで実行すると、ヘルプ エントリが表示されます。過去にSubversionでこれを行ったことがあり、この記事を参照として使用したと思います:

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

于 2009-08-14T18:38:47.863 に答える
20

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

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

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

3)次の内容(一意のサービス名)を使用して、Service実行可能フォルダーに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());
            }
        }
    }
}
于 2009-08-14T18:37:18.727 に答える
11

古い質問ですが、InstallUtil.exe で /servicename オプションを使用することができました。ただし、組み込みのヘルプにはリストされていません。

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

これについて最初にどこで読んだかは完全にはわかりませんが、それ以来見ていません。YMMV。

于 2013-04-22T20:25:40.717 に答える
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 バージョンを 1 台のマシンで並行して実行しました。各バージョンには独自のファイル/構成セットがあります-同じファイルセットを指す複数のサービスをインストールするためにこれを試したことはありません。

注:/ServiceSuffixサービスをアンインストールするには同じパラメーターを使用する必要があるため、次のコマンドを実行してアンインストールします。

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

于 2016-05-19T17:57:09.767 に答える
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を使用して何かを実行できる場合がありますが、私の場合、ロードされていないカスタム構成セクションを取得しようとしていたため、これは機能しませんでした。

于 2011-08-24T16:42:30.737 に答える
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";
            }
        }
于 2016-04-29T21:39:31.040 に答える