Aynı Windows hizmetinin birden çok örneğini bir sunucuya yükleme


96

Bu nedenle, müşteri uygulamamıza veri beslemek için bir Windows hizmeti ürettik ve her şey harika gidiyor. İstemci, bu hizmetin aynı sunucuda çalışan ve ayrı veritabanlarını işaret edecek şekilde yapılandırılmış iki örneğini gerektiren eğlenceli bir yapılandırma isteği ile geldi.

Şimdiye kadar bunu gerçekleştiremedim ve yığın aşımı üyelerimin neden olduğuna dair bazı ipuçları verebileceğini umuyordum.

Mevcut kurulum:

Windows hizmetini içeren projeyi kurdum, bundan sonra buna AppService adını vereceğiz ve hizmet adını App.config içindeki bir anahtara dayalı olarak ayarlamak için özel kurulum adımlarını işleyen ProjectInstaller.cs dosyası gibi. :

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

Bu durumda Util, yapılandırma dosyasından hizmet adını yükleyen statik bir sınıftır.

Bundan sonra, her iki hizmeti de kurmak için iki farklı yol denedim ve her ikisi de aynı şekilde başarısız oldu.

İlk yol, hizmetin ilk kopyasını kurmak, kurulu dizini kopyalayıp yeniden adlandırmak ve ardından istenen hizmet adını değiştirmek için uygulama yapılandırmasını değiştirdikten sonra aşağıdaki komutu çalıştırmaktı:

InstallUtil.exe /i AppService.exe

Bu işe yaramadığında ikinci bir yükleyici projesi oluşturmaya çalıştım, yapılandırma dosyasını düzenledim ve ikinci yükleyiciyi oluşturdum. Yükleyiciyi çalıştırdığımda iyi çalıştı ancak hizmet services.msc'de görünmedi, bu yüzden önceki komutu ikinci yüklü kod tabanına karşı çalıştırdım.

Her iki seferinde InstallUtil'den aşağıdaki çıktıyı aldım (yalnızca ilgili parçalar):

İşlem yapılmış bir kurulumu çalıştırma.

Kurulumun Kurulum aşamasının başlangıcı.

App Service Two hizmeti yükleniyor ... Service App Service Two başarıyla yüklendi. EventLog kaynak Uygulama Hizmeti İki günlük Uygulamasında oluşturuluyor ...

Kurulum aşamasında bir istisna meydana geldi. System.NullReferenceException: Nesne başvurusu bir nesnenin örneğine ayarlanmadı.

Kurulumun geri alma aşaması başlıyor.

Kaynak App Service Two için olay günlüğü önceki durumuna geri yükleniyor. Service App Service Two sistemden kaldırılıyor ... Service App Service Two sistemden başarıyla kaldırıldı.

Geri alma aşaması başarıyla tamamlandı.

İşlem yapılan yükleme tamamlandı. Kurulum başarısız oldu ve geri alma gerçekleştirildi.

Uzun soluklu gönderi için özür dilerim, yeterince alakalı bilgi olduğundan emin olmak istedim. Şimdiye kadar beni şaşkına çeviren parça, hizmetin kurulumunun başarıyla tamamlandığını ve ancak NullReferenceException'ın atılmış gibi göründüğü EventLog kaynağını oluşturmaya gittikten sonra olduğunu belirtmesidir. Bu yüzden, neyi yanlış yaptığımı bilen veya daha iyi bir yaklaşımı olan biri varsa çok takdir edilecektir.

Yanıtlar:


82

Sc / service controller işlevini denediniz mi? Tür

sc create

bir komut satırında ve size yardım girişini verecektir. Bunu geçmişte Subversion için yaptığımı ve bu makaleyi referans olarak kullandığımı düşünüyorum :

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


5
Ben faydalı olmak için bu sayfayı bulundu: http://journalofasoftwaredev.wordpress.com/2008/07/16/multiple-instances-of-same-windows-service/. İnstallutil'i çalıştırdığınızda istediğiniz hizmet adını almak için yükleyiciye kod ekleyebilirsiniz.
Vivian Nehri

9
Wordpress blog bağlantısı şu şekilde değiştirildi: journalofasoftwaredev.wordpress.com/2008/07
STLDev

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

Bu çözüm benim için çalıştı.


5
sadece belirtmek için; [path to your exe]tam yol olmalı ve sonrasındaki boşluğu unutmayınbinpath=
mkb

2
Bu gerçekten bir hizmetin birden çok kez kurulmasına izin verir. Bununla birlikte, servis yükleyicisi tarafından sağlanan tüm bilgiler. Fe açıklaması, oturum açma türü vb.
Yok

20

Aşağıdakileri yaparak aynı hizmetin birden çok sürümünü çalıştırabilirsiniz:

1) Hizmet yürütülebilir dosyasını ve yapılandırmayı kendi klasörüne kopyalayın.

2) Install.Exe'yi hizmet yürütülebilir klasörüne kopyalayın (.net çerçeve klasöründen)

3) Hizmet yürütülebilir klasöründe, aşağıdaki içeriklerle (benzersiz hizmet adları) Install.exe.config adlı bir yapılandırma dosyası oluşturun:

<?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) Hizmeti aşağıdaki içeriklerle yüklemek için bir toplu iş dosyası oluşturun:

REM Install
InstallUtil.exe YourService.exe
pause

5) Oradayken, bir kaldırma toplu iş dosyası oluşturun

REM Uninstall
InstallUtil.exe -u YourService.exe
pause

DÜZENLE:

Bir şeyi kaçırdıysam, işte ServiceInstaller Sınıfı (gerektiği gibi ayarlayın):

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

Açıkladığınız şeyin aşağı yukarı ServiceName ve DisplayName'in hizmetlerim app.config'den ayarlanmasına izin vererek yaptığım şey olduğunu düşünüyorum.
Switters

Kullandığım bir şablon var, yıllardır kullandığım, bu yüzden belki bir şeyi kaçırdım, ServiceInstaller Sınıfınız neye benziyor, kullandığım birinin çalışan bir kopyasını gönderecek, bana bildirin, bu yardımcı olur mu?
Mark Redman

Servis kurucularımız aslında neredeyse aynı. Yapılandırma dosyasından hizmeti ve görünen adları yüklemek için statik bir sınıf kullanıyorum, ancak bunun dışında çok benzerler. Neden benim için çalışmadığına dair tahminim, servis kodumuzla ilgili biraz tuhaf bir şey olabileceğidir. Maalesef pek çok el var. Yine de anladığım kadarıyla, cevabınız çoğu durumda işe yaramalı yardım için teşekkürler.
Switters

2
Büyük yardım, teşekkürler. Kurulum yapılandırma dosyasının InstallUtil.exe
NullReference

Tamamen işe yarayan güzel bir yaklaşım. Bu, kurulum klasörünüze hangi InstallUtil.exe dosyasını kopyalayacağınızı biliyorsanız (şahsen, 64 bit kopyalarla daha da kötüleşen tonlarca çerçeve sürümüne sahibim). Bu, kurulumları yaparlarsa Yardım Masası ekibine açıklamayı oldukça zorlaştırır. Ancak geliştirici tarafından yönetilen bir kurulum için çok zariftir.
timmi4sa

11

Eski soru, biliyorum ama InstallUtil.exe üzerinde / servicename seçeneğini kullanma şansım oldu. Yine de yerleşik yardımda listelendiğini görmüyorum.

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

Bunu ilk nerede okuduğumdan tam olarak emin değilim ama o zamandan beri görmedim. YMMV.


3
Şu hatayı döndürür:An exception occurred during the Install phase. System.ComponentModel.Win32Exception: The specified service already exists
mkb

@mkb "Servisim" adında başka bir servisiniz var mı?
Jonathon Watney

Evet, soruda olduğu gibi, aynı çalıştırılabilir bir hizmetim var, ancak her biri farklı yapılandırmaya sahip iki örneğini yüklemek istiyorum. Service exe'yi kopyalayıp yapıştırdım ama bu işe yaramadı.
mkb

1
/ servicename = "My Service InstanceOne" ve / servicename = "My Service InstanceTwo" İsimlerin benzersiz olması gerekir.
granadaCoder

11

Bir başka hızlı bir yolu için özel bir değeri belirtmek için ServiceNameve DisplayNamekullandığı installutilkomut satırı parametreleri.

  1. Sizin de ProjectInstallersınıfının sanal yöntemleri geçersiz Install(IDictionary stateSaver)veUninstall(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. Projenizi oluşturun
  3. Hizmeti, parametre installutilkullanarak özel adınızı ekleyerek kurun /servicename:

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

/servicenameKomut satırında belirtmezseniz , hizmetin ProjectInstaller properties / config'de belirtilen ServiceName ve DisplayName değerleriyle yükleneceğini lütfen unutmayın.


2
Parlak!! Teşekkürler - bu tam olarak ihtiyaç duyulan şeydi ve konuya.
Iofacture

7

Yan yana pencere hizmetlerini sık sık yüklemek / kaldırmak için otomatik dağıtım yazılımımızı kullanırken yukarıdaki yöntemlerle pek şansım olmadı, ancak sonunda bir sonek belirtmek için bir parametreyi geçirmeme izin veren aşağıdakileri buldum. komut satırındaki hizmet adına. Ayrıca, tasarımcının düzgün çalışmasına izin verir ve gerekirse tüm adı geçersiz kılmak için kolayca uyarlanabilir.

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

Bunu aklımda tutarak şunları yapabilirim: "Harika Hizmet" hizmetini aradıysam, hizmetin bir UAT sürümünü aşağıdaki şekilde yükleyebilirim:

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

Bu, hizmeti "Awesome Service - UAT" adıyla oluşturacaktır. Bunu, tek bir makinede yan yana çalışan aynı hizmetin DEVINT, TESTING ve ACCEPTANCE sürümlerini çalıştırmak için kullandık. Her sürümün kendi dosya / yapılandırma kümesi vardır - bunu aynı dosya kümesine işaret eden birden fazla hizmet yüklemeyi denemedim.

NOT: /ServiceSuffixhizmeti kaldırmak için aynı parametreyi kullanmanız gerekir , bu nedenle kaldırmak için aşağıdakileri uygulayabilirsiniz:

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


Bu harika, ancak bu sadece kurulumcu için. Yeni bir örnek adına sahip olduğunuzda, Windows hizmeti bu yeni adı nasıl öğrenecek? Windows hizmetinin yapımında bunu iletmek zorunda mısınız?
progLearner

Teşekkürler! Yükleyici, Windows Hizmetini yüklerken yukarıdaki SetNames () yönteminde ayarlanan değerleri kullanarak adı belirleyecektir.
tristankoffee

Elbette, ama bu ismi dış dünyadan nasıl belirleyebilirsin?
progLearner

Cevabım, servisi dış dünyada kurmak (ve kaldırmak) için komut satırında kullanılan komuttur. Gönderdiğiniz değer /ServiceSuffix="UAT", yükleyici tarafından hizmette son eki ayarlamak için kullanılır. Örneğimde, aktarılan değer UAT. Benim senaryomda, hizmetin mevcut adına bir sonek eklemek istedim, ancak bunu, adı tamamen aktarılan değerle değiştirecek şekilde uyarlayamamanız için hiçbir neden yok.
tristankoffee

Teşekkürler, ancak bu bir komut satırı girişi (= manuel giriş), kod değil. Orijinal soruya göre: Yeni bir örnek adına sahip olduğunuzda, Windows hizmeti bu yeni adı nasıl öğrenecek? Windows hizmetinin yapımında bunu iletmek zorunda mısınız?
progLearner

4

Bu işi yapmak için yaptığım şey, hizmet adını ve görünen adını hizmetim için bir app.config dosyasında saklamaktır. Ardından, yükleyici sınıfımda, app.config dosyasını bir XmlDocument olarak yüklüyorum ve değerleri almak için xpath kullanıyorum ve InitializeComponent () 'i çağırmadan önce ServiceInstaller.ServiceName ve ServiceInstaller.DisplayName'e uyguluyorum. Bu, bu özellikleri InitializeComponent () içinde zaten ayarlamadığınızı varsayar, bu durumda yapılandırma dosyanızdaki ayarlar yok sayılır. Aşağıdaki kod, InitializeComponent () 'den önce yükleyici sınıf yapıcımdan çağırdığım şeydir:

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

Konfigürasyon dosyasını doğrudan ConfigurationManager.AppSettings'den okumanın veya benzer bir şeyin yükleyici çalıştığında işe yarayacağına inanmıyorum, hizmetinizin .exe'si değil, InstallUtil.exe bağlamında çalışıyor. ConfigurationManager.OpenExeConfiguration ile bir şeyler yapabilirsiniz, ancak benim durumumda bu, yüklenmemiş özel bir yapılandırma bölümüne ulaşmaya çalıştığım için işe yaramadı.


Merhaba Chris House! Cevabınız karşısında tökezledim çünkü Quartz.NET zamanlayıcı etrafında kendi kendine barındırılan OWIN tabanlı bir Web API oluşturuyorum ve bunu bir Windows Hizmetine yapıştırıyorum. Oldukça şık! Umarım iyisindir!
NovaJoe

Merhaba Chris House! Cevabınız karşısında tökezledim çünkü Quartz.NET zamanlayıcı etrafında kendi kendine barındırılan OWIN tabanlı bir Web API oluşturuyorum ve bunu bir Windows Hizmetine yapıştırıyorum. Oldukça şık! Umarım iyisindir!
NovaJoe

4

Sadece @ chris.house.00 mükemmel cevabı artırmak için bu , uygulama ayarlarınızda okuma işlevini aşağıdakileri dikkate edebilirsiniz:

 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

Daha önce bir hizmete ve aynı sunucuda yan yana çalışan güncellenmiş bir hizmete ihtiyacım olduğu benzer bir durum yaşadım. (Bir veritabanı değişikliğinden daha fazlasıydı, aynı zamanda kod değişiklikleriydi). Yani aynı .exe dosyasını iki kez çalıştıramadım. Yeni DLL'lerle ancak aynı projeden derlenmiş yeni bir .exe dosyasına ihtiyacım vardı. Sadece hizmet adını ve görünen adını değiştirmek benim için işe yaramadı, yine de bir Dağıtım Projesi kullandığım için "hizmet zaten mevcuttu" hatasını aldım. Sonunda benim için işe yarayan şey, Dağıtım Projesi Özelliklerimde bir Kılavuz olan "Ürün Kodu" adlı bir özellik var.

görüntü açıklamasını buraya girin

Bundan sonra, Kurulum Projesi başarıyla yüklenmiş yeni bir .exe veya .msi olarak yeniden oluşturulur.


1

En basit yaklaşım, dll adına göre hizmet adını temel almaktır:

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.