Ứng dụng giao diện điều khiển .NET như dịch vụ Windows


145

Tôi có ứng dụng console và muốn chạy nó dưới dạng dịch vụ Windows. VS2010 có mẫu dự án cho phép đính kèm dự án giao diện điều khiển và xây dựng dịch vụ Windows. Tôi muốn không thêm dự án dịch vụ riêng biệt và nếu có thể tích hợp mã dịch vụ vào ứng dụng bàn điều khiển để giữ ứng dụng bàn điều khiển là một dự án có thể chạy như ứng dụng bàn điều khiển hoặc dịch vụ windows nếu chạy từ dòng lệnh bằng cách sử dụng các công tắc.

Có lẽ ai đó có thể đề xuất thư viện lớp hoặc đoạn mã có thể nhanh chóng và dễ dàng chuyển đổi ứng dụng bảng điều khiển c # sang dịch vụ?


Tại sao bạn không tạo một dự án dịch vụ tạm thời và sao chép các bit làm cho nó trở thành một dịch vụ?
Gabe

4
Bạn có thể dùng thử Topshelf topshelf-project.com
Artem Koshelev

Bạn có thể thử kỹ thuật được mô tả ở đây: einaregilsson.com/2007/08/15/iêu
Joe

Huh? Tôi không chắc. về điều này

2
Một thay thế kệ hàng đầu rất đơn giản: runasservice.com
Luis Perez

Câu trả lời:


185

Tôi thường sử dụng techinque sau để chạy cùng một ứng dụng như ứng dụng bảng điều khiển hoặc như một dịch vụ:

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.UserInteractivethường đúng với ứng dụng console và false cho dịch vụ. Về mặt kỹ thuật, có thể chạy một dịch vụ ở chế độ tương tác người dùng, vì vậy bạn có thể kiểm tra một công tắc dòng lệnh thay thế.


3
Bạn sử dụng lớp ServiceInstaller, xem msdn.microsoft.com/en-us/l Library / .
VladV

2
Điều đó được mong đợi - dịch vụ của bạn sẽ chạy như một quy trình riêng biệt (vì vậy nó sẽ được hiển thị trong trình quản lý tác vụ), nhưng quy trình này sẽ được hệ thống kiểm soát (ví dụ: đã khởi động, dừng, khởi động lại theo cài đặt dịch vụ).
VladV

2
Nếu bạn chạy nó dưới dạng một ứng dụng bảng điều khiển, bạn sẽ không thấy một dịch vụ. Toàn bộ mục đích của mã này là cho phép bạn chạy nó dưới dạng ứng dụng bảng điều khiển hoặc dịch vụ. Để chạy như một dịch vụ, trước tiên bạn cần cài đặt nó (sử dụng lớp ServiceInstaller - xem liên kết MSDN ở trên - hoặc installuitil.exe) và chạy dịch vụ từ bảng điều khiển.
VladV

2
ServiceInstaller chỉ là một lớp tiện ích để đối phó với các dịch vụ Windows (một chút giống như các tiện ích installutil.exe hoặc sc.exe). Bạn có thể sử dụng nó để cài đặt bất cứ thứ gì bạn muốn như một dịch vụ, HĐH không quan tâm đến loại dự án bạn sử dụng.
VladV

5
Chỉ cần thêm một tham chiếu trong dự án của bạn vào System.ServiceProcess và bạn sẽ có thể sử dụng mã ở trên
danimal

59

Tôi đã thành công lớn với TopShelf .

TopShelf là gói Nuget được thiết kế để giúp dễ dàng tạo các ứng dụng .NET Windows có thể chạy dưới dạng ứng dụng bảng điều khiển hoặc dưới dạng Dịch vụ Windows. Bạn có thể nhanh chóng kết nối các sự kiện như dịch vụ Bắt đầu và Dừng sự kiện, định cấu hình bằng mã, ví dụ như để đặt tài khoản mà nó chạy, định cấu hình phụ thuộc vào các dịch vụ khác và định cấu hình cách phục hồi từ các lỗi.

Từ Bảng điều khiển quản lý gói (Nuget):

Cài đặt Topshelf

Tham khảo các mẫu mã để bắt đầu.

Thí dụ:

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 cũng đảm nhiệm việc cài đặt dịch vụ, có thể tiết kiệm rất nhiều thời gian và loại bỏ mã soạn sẵn khỏi giải pháp của bạn. Để cài đặt .exe của bạn dưới dạng dịch vụ, bạn chỉ cần thực hiện các thao tác sau từ dấu nhắc lệnh:

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

Bạn không cần kết nối ServiceInstaller và tất cả những thứ đó - TopShelf làm tất cả cho bạn.


1
Xin chào, tôi đang nhận được điều này: - "Không thể cài đặt gói 'Topshelf 4.0.1'. Bạn đang cố gắng cài đặt gói này vào dự án nhắm mục tiêu '.NETFramework, Version = v4.5', nhưng gói không chứa bất kỳ tham chiếu lắp ráp hoặc tệp nội dung tương thích với khung đó. " có chuyện gì ở đây vậy

3
Hãy chắc chắn rằng bạn đang nhắm mục tiêu toàn bộ thời gian chạy .NET 4.5.2, không phải cấu hình Máy khách.
thuyền buồm

xin vui lòng bạn có thể ném thêm ánh sáng vào myservice.exe và từ thư mục nào bạn sẽ mở dấu nhắc lệnh
Izuagbala

1
@Izuagbala myservice.exe là ứng dụng bảng điều khiển mà bạn đã tạo, với TopShelf khởi động vào nó như thể hiện trong mẫu mã.
buồm

Myservice.exe có thể chạy như bàn điều khiển sau khi được cài đặt như một dịch vụ không?. Tài liệu không rõ ràng: "Khi ứng dụng bảng điều khiển được tạo, nhà phát triển sẽ tạo một lớp dịch vụ duy nhất" docs.topshelf-project.com/en/latest/overview/ chủ
Michael Freidgeim 18/03/19

27

Vì vậy, đây là hướng dẫn đầy đủ:

  1. Tạo dự án Ứng dụng Console mới (ví dụ MyService)
  2. Thêm hai tài liệu tham khảo thư viện: System.ServiceProcess và System.Configuration.Install
  3. Thêm ba tệp được in dưới đây
  4. Xây dựng dự án và chạy "InstallUtil.exe c: \ path \ to \ MyService.exe"
  5. Bây giờ bạn sẽ thấy MyService trên danh sách dịch vụ (chạy services.msc)

* InstallUtil.exe thường có thể được tìm thấy ở đây: C: \ windows \ Microsoft.NET \ Framework \ v4.0.30319 \ InstallUtil.ex‌ e

Chương trình.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
Nếu bạn đang biên dịch dự án của mình cho 64 bit, bạn phải sử dụng InstallUtil.exe cho 64 bit có thể tìm thấy ở đây: C: \ windows \ Microsoft.NET \ Framework64 \ ... Phiên bản dành cho 32 bit (C: \ windows \ Microsoft.NET \ Framework) sẽ ném BadImageFormatException cho bạn ...
snytek

Điều này hoạt động rất tốt, lưu ý rằng như @snytek nói, nếu bạn đang sử dụng cơ sở 64, hãy đảm bảo rằng bạn sử dụng đúng thư mục. Ngoài ra, nếu bạn tình cờ làm như tôi và quên đổi tên dịch vụ thành một thứ khác ngoài "MyService", hãy đảm bảo rằng bạn gỡ cài đặt dịch vụ trước khi thực hiện thay đổi mã.
dmoore1181

3

Tôi nghe thấy quan điểm của bạn là muốn một hội đồng dừng mã lặp lại, nhưng nó sẽ đơn giản nhất và giảm sự lặp lại mã và giúp bạn dễ dàng sử dụng lại mã của mình theo những cách khác trong tương lai nếu ...... bạn chia nó thành 3 cụm.

  1. Một thư viện lắp ráp mà làm tất cả các công việc. Sau đó, có hai dự án rất mỏng / đơn giản:
  2. một trong đó là dòng lệnh
  3. một trong đó là dịch vụ windows.

1
Đây là cách tôi đã thực hiện nó trong nhiều năm - Dịch vụ khá nhiều có Start()Stop()phương pháp và ứng dụng giao diện điều khiển có một vòng lặp. Không sử dụng một khung công tác như TopShelf , đây là tùy chọn tốt nhất
Cơ bản

đồng ý với câu trả lời đó nhất sử dụng các công cụ của bên 3d cho các giải pháp đơn giản làm cho việc bảo trì trong tương lai trở nên phức tạp không cần thiết
tatigo

3

Đây là một cách mới hơn để biến Ứng dụng Console thành Dịch vụ Windows thành Dịch vụ Công nhân dựa trên .Net Core 3.1 mới nhất .

Nếu bạn tạo Dịch vụ Công nhân từ Visual Studio 2019, nó sẽ cung cấp cho bạn hầu hết mọi thứ bạn cần để tạo Dịch vụ Windows, đây cũng là thứ bạn cần thay đổi thành ứng dụng bảng điều khiển để chuyển đổi sang Dịch vụ Windows.

Dưới đây là những thay đổi bạn cần làm:

Cài đặt các gói NuGet sau

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

Thay đổi Program.cs để có một triển khai như dưới đây:

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

và thêm Worker.cs nơi bạn sẽ đặt mã sẽ được chạy bởi các hoạt động dịch vụ:

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

Khi mọi thứ đã sẵn sàng và ứng dụng đã được xây dựng thành công, bạn có thể sử dụng sc.exe để cài đặt ứng dụng bảng điều khiển của bạn như là một Dịch vụ Windows với lệnh sau:

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

2

Bạn có thể dùng

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

Và nó sẽ xuất hiện trong danh sách dịch vụ. Tôi không biết, mặc dù điều đó hoạt động chính xác. Một dịch vụ thường phải nghe một vài sự kiện.

Có một số trình bao bọc dịch vụ, có thể chạy bất kỳ ứng dụng nào như một dịch vụ thực sự. Ví dụ: microsofts SrvAny từ Bộ tài nguyên Win2003


Như bạn nói, exe dịch vụ sẽ cần giao tiếp với windows. +1 cho liên kết đến SrvAny
Jodrell

5
Tôi cho rằng phương pháp này không an toàn. Windows có các thư viện và tiện ích đặc biệt để quản lý các dịch vụ và chúng có nhiều khả năng hoạt động ổn định trong các phiên bản và môi trường hệ điều hành khác nhau. Đối với ứng dụng .NET, khá dễ dàng để tạo trình cài đặt MSI trong VS. Nó cũng có thể thực hiện cài đặt progrmmatically bằng phương thức ManagedInstallerClass.InstallHelper.
VladV

1
Không cần trình cài đặt và công cụ: chỉ cần sử dụng dòng lệnh này: sc tạo MyServiceName binPath = "c: \ path \ to \ service \ file \ exe"
JDC

2

Đầu tiên tôi nhúng giải pháp ứng dụng giao diện điều khiển vào giải pháp dịch vụ windows và tham chiếu nó.

Sau đó, tôi làm cho ứng dụng giao diện điều khiển lớp Chương trình công khai

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

Sau đó tôi tạo hai chức năng trong ứng dụng console

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

Sau đó, trong chính dịch vụ windows, tôi khởi tạo Chương trình và gọi các chức năng Bắt đầu và Dừng được thêm vào trong OnStart và OnStop. Xem bên dưới

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

Cách tiếp cận này cũng có thể được sử dụng cho ứng dụng windows / dịch vụ windows lai


về cơ bản đây là những gì JonAlb đã nói trong câu trả lời trước, nhưng cảm ơn về ví dụ mã
tatigo

0

Có lẽ bạn nên xác định những gì bạn cần, theo như tôi biết, bạn không thể chạy ứng dụng của mình dưới dạng Bảng điều khiển hoặc Dịch vụ với dòng lệnh, cùng một lúc. Hãy nhớ rằng dịch vụ đã được cài đặt và bạn phải khởi động nó trong Trình quản lý dịch vụ, bạn có thể tạo một ứng dụng mới để khởi động dịch vụ hoặc bắt đầu một quy trình mới chạy ứng dụng bảng điều khiển của bạn. Nhưng như bạn đã viết

"giữ ứng dụng giao diện điều khiển như một dự án"

Một lần, tôi đã ở vị trí của bạn, biến một ứng dụng console thành một dịch vụ. Trước tiên, bạn cần mẫu, trong trường hợp bạn đang làm việc với VS Express Edition. Đây là một liên kết nơi bạn có thể có những bước đầu tiên: Dịch vụ Windows C # , điều này rất hữu ích cho tôi. Sau đó, sử dụng mẫu đó, thêm mã của bạn vào các sự kiện mong muốn của dịch vụ.

Để cải thiện dịch vụ của bạn, có một điều khác bạn có thể làm, nhưng điều này không nhanh chóng và / hoặc dễ dàng, đó là sử dụng tên miền appd và tạo dll để tải / dỡ tải. Trong một, bạn có thể bắt đầu một quy trình mới với ứng dụng bảng điều khiển và trong một dll khác, bạn có thể chỉ cần đặt chức năng mà dịch vụ phải thực hiện.

Chúc may mắn.


0

Bạn cần tách chức năng thành một lớp hoặc các lớp và khởi chạy chức năng đó thông qua một trong hai sơ khai. Các cuống bàn điều khiển hoặc còn sơ khai dịch vụ.

Như thường thấy, khi chạy windows, vô số dịch vụ tạo nên cơ sở hạ tầng không (và không thể trực tiếp) trình bày các cửa sổ điều khiển cho người dùng. Dịch vụ cần liên lạc với người dùng theo cách không có đồ họa: thông qua SCM; trong nhật ký sự kiện, đến một số tệp nhật ký, v.v. Dịch vụ cũng sẽ cần liên lạc với các cửa sổ thông qua SCM, nếu không nó sẽ bị tắt máy.

Rõ ràng sẽ có thể chấp nhận được một số ứng dụng bảng điều khiển có thể giao tiếp với dịch vụ nhưng dịch vụ cần phải chạy độc lập mà không yêu cầu tương tác GUI.

Sơ đồ Console có thể rất hữu ích để gỡ lỗi hành vi dịch vụ nhưng không nên được sử dụng trong môi trường "sản xuất" mà suy cho cùng là mục đích tạo ra một dịch vụ.

Tôi đã không đọc nó đầy đủ nhưng bài viết này dường như đi đúng hướng.


0

Tôi sử dụng một lớp dịch vụ theo mô hình chuẩn được quy định bởi ServiceBasevà giải quyết các trợ giúp để dễ dàng gỡ lỗi F5. Điều này giữ cho dữ liệu dịch vụ được xác định trong dịch vụ, giúp chúng dễ dàng tìm thấy và tuổi thọ của chúng dễ quản lý.

Tôi thường tạo một ứng dụng Windows với cấu trúc bên dưới. Tôi không tạo ra một ứng dụng giao diện điều khiển; bằng cách đó tôi không nhận được một hộp đen lớn xuất hiện trên mặt mỗi khi tôi chạy ứng dụng. Tôi ở trong trình gỡ lỗi, nơi tất cả các hành động. Tôi sử dụng Debug.WriteLineđể các tin nhắn đi đến cửa sổ đầu ra, cổng này sẽ hiển thị độc đáo và hiển thị sau khi ứng dụng kết thúc.

Tôi thường không bận tâm thêm mã gỡ lỗi để dừng; Tôi chỉ sử dụng trình gỡ lỗi thay thế. Nếu tôi cần gỡ lỗi dừng, tôi biến dự án thành ứng dụng bảng điều khiển, thêm Stopphương thức chuyển tiếp và gọi nó sau khi gọi đến 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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.