Создание пользовательского интерфейса для мониторинга и взаимодействия с работающей службой Windows

Мне нужно запустить кучу подключаемых процессов в службе Windows на моем сервере и вы хотите создать пользовательский интерфейс, который позволяет мне взаимодействовать с каждым из подключаемых модулей, используемых службой.

Каков наиболее распространенный метод (или методы) для связи между пользовательским интерфейсом и долговременной службой Windows? Я имею в виду предоставление промежуточного местоположения, такого как база данных, и использование какой-то очереди сообщений для выдачи команд службе. Кто-нибудь из вас применял такой подход или какой-то другой превосходный подход? С какими проблемами вы сталкиваетесь в этом процессе?

Ответ 1

Не используйте удаленное взаимодействие! Хотя это, безусловно, будет работать, Microsoft говорит, что удаленное взаимодействие является устаревшей технологией и что все новые распределенные приложения должны разрабатываться с использованием WCF. Смотрите здесь для более подробной информации.

Windows Communication Foundation (WCF) - рекомендуемый способ взаимодействия двух процессов .NET. WCF предоставляет унифицированную модель программирования, которая значительно упрощает распределенную разработку, абстрагируя многие из сложностей, связанных с конкретными механизмами связи, например, сокеты, каналы и т.д.

Учитывая детали вашей ситуации, я бы предложил сделать каждый плагин службы Windows службой WCF. Для каждой службы WCF, т.е. Плагина, определите интерфейс, который необходимо предоставить вашему пользовательскому интерфейсу. Интерфейс - это просто интерфейс С#, украшенный атрибутом ServiceContract. Этот интерфейс содержит методы, каждый из которых украшен атрибутом OperationContract, который ваш пользовательский интерфейс будет использовать для взаимодействия со службой WCF (плагин). Эти методы могут принимать и возвращать любой сериализуемый тип .NET или, как это часто бывает, ваши собственные пользовательские типы. Чтобы использовать пользовательские типы с WCF, просто украсьте их атрибутом DataContract и отметьте членов, которых вы хотите обменять через WCF, с атрибутом DataMember.

Определив интерфейс ServiceContract, определите класс, который реализует этот интерфейс. Каждый метод OperationContract делает все, что ему нужно, например, взаимодействует с базой данных, вычисляет некоторое значение и т.д. После того, как вы это сделали, вы фактически определили службу WCF. Вот короткий, но рабочий пример:

using System.ServiceModel;
namespace AdditionServiceNamespace
{
    [DataContract]
    public class Complex
    {
        [DataMember]
        public int real;
        [DataMember]
        public int imag;
    }
    [ServiceContract]
    public interface IAdditionService
    {
        [OperationContract]
        Complex Add(Complex c1, Complex c2);
    }
    public class AdditionService : IAdditionService
    {
        public Complex Add(Complex c1, Complex c2)
        {
            Complex result = new Complex();
            result.real = c1.real + c2.real;
            result.imag = c1.imag + c2.imag;
            return result;
        }
    }
}

Следующим шагом является размещение этой службы WCF, чтобы она была доступна для вашего пользовательского интерфейса. Поскольку вы будете использовать службу Windows, размещение службы WCF достаточно легко выполняется с помощью обратного вызова OnStart() вашей службы Windows, например:

using System.ServiceModel;
using System.ServiceProcess;
using AdditionServiceNamespace;
namespace WindowsServiceNamespace
{
    public class WindowsService : ServiceBase
    {
        static void Main()
        {
            ServiceBase[] ServicesToRun = new ServiceBase[]
            { new WindowsService() };
            ServiceBase.Run(ServicesToRun);
        }
        private ServiceHost _host;
        public WindowsService()
        {
            InitializeComponent();
        }
        protected override void OnStart(string[] args)
        {
            _host = new ServiceHost(typeof(AdditionService));
            _host.Open();
        }
        protected override void OnStop()
        {
            try
            {
                if (_host.State != CommunicationState.Closed)
                {
                    _host.Close();
                }
            }
            catch
            {
                // handle exception somehow...log to event viewer, for example
            }
        }
    }
}

Осталось только определить файл app.config для вашей службы Windows, который настроит некоторые необходимые аспекты вашей службы WCF. Это может показаться излишним, но имейте в виду две вещи. Прежде всего, Visual Studio автоматически предоставляет вам базовый файл app.config при добавлении класса обслуживания WCF в проект. Во-вторых, файл app.config дает вам огромный контроль над вашей службой WCF без необходимости внесения изменений в код. Вот файл app.config для примера выше:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <services>
            <service name="AdditionServiceNamespace.MyAdditionService"
                     behaviorConfiguration="default">
                <endpoint name="AdditionService"
                     address="net.pipe://localhost/AdditionService"
                     binding="netNamedPipeBinding"
                     contract="AdditionServiceNamespace.IAdditionService" />
                <endpoint address="net.pipe://localhost/AdditionService/MEX"
                     binding="mexNamedPipeBinding"
                     contract="IMetadataExchange" />
            </service>
        </services>
        <behaviors>
            <serviceBehaviors>
                <behavior name="default">
                    <serviceMetadata />
                </behavior>
            </serviceBehaviors>
        </behaviors>
    </system.serviceModel>
</configuration>

Обратите внимание, что служба WCF AdditionService имеет две конечные точки. Конечная точка обмена метаданными используется для генерации кода клиентом, поэтому пока игнорируйте ее. Первая конечная точка настроена на использование NetNamedPipeBinding. Это привязка, которую следует использовать, если ваш пользовательский интерфейс и служба Windows будут работать на одном компьютере (здесь приведена блок-схема выбора подходящей привязки для использования). Однако эту привязку нельзя использовать, если ваш пользовательский интерфейс и служба Windows будут работать на разных компьютерах. В этом случае вы можете использовать NetTcpBinding в качестве замены. Чтобы заменить NetTcpBinding на NetNamedPipeBinding, вам просто нужно изменить адрес и привязку конечной точки, например:

<endpoint name="AdditionService"
          address="net.tcp://<machine hostname here>/AdditionService"
          binding="netTcpBinding"
          contract="AdditionServiceNamespace.IAdditionService" />

Никаких изменений кода не требуется! Внесите изменения, перезапустите службу, и ваша служба WCF теперь доступна для удаленных компьютеров. Вы можете даже разрешить несколько конечных точек для одной и той же службы WCF, если хотите. Дело в том, что файл app.config предлагает огромную гибкость, не требуя изменений в коде.

Это! Теперь у вас есть служба WCF, размещенная внутри вашей службы Windows, доступная для использования вашим пользовательским интерфейсом.

Так как же работает пользовательский интерфейс, то есть клиентская сторона?

Это где настоящая сила WCF вступает в игру. Приступая к работе с WCF, проще всего воспользоваться возможностями генерации кода в Visual Studio. Убедитесь, что ваш сервис Windows (тот, на котором размещается AdditionService) работает. В своем проекте пользовательского интерфейса щелкните правой кнопкой мыши свой проект в обозревателе решений и выберите пункт меню Добавить ссылку на службу.... В поле Адрес введите net.pipe://localhost/AdditionService и нажмите кнопку Перейти. Вы должны увидеть, что AdditionService появится в списке сервисов. В поле Пространство имен введите AdditionService и нажмите кнопку ОК.

Выполнение этих шагов приведет к созданию клиентского прокси и правильно определенного файла app.config, которые будут добавлены в ваш проект пользовательского интерфейса. Этот клиентский прокси становится вашим клиентским API AdditionService, и вы используете его следующим образом:

using TestConsoleApp.AdditionService;
namespace TestConsoleApp
    class Program
    {
        static void Main(string[] args)
        {
            AdditionServiceClient client = new AdditionServiceClient();
            Complex c1 = new Complex(), c2 = new Complex();
            c1.real = 3; c1.imag = 5;
            c2.real = 1; c2.imag = 7;
            Complex result = client.Add(c1, c2);
        }
    }
}

Обратите внимание, насколько это просто. По сути, создается клиентский прокси, AdditionServiceClient. Затем создаются два Complex объекта. Наконец, метод Add() на клиентском прокси вызывается, и возвращается Complex результат.

За кулисами происходит то, что метод Add() клиентского прокси-сервера фактически передает два Complex объекта в службу WCF AdditionService, размещенную в службе Windows. AdditionService выполняет сложение, а затем возвращает результат. Все это происходит по именованному каналу, но обратите внимание, что здесь нет кода, специфичного для именованного канала! WCF абстрагировался от всей этой сложности за моделью программирования, которая определяется интерфейсом IAdditionService.

Я знаю, что это много информации для усвоения, но я надеюсь, что очевидно, насколько мощным и простым в использовании может быть WCF. Конечно, этот пример охватывает только небольшое подмножество всего, что доступно в WCF.

В конце концов, однако, WCF должен быть механизмом, который вы используете для связи между вашим пользовательским интерфейсом и службой Windows. Для получения дополнительной информации я настоятельно рекомендую книгу Juval Lowy " Программирование служб WCF для всех вещей WCF". Вы также можете посетить его веб-сайт IDesign.net, чтобы получить бесплатные образцы кода WCF. Чтобы узнать больше о WCF, посмотрите это бесплатное видео на dnrTV. Он охватывает цель WCF и демонстрирует программирование WCF на нескольких простых для понимания примерах.

Ответ 2

Лучше всего использовать удаленное использование .NET по каналу IPC.

Пока сложно настроить, во второй раз это довольно легко.

Я предлагаю вам сначала сыграть с несколькими образцами при экспонировании удаленных объектов из одного приложения в другое.

Я раньше не использовал очереди сообщений, поэтому не могу комментировать это.