Aplikasi .NET console sebagai layanan Windows


145

Saya memiliki aplikasi konsol dan ingin menjalankannya sebagai layanan Windows. VS2010 memiliki templat proyek yang memungkinkan untuk melampirkan proyek konsol dan membangun layanan Windows. Saya ingin tidak menambahkan proyek layanan yang terpisah dan jika mungkin mengintegrasikan kode layanan ke dalam aplikasi konsol untuk menjaga aplikasi konsol sebagai satu proyek yang dapat berjalan sebagai aplikasi konsol atau sebagai layanan windows jika dijalankan misalnya dari baris perintah menggunakan sakelar.

Mungkin seseorang dapat menyarankan perpustakaan kelas atau potongan kode yang dapat dengan cepat dan mudah mengubah aplikasi c # console ke layanan?


Mengapa Anda tidak membuat proyek layanan sementara saja dan menyalin bit yang membuatnya menjadi layanan?
Gabe

4
Anda dapat mencoba Topshelf topshelf-project.com
Artem Koshelev

Anda dapat mencoba teknik yang dijelaskan di sini: einaregilsson.com/2007/08/15/…
Joe

Hah? Saya tidak yakin. tentang ini.

2
Alternatif rak paling atas yang sangat sederhana: runasservice.com
Luis Perez

Jawaban:


185

Saya biasanya menggunakan techinque berikut untuk menjalankan aplikasi yang sama dengan aplikasi konsol atau sebagai layanan:

public static class Program
{
    #region Nested classes to support running as service
    public const string ServiceName = "MyService";

    public class Service : ServiceBase
    {
        public Service()
        {
            ServiceName = Program.ServiceName;
        }

        protected override void OnStart(string[] args)
        {
            Program.Start(args);
        }

        protected override void OnStop()
        {
            Program.Stop();
        }
    }
    #endregion

    static void Main(string[] args)
    {
        if (!Environment.UserInteractive)
            // running as service
            using (var service = new Service())
                ServiceBase.Run(service);
        else
        {
            // running as console app
            Start(args);

            Console.WriteLine("Press any key to stop...");
            Console.ReadKey(true);

            Stop();
        }
    }

    private static void Start(string[] args)
    {
        // onstart code here
    }

    private static void Stop()
    {
        // onstop code here
    }
}

Environment.UserInteractivebiasanya berlaku untuk aplikasi konsol dan palsu untuk layanan. Secara teknis, dimungkinkan untuk menjalankan layanan dalam mode interaktif-pengguna, sehingga Anda dapat memeriksa sakelar baris perintah sebagai gantinya.


3
Anda menggunakan kelas ServiceInstaller, lihat msdn.microsoft.com/en-us/library/… .
VladV

2
Itu diharapkan - layanan Anda akan berjalan sebagai proses yang terpisah (sehingga akan ditampilkan di task manager), tetapi proses ini akan dikendalikan oleh sistem (mis. Mulai, berhenti, restart sesuai dengan pengaturan layanan).
VladV

2
Jika Anda menjalankannya sebagai aplikasi konsol, Anda tidak akan melihat layanan. Seluruh tujuan kode ini adalah untuk memungkinkan Anda menjalankannya baik sebagai aplikasi konsol, atau sebagai layanan. Untuk menjalankan sebagai layanan, Anda harus menginstalnya terlebih dahulu (menggunakan kelas ServiceInstaller - lihat tautan MSDN di atas - atau installuitil.exe), dan jalankan layanan dari panel kontrol.
VladV

2
ServiceInstaller hanyalah kelas utilitas untuk menangani layanan Windows (sedikit seperti utilitas installutil.exe atau sc.exe). Anda dapat menggunakannya untuk menginstal apa pun yang Anda inginkan sebagai layanan, OS tidak peduli dengan jenis proyek yang Anda gunakan.
VladV

5
Cukup tambahkan referensi dalam proyek Anda ke System.ServiceProcess dan Anda akan dapat menggunakan kode di atas
danimal

59

Saya sudah sukses besar dengan TopShelf .

TopShelf adalah paket Nuget yang dirancang untuk membuatnya mudah untuk membuat aplikasi .NET Windows yang dapat berjalan sebagai aplikasi konsol atau sebagai Layanan Windows. Anda dapat dengan cepat menghubungkan peristiwa seperti layanan Anda Memulai dan Menghentikan acara, mengonfigurasi menggunakan kode misalnya untuk mengatur akun berjalan, mengonfigurasi dependensi pada layanan lain, dan mengonfigurasi cara pemulihan dari kesalahan.

Dari Package Manager Console (Nuget):

Instal-Paket Topshelf

Lihat contoh kode untuk memulai.

Contoh:

HostFactory.Run(x =>                                 
{
    x.Service<TownCrier>(s =>                        
    {
       s.ConstructUsing(name=> new TownCrier());     
       s.WhenStarted(tc => tc.Start());              
       s.WhenStopped(tc => tc.Stop());               
    });
    x.RunAsLocalSystem();                            

    x.SetDescription("Sample Topshelf Host");        
    x.SetDisplayName("Stuff");                       
    x.SetServiceName("stuff");                       
}); 

TopShelf juga menangani pemasangan layanan, yang dapat menghemat banyak waktu dan menghapus kode boilerplate dari solusi Anda. Untuk menginstal .exe Anda sebagai layanan, Anda cukup menjalankan yang berikut dari command prompt:

myservice.exe install -servicename "MyService" -displayname "My Service" -description "This is my service."

Anda tidak perlu menghubungkan ServiceInstaller dan semua itu - TopShelf melakukan semuanya untuk Anda.


1
Hai, saya mendapatkan ini: - "Tidak dapat menginstal paket 'Topshelf 4.0.1'. Anda mencoba untuk menginstal paket ini ke proyek yang menargetkan '.NETFramework, Versi = v4.5', tetapi paket tersebut tidak mengandung referensi perakitan atau file konten yang kompatibel dengan kerangka kerja itu. " apa yang salah di sini?

3
Pastikan Anda menargetkan runtime penuh .NET 4.5.2, bukan profil Klien.
berlayar

tolong bisakah Anda memberikan lebih banyak cahaya pada myservice.exe dan dari direktori mana Anda akan membuka command prompt
Izuagbala

1
@Izuagbala myservice.exe adalah aplikasi konsol yang telah Anda buat, dengan TopShelf di-boot ke dalamnya seperti yang ditunjukkan pada contoh kode.
berlayar

Bisakah myservice.exe dijalankan sebagai konsol setelah diinstal sebagai layanan? Dokumentasi tidak jelas: "Setelah aplikasi konsol dibuat, pengembang membuat kelas layanan tunggal" docs.topshelf-project.com/en/latest/overview/…
Michael Freidgeim

27

Jadi, inilah langkah-langkah lengkapnya:

  1. Buat proyek Aplikasi Konsol baru (mis. MyService)
  2. Tambahkan dua referensi pustaka: System.ServiceProcess dan System.Configuration.Install
  3. Tambahkan tiga file yang dicetak di bawah ini
  4. Bangun proyek dan jalankan "InstallUtil.exe c: \ path \ to \ MyService.exe"
  5. Sekarang Anda akan melihat MyService pada daftar layanan (jalankan services.msc)

* InstallUtil.exe biasanya dapat ditemukan di sini: C: \ windows \ Microsoft.NET \ Framework \ v4.0.30319 \ InstallUtil.ex‌ e

Program.cs

using System;
using System.IO;
using System.ServiceProcess;

namespace MyService
{
    class Program
    {
        public const string ServiceName = "MyService";

        static void Main(string[] args)
        {
            if (Environment.UserInteractive)
            {
                // running as console app
                Start(args);

                Console.WriteLine("Press any key to stop...");
                Console.ReadKey(true);

                Stop();
            }
            else
            {
                // running as service
                using (var service = new Service())
                {
                    ServiceBase.Run(service);
                }
            }
        }

        public static void Start(string[] args)
        {
            File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} started{1}", DateTime.Now, Environment.NewLine));
        }

        public static void Stop()
        {
            File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} stopped{1}", DateTime.Now, Environment.NewLine));
        }
    }
}

MyService.cs

using System.ServiceProcess;

namespace MyService
{
    class Service : ServiceBase
    {
        public Service()
        {
            ServiceName = Program.ServiceName;
        }

        protected override void OnStart(string[] args)
        {
            Program.Start(args);
        }

        protected override void OnStop()
        {
            Program.Stop();
        }
    }
}

MyServiceInstaller.cs

using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;

namespace MyService
{
    [RunInstaller(true)]
    public class MyServiceInstaller : Installer
    {
        public MyServiceInstaller()
        {
            var spi = new ServiceProcessInstaller();
            var si = new ServiceInstaller();

            spi.Account = ServiceAccount.LocalSystem;
            spi.Username = null;
            spi.Password = null;

            si.DisplayName = Program.ServiceName;
            si.ServiceName = Program.ServiceName;
            si.StartType = ServiceStartMode.Automatic;

            Installers.Add(spi);
            Installers.Add(si);
        }
    }
}

1
Jika Anda mengkompilasi proyek Anda untuk 64 bit Anda harus menggunakan InstallUtil.exe untuk 64 bit yang dapat ditemukan di sini: C: \ windows \ Microsoft.NET \ Framework64 \ ... Versi untuk 32 bit (C: \ windows \ Microsoft.NET \ Framework) akan melemparkan BadImageFormatException pada Anda ...
snytek

Ini berfungsi dengan baik, perhatikan bahwa seperti yang dikatakan @snytek, jika Anda menggunakan basis 64, pastikan bahwa Anda menggunakan direktori yang benar. Juga, jika Anda melakukan hal yang sama seperti saya dan lupa untuk mengubah nama layanan menjadi selain "MyService", pastikan Anda menghapus instalasi layanan sebelum membuat perubahan pada kode.
dmoore1181

3

Saya mendengar maksud Anda menginginkan satu majelis untuk menghentikan kode berulang tetapi, Akan lebih sederhana dan mengurangi pengulangan kode dan membuatnya lebih mudah untuk menggunakan kembali kode Anda dengan cara lain di masa depan jika ...... Anda memecahnya menjadi 3 majelis.

  1. Satu perakitan perpustakaan yang melakukan semua pekerjaan. Kemudian memiliki dua proyek yang sangat sangat ramping / sederhana:
  2. satu yang merupakan commandline
  3. yang merupakan layanan windows.

1
Ini adalah bagaimana saya melakukannya selama bertahun-tahun - Layanan ini memiliki cukup banyak Start()dan Stop()metode dan aplikasi konsol memiliki loop. Pendek menggunakan kerangka kerja seperti TopShelf , ini adalah pilihan terbaik
Dasar

setuju dengan jawaban itu paling banyak. menggunakan alat pesta 3d untuk solusi sederhana membuat pemeliharaan masa depan menjadi tidak perlu
tatigo

3

Berikut adalah cara yang lebih baru tentang cara mengubah Aplikasi Konsol ke Layanan Windows sebagai Layanan Pekerja berdasarkan .Net Core 3.1 terbaru .

Jika Anda membuat Layanan Pekerja dari Visual Studio 2019 itu akan memberi Anda hampir semua yang Anda butuhkan untuk membuat Layanan Windows di luar kotak, yang juga merupakan apa yang Anda perlu ubah ke aplikasi konsol untuk mengubahnya menjadi Layanan Windows.

Berikut adalah perubahan yang perlu Anda lakukan:

Instal paket NuGet berikut

Install-Package Microsoft.Extensions.Hosting.WindowsServices -Version 3.1.0
Install-Package Microsoft.Extensions.Configuration.Abstractions -Version 3.1.0

Ubah Program.cs untuk memiliki implementasi seperti di bawah ini:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ConsoleApp
{
    class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).UseWindowsService().Build().Run();
        }

        private static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>();
                });
    }
}

dan tambahkan Worker.cs di mana Anda akan meletakkan kode yang akan dijalankan oleh operasi layanan:

using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp
{
    public class Worker : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            //do some operation
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            return base.StartAsync(cancellationToken);
        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
            return base.StopAsync(cancellationToken);
        }
    }
}

Ketika semuanya sudah siap, dan aplikasi telah berhasil dibangun, Anda dapat menggunakan sc.exe untuk menginstal exe aplikasi konsol Anda sebagai Layanan Windows dengan perintah berikut:

sc.exe create DemoService binpath= "path/to/your/file.exe"

2

Kamu bisa memakai

reg add HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v ServiceName /d "c:\path\to\service\file\exe"

Dan itu akan muncul di daftar layanan. Saya tidak tahu, apakah itu berfungsi dengan baik. Suatu layanan biasanya harus mendengarkan beberapa acara.

Ada beberapa pembungkus layanan, yang dapat menjalankan aplikasi apa pun sebagai layanan nyata. Sebagai Contoh, Microsoft SrvAny dari Win2003 Resource Kit


Seperti yang Anda katakan, exe layanan perlu berkomunikasi dengan windows. +1 untuk tautan ke SrvAny
Jodrell

5
Saya menganggap pendekatan ini tidak aman. Windows memiliki perpustakaan dan utilitas khusus untuk mengelola layanan, dan mereka cenderung bekerja secara konsisten di berbagai versi dan lingkungan OS. Untuk aplikasi .NET, cukup mudah untuk membuat installer MSI di VS. Ini juga mungkin untuk melakukan instalasi progrmmatically menggunakan metode ManagedInstallerClass.InstallHelper.
VladV

1
Tidak perlu installer dan yang lainnya: cukup gunakan baris perintah ini: sc create MyServiceName binPath = "c: \ path \ to \ service \ file \ exe"
JDC

2

Pertama saya menanamkan solusi aplikasi konsol ke dalam solusi layanan windows dan referensi itu.

Kemudian saya membuat aplikasi Program konsol kelas untuk umum

/// <summary>
/// Hybrid service/console application
/// </summary>
public class Program
{
}

Saya kemudian membuat dua fungsi di dalam aplikasi konsol

    /// <summary>
    /// Used to start as a service
    /// </summary>
    public void Start()
    {
        Main();
    }

    /// <summary>
    /// Used to stop the service
    /// </summary>
    public void Stop()
    {
       if (Application.MessageLoop)
            Application.Exit();   //windows app
        else
            Environment.Exit(1);  //console app
    }

Kemudian di dalam layanan windows itu sendiri saya instantiate Program dan memanggil fungsi Mulai dan Berhenti ditambahkan dalam OnStart dan OnStop. Lihat di bawah

class WinService : ServiceBase
{
    readonly Program _application = new Program();

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] servicesToRun = { new WinService() };
        Run(servicesToRun);
    }

    /// <summary>
    /// Set things in motion so your service can do its work.
    /// </summary>
    protected override void OnStart(string[] args)
    {
        Thread thread = new Thread(() => _application.Start());
        thread.Start();
    }

    /// <summary>
    /// Stop this service.
    /// </summary>
    protected override void OnStop()
    {
        Thread thread = new Thread(() => _application.Stop());
        thread.Start();
    }
}

Pendekatan ini juga dapat digunakan untuk aplikasi windows / windows service hybrid


ini pada dasarnya adalah apa yang dikatakan JonAlb dalam jawaban sebelumnya, tetapi terima kasih untuk contoh kode
tatigo

0

Mungkin Anda harus mendefinisikan apa yang Anda butuhkan, sejauh yang saya tahu, Anda tidak dapat menjalankan aplikasi Anda sebagai Konsol atau Layanan dengan baris perintah, pada saat yang sama. Ingat bahwa layanan ini diinstal dan Anda harus memulainya di Manajer Layanan, Anda dapat membuat aplikasi baru yang memulai layanan atau memulai proses baru menjalankan aplikasi konsol Anda. Tetapi ketika Anda menulis

"simpan aplikasi konsol sebagai satu proyek"

Suatu ketika, saya berada di posisi Anda, mengubah aplikasi konsol menjadi layanan. Pertama, Anda perlu templat, jika Anda bekerja dengan VS Express Edition. Berikut ini tautan tempat Anda dapat memiliki langkah pertama: C # Layanan Windows , ini sangat membantu bagi saya. Kemudian menggunakan templat itu, tambahkan kode Anda ke acara layanan yang diinginkan.

Untuk meningkatkan layanan Anda, ada hal lain yang dapat Anda lakukan, tetapi ini tidak cepat dan / atau mudah, menggunakan appdomains, dan membuat dll untuk memuat / membongkar. Dalam satu Anda dapat memulai proses baru dengan aplikasi konsol, dan di lain lain Anda hanya dapat menempatkan fungsi layanan yang harus dilakukan.

Semoga berhasil.


0

Anda perlu memisahkan fungsi menjadi kelas atau kelas dan meluncurkannya melalui salah satu dari dua bertopik. Potongan rintisan konsol atau potongan layanan.

Seperti terlihat jelas, ketika menjalankan windows, berbagai layanan yang membentuk infrastruktur tidak (dan tidak bisa langsung) menghadirkan jendela konsol kepada pengguna. Layanan perlu berkomunikasi dengan pengguna dengan cara yang tidak grafis: melalui SCM; dalam event log, ke beberapa file log dll. Layanan ini juga perlu berkomunikasi dengan windows melalui SCM, jika tidak maka akan mendapatkan shutdown.

Jelas akan dapat diterima untuk memiliki beberapa aplikasi konsol yang dapat berkomunikasi dengan layanan tetapi layanan harus berjalan secara mandiri tanpa persyaratan untuk interaksi GUI.

Rintisan konsol dapat sangat berguna untuk men-debug perilaku layanan tetapi tidak boleh digunakan dalam lingkungan "produksi" yang, setelah semua, adalah tujuan membuat layanan.

Saya belum membacanya secara penuh tetapi artikel ini sepertinya membanjiri arah yang benar.


0

Saya menggunakan kelas layanan yang mengikuti pola standar yang ditentukan oleh ServiceBase, dan memakukan pembantu untuk memudahkan debugging F5. Ini membuat data layanan didefinisikan dalam layanan, membuatnya mudah ditemukan dan masa hidupnya mudah dikelola.

Saya biasanya membuat aplikasi Windows dengan struktur di bawah ini. Saya tidak membuat aplikasi konsol; dengan cara itu saya tidak mendapatkan kotak hitam besar muncul di wajah saya setiap kali saya menjalankan aplikasi. Saya tinggal di debugger di mana semua tindakan. Saya menggunakan Debug.WriteLinesehingga pesan pergi ke jendela output, yang berlabuh dengan baik dan tetap terlihat setelah aplikasi berakhir.

Saya biasanya tidak repot menambahkan kode debug untuk berhenti; Saya hanya menggunakan debugger saja. Jika saya perlu menghentikan debug, saya membuat proyek aplikasi konsol, menambahkan Stopmetode penerusan, dan memanggilnya setelah panggilan ke Console.ReadKey.

public class Service : ServiceBase
{
    protected override void OnStart(string[] args)
    {
        // Start logic here.
    }

    protected override void OnStop()
    {
        // Stop logic here.
    }

    static void Main(string[] args)
    {
        using (var service = new Service()) {
            if (Environment.UserInteractive) {
                service.Start();
                Thread.Sleep(Timeout.Infinite);
            } else
                Run(service);
        }
    }
    public void Start() => OnStart(null);
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.