Menginstal beberapa contoh layanan windows yang sama di server

96

Jadi kami telah membuat layanan windows untuk memasukkan data ke aplikasi klien kami dan semuanya berjalan dengan baik. Klien telah datang dengan permintaan konfigurasi yang menyenangkan yang membutuhkan dua contoh layanan ini yang berjalan di server yang sama dan dikonfigurasi untuk menunjuk ke database terpisah.

Sejauh ini saya belum bisa membuat ini terjadi dan berharap sesama anggota stackoverflow mungkin bisa memberikan beberapa petunjuk mengapa.

Setup saat ini:

Saya telah menyiapkan proyek yang berisi layanan windows, kami akan menyebutnya AppService mulai sekarang, dan file ProjectInstaller.cs yang menangani langkah-langkah instalasi khusus untuk mengatur nama layanan berdasarkan kunci di App.config seperti itu :

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

Dalam hal ini Util hanyalah kelas statis yang memuat nama layanan dari file konfigurasi.

Dari sini dan seterusnya, saya telah mencoba dua cara berbeda untuk memasang kedua layanan dan keduanya gagal dengan cara yang sama.

Cara pertama adalah dengan menginstal salinan pertama layanan, menyalin direktori yang diinstal dan menamainya, lalu menjalankan perintah berikut setelah memodifikasi konfigurasi aplikasi untuk mengubah nama layanan yang diinginkan:

InstallUtil.exe /i AppService.exe

Ketika itu tidak berhasil, saya mencoba membuat proyek penginstal kedua, mengedit file konfigurasi dan membangun penginstal kedua. Ketika saya menjalankan penginstal, itu berfungsi dengan baik tetapi layanan tidak muncul di services.msc jadi saya menjalankan perintah sebelumnya terhadap basis kode yang diinstal kedua.

Kedua kali saya menerima output berikut dari InstallUtil (hanya bagian yang relevan):

Menjalankan instalasi yang ditransaksikan.

Memulai tahap penginstalan.

Menginstal layanan Layanan Aplikasi Dua ... Layanan Aplikasi Layanan Dua telah berhasil diinstal. Membuat Layanan Aplikasi sumber EventLog Dua di Aplikasi log ...

Pengecualian terjadi selama fase Instal. System.NullReferenceException: Referensi objek tidak disetel ke turunan objek.

Fase Rollback penginstalan dimulai.

Mengembalikan log peristiwa ke keadaan sebelumnya untuk sumber App Service Two. Layanan Aplikasi Layanan Dua sedang dihapus dari sistem ... Layanan Aplikasi Layanan Dua berhasil dihapus dari sistem.

Fase Rollback berhasil diselesaikan.

Penginstalan yang ditransaksikan telah selesai. Instalasi gagal, dan rollback telah dilakukan.

Maaf untuk posting yang bertele-tele, ingin memastikan ada informasi yang cukup relevan. Bagian yang sejauh ini membuat saya bingung adalah menyatakan bahwa penginstalan layanan selesai dengan sukses dan hanya setelah itu pergi untuk membuat sumber EventLog yang NullReferenceException tampaknya dilemparkan. Jadi, jika ada yang tahu apa yang saya lakukan salah atau memiliki pendekatan yang lebih baik, itu akan sangat dihargai.

Switter
sumber

Jawaban:

81

Sudahkah Anda mencoba util pengontrol sc / service? Tipe

sc create

di baris perintah, dan itu akan memberi Anda entri bantuan. Saya rasa saya pernah melakukan ini di masa lalu untuk Subversion dan menggunakan artikel ini sebagai referensi:

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

jamesaharvey
sumber
5
Saya menemukan halaman ini berguna: http://journalofasoftwaredev.wordpress.com/2008/07/16/multiple-instances-of-same-windows-service/. Anda dapat memasukkan kode ke dalam penginstal untuk mendapatkan nama layanan yang Anda inginkan ketika Anda menjalankan installutil.
Sungai Vivian
9
Tautan ke blog wordpress telah diubah menjadi: journalofasoftwaredev.wordpress.com/2008/07
STLDev
21
  sc create [servicename] binpath= [path to your exe]

Solusi ini berhasil untuk saya.

Rajesh Kumar
sumber
5
hanya untuk menunjukkan; [path to your exe]harus jalur penuh dan jangan lupa spasi setelahnyabinpath=
mkb
2
Ini memang memungkinkan layanan dipasang beberapa kali. Namun, semua informasi disediakan oleh penginstal layanan. Deskripsi Fe, tipe logon, dll. Diabaikan
Noel Widmer
20

Anda dapat menjalankan beberapa versi dari layanan yang sama dengan melakukan hal berikut:

1) Salin Layanan yang dapat dieksekusi dan konfigurasikan ke foldernya sendiri.

2) Salin Install.Exe ke folder layanan yang dapat dieksekusi (dari folder framework .net)

3) Buat file konfigurasi bernama Install.exe.config di folder layanan yang dapat dieksekusi dengan konten berikut (nama layanan unik):

<?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) Buat file batch untuk menginstal layanan dengan konten berikut:

REM Install
InstallUtil.exe YourService.exe
pause

5) Saat Anda di sana, buat file batch uninstall

REM Uninstall
InstallUtil.exe -u YourService.exe
pause

EDIT:

Perhatikan jika saya melewatkan sesuatu, berikut adalah Kelas ServiceInstaller (sesuaikan sesuai kebutuhan):

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());
            }
        }
    }
}
Mark Redman
sumber
Saya pikir apa yang Anda gambarkan kurang lebih adalah apa yang telah saya lakukan dengan mengizinkan ServiceName dan DisplayName disetel dari layanan app.config Saya memang mencoba apa yang Anda gambarkan tetapi sayangnya hal itu mengakibatkan masalah yang sama yang tercantum dalam pertanyaan saya.
Switter
Saya memiliki template yang saya gunakan, yang telah saya gunakan selama berabad-abad, jadi mungkin saya melewatkan sesuatu, seperti apa tampilan Kelas ServiceInstaller Anda, apakah akan memposting salinan pekerjaan yang saya gunakan, beri tahu saya apakah ini membantu?
Mark Redman
Pemasang layanan kami sebenarnya hampir sama. Saya menggunakan kelas statis untuk memuat nama layanan dan tampilan dari file konfigurasi tetapi selain itu mereka sangat mirip. Dugaan saya mengapa ini tidak berhasil bagi saya adalah mungkin ada sesuatu yang agak aneh tentang kode layanan kami. Sayangnya, banyak pihak yang telah melakukannya. Dari apa yang saya pahami, jawaban Anda seharusnya berfungsi dalam sebagian besar kasus, terima kasih atas bantuannya.
Switter
2
Bantuan besar terima kasih. Saya pikir file konfigurasi instalasi harus diberi nama InstallUtil.exe.confg bukan Install.exe.config untuk InstallUtil.exe
NullReference
Pendekatan bagus yang benar-benar berhasil. Itu jika Anda tahu InstallUtil.exe mana yang akan disalin ke folder instalasi Anda (Saya pribadi memiliki banyak versi kerangka kerja yang diinstal yang diperburuk oleh salinan 64-bit). Ini akan membuat sulit untuk menjelaskan kepada tim Helpdesk jika mereka melakukan penginstalan. Tetapi untuk instalasi yang dipimpin pengembang itu sangat elegan.
timmi4sa
11

Pertanyaan lama, saya tahu, tapi saya beruntung menggunakan opsi / servicename di InstallUtil.exe. Saya tidak melihatnya tercantum dalam bantuan bawaan.

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

Saya tidak sepenuhnya yakin di mana saya pertama kali membaca tentang ini tetapi saya belum melihatnya sejak itu. YMMV.

Jonathon Watney
sumber
3
Mengembalikan kesalahan ini:An exception occurred during the Install phase. System.ComponentModel.Win32Exception: The specified service already exists
mkb
@mkb Apakah Anda memiliki layanan lain yang disebut "Layanan Saya"?
Jonathon Watney
Ya, seperti dalam pertanyaan saya memiliki satu layanan, dapat dijalankan yang sama, tetapi saya ingin menginstal dua contoh, masing-masing dengan konfigurasi yang berbeda. Saya salin-tempel exe layanan tetapi yang ini tidak berhasil.
mkb
1
/ servicename = "My Service InstanceOne" dan / servicename = "My Service InstanceTwo" Nama harus unik.
granadaCoder
11

Cara cepat lain untuk menentukan nilai khusus untuk ServiceNamedan DisplayNameadalah menggunakan installutilparameter baris perintah.

  1. Di ProjectInstallerkelas Anda, timpa metode virtual Install(IDictionary stateSaver)danUninstall(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. Bangun proyek Anda
  3. Instal layanan dengan installutilmenambahkan nama kustom Anda menggunakan /servicenameparameter:

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

Harap perhatikan bahwa jika Anda tidak menentukan /servicenamedi baris perintah, layanan akan diinstal dengan nilai ServiceName dan DisplayName yang ditentukan di properti / konfigurasi ProjectInstaller

Andrea
sumber
2
Cemerlang!! Terima kasih - inilah yang dibutuhkan dan langsung ke sasaran.
Iofacture
7

Saya tidak terlalu beruntung dengan metode di atas ketika menggunakan perangkat lunak penyebaran otomatis kami untuk sering menginstal / menghapus layanan windows berdampingan, tetapi saya akhirnya menemukan yang berikut ini yang memungkinkan saya untuk memberikan parameter untuk menentukan sufiks ke nama layanan di baris perintah. Ini juga memungkinkan perancang berfungsi dengan baik dan dapat dengan mudah disesuaikan untuk mengganti seluruh nama jika perlu.

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

Dengan pemikiran ini, saya dapat melakukan hal berikut: Jika saya telah memanggil layanan "Layanan Luar Biasa" maka saya dapat menginstal versi UAT dari layanan tersebut sebagai berikut:

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

Ini akan membuat layanan dengan nama "Layanan Luar Biasa - UAT". Kami telah menggunakan ini untuk menjalankan versi DEVINT, PENGUJIAN, dan PENERIMAAN dari layanan yang sama yang berjalan berdampingan pada satu mesin. Setiap versi memiliki kumpulan file / konfigurasinya sendiri - Saya belum mencoba ini untuk menginstal beberapa layanan yang menunjuk ke kumpulan file yang sama.

CATATAN: Anda harus menggunakan /ServiceSuffixparameter yang sama untuk menghapus instalan layanan, jadi Anda akan menjalankan yang berikut ini untuk menghapus instalan:

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

tristankoffee.dll
sumber
Itu bagus, tapi itu hanya untuk penginstal. Setelah Anda memiliki nama instance baru, bagaimana layanan Windows akan mengetahui tentang nama baru ini? Apakah Anda harus meneruskannya pada saat pembangunan layanan Windows?
progLearner
Terima kasih! Penginstal akan menetapkan nama pada Layanan Windows saat menginstalnya menggunakan nilai yang ditetapkan dalam metode SetNames () di atas.
tristankoffee
Tentu, tapi bagaimana Anda bisa menyetel nama ini dari dunia luar?
progLearner
Jawaban saya adalah perintah yang digunakan pada baris perintah untuk menginstal (dan menghapus) layanan di dunia luar. Nilai yang Anda masukkan /ServiceSuffix="UAT"digunakan oleh penginstal untuk menyetel sufiks pada layanan. Dalam contoh saya, nilai yang diteruskan adalah UAT. Dalam skenario saya, saya hanya ingin menambahkan sufiks ke nama layanan yang ada, tetapi tidak ada alasan Anda tidak dapat menyesuaikan ini untuk mengganti nama sepenuhnya dengan nilai yang diteruskan.
tristankoffee
Terima kasih, tapi itu adalah input baris perintah (= input manual), bukan kode. Sesuai pertanyaan awal: Setelah Anda memiliki nama instance baru, bagaimana layanan Windows tahu tentang nama baru ini? Apakah Anda harus meneruskannya pada saat pembangunan layanan Windows?
progLearner
4

Apa yang telah saya lakukan untuk membuat ini berfungsi adalah menyimpan nama layanan dan nama tampilan di app.config untuk layanan saya. Kemudian di kelas installer saya, saya memuat app.config sebagai XmlDocument dan menggunakan xpath untuk mengeluarkan nilai dan menerapkannya ke ServiceInstaller.ServiceName dan ServiceInstaller.DisplayName, sebelum memanggil InitializeComponent (). Ini mengasumsikan Anda belum menyetel properti ini di InitializeComponent (), dalam hal ini, pengaturan dari file konfigurasi Anda akan diabaikan. Kode berikut adalah apa yang saya panggil dari konstruktor kelas penginstal saya, sebelum 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;
          }
      }

Saya tidak percaya membaca file konfigurasi langsung dari ConfigurationManager.AppSettings atau yang serupa akan berfungsi seperti ketika penginstal berjalan, itu berjalan dalam konteks InstallUtil.exe, bukan .exe layanan Anda. Anda mungkin dapat melakukan sesuatu dengan ConfigurationManager.OpenExeConfiguration, namun dalam kasus saya, ini tidak berfungsi karena saya mencoba masuk ke bagian konfigurasi kustom yang tidak dimuat.

chris.house.00
sumber
Hai Chris House! Tersandung di jawaban Anda karena saya sedang membangun API Web berbasis OWIN yang dihosting sendiri di sekitar penjadwal Quartz.NET dan menempelkannya di Layanan Windows. Cukup apik! Berharap Anda baik-baik saja!
NovaJoe
Hai Chris House! Tersandung di jawaban Anda karena saya membangun API Web berbasis OWIN yang dihosting sendiri di sekitar penjadwal Quartz.NET dan menempelkannya di Layanan Windows. Cukup apik! Berharap Anda baik-baik saja!
NovaJoe
4

Hanya untuk meningkatkan jawaban sempurna dari @ chris.house.00 ini , Anda dapat mempertimbangkan fungsi berikut untuk membaca dari pengaturan aplikasi Anda:

 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";
            }
        }
Teoman shipahi
sumber
2

Saya memiliki situasi yang sama, di mana saya memerlukan layanan sebelumnya, dan layanan yang diperbarui berjalan berdampingan di server yang sama. (Itu lebih dari sekedar perubahan database, itu juga perubahan kode). Jadi saya tidak bisa menjalankan .exe yang sama dua kali. Saya membutuhkan .exe baru yang dikompilasi dengan DLL baru tetapi dari proyek yang sama. Hanya mengubah nama layanan dan nama tampilan layanan tidak berhasil untuk saya, saya masih menerima "kesalahan layanan sudah ada" yang saya yakini karena saya menggunakan Proyek Penerapan. Apa yang akhirnya berhasil untuk saya adalah di dalam Properti Proyek Penerapan saya, ada properti bernama "Kode Produk" yang merupakan Panduan.

masukkan deskripsi gambar di sini

Setelah itu, membangun kembali Proyek Penataan ke .exe atau .msi baru berhasil diinstal.

cmartin.dll
sumber
1

Pendekatan paling sederhana didasarkan pada nama layanan pada nama 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;
}
Igor Krupitsky
sumber