创建用于监视正在运行的 Windows 服务并与之交互的用户界面 [英] Creating a user interface for monitoring and interacting with a running windows service

查看:23
本文介绍了创建用于监视正在运行的 Windows 服务并与之交互的用户界面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在我的服务器上的 Windows 服务中运行一堆可插入的进程,并希望创建一个用户界面,允许我与服务使用的每个插件进行交互.

I need to run a bunch of pluggable processes in a windows service on my server and want to create a user interface that allows me to interact with each of the plugins in use by the service.

用户界面和长时间运行的 Windows 服务之间最常用的通信方法是什么?我正在考虑提供一个中间位置,例如数据库,并使用某种消息队列向服务发出命令.你们中有没有人实施过这样的方法,或者其他一些更好的方法?在此过程中您遇到了哪些问题?

What is the most common method (or methods) for communication between a user interface and a long-running windows service? I am thinking of providing a go-between location such as a database and using some sort of messaging queue to issue commands to the service. Have any of you implemented such an approach, or some other superior approach? WHat problems have you come across in the process?

推荐答案

不要使用远程处理! 虽然它肯定会奏效,但微软表示远程处理是一项遗留技术并且所有新的分布式应用程序都应该使用 WCF 开发.请参阅此处了解更多详情.

Windows Communication Foundation (WCF) 是两个 .NET 进程相互通信的推荐方式.WCF 提供了一个统一的编程模型,通过抽象与特定通信机制(例如套接字、管道等)相关的许多复杂性,极大地简化了分布式开发.

Windows Communication Foundation (WCF) is the recommended way for two .NET processes to communicate with each other. WCF provides a unified programming model that greatly simplifies distributed development by abstracting many of the complexities associated with specific communication mechanisms, e.g., sockets, pipes, etc.

鉴于您的情况的详细信息,我建议将每个 Windows 服务插件都设为 WCF 服务.对于每个 WCF 服务,即插件,定义它需要向您的 UI 公开的接口.该界面只是一个 C# 界面,上面装饰有 ServiceContract 属性.此接口包含方法,每个方法都装饰有 OperationContract 属性,您的 UI 将使用该属性与 WCF 服务(插件)进行通信.这些方法可以接受和返回任何可序列化的 .NET 类型,或者通常情况下,您自己的自定义类型.要在 WCF 中使用自定义类型,只需使用 DataContract 属性,并使用 DataMember 属性.

Given the details of your situation, I would suggest making each Windows service plugin a WCF service. For each WCF service, i.e., plugin, define the interface that it needs to expose to your UI. The interface is simply a C# interface adorned with the ServiceContract attribute. This interface contains the methods, each of which is adorned with the OperationContract attribute, that your UI will use to communicate with the WCF service (plugin). These methods can accept and return any serializable .NET type or, as is often the case, your own custom types. To use custom types with WCF, simply decorate them with the DataContract attribute and mark the members that you want to be exchanged via WCF with the DataMember attribute.

一旦您拥有ServiceContract 接口定义,定义一个实现该接口的类.每个 OperationContract 方法都做它需要做的事情,例如,与数据库交互、计算某个值等.完成此操作后,您就有效地定义了 WCF 服务.这是一个简短但有效的示例:

Once you have your ServiceContract interface defined, define a class that implements that interface. Each OperationContract method does whatever it needs to do, e.g., interact with database, calculate some value, etc. Once you've done this, you have effectively defined a WCF service. Here's a short, but working, example:

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 服务,以便您的 UI 可以使用它.由于您将使用 Windows 服务,因此在 Windows 服务的 OnStart() 回调中可以轻松托管 WCF 服务,如下所示:

The next step is to host this WCF service so that it is available to be used by your UI. Since you will be using a Windows service, hosting your WCF service is done easily enough in the OnStart() callback of your Windows service, like so:

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

剩下要做的唯一一件事就是为您的 Windows 服务定义一个 app.config 文件,该文件将配置您的 WCF 服务的某些必需方面.这可能看起来有点矫枉过正,但请记住两件事.首先,当您向项目添加 WCF 服务类时,Visual Studio 会自动为您提供一个基本的 app.config 文件.其次,app.config 文件使您可以对 WCF 服务进行大量控制,而无需更改代码.以下是上述示例的配套 app.config 文件:

The only thing left to do is to define an app.config file for your Windows service that will configure certain required aspects of your WCF service. This may seem like overkill, but keep two things in mind. First of all, Visual Studio gives you a basic app.config file automatically when you add a WCF service class to your project. Second, the app.config file gives you a tremendous amount of control over your WCF service without requiring changes to the code. Here's the companion app.config file for the example above:

<?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>

请注意 AdditionService WCF 服务有两个端点.元数据交换端点用于客户端的代码生成,所以暂时忽略它.第一个终结点配置为使用 NetNamedPipeBinding.这是绑定,如果您的 UI 和 Windows 服务将在同一台机器上运行(请参阅此处 有关选择要使用的适当绑定的流程图).但是,如果您的 UI 和 Windows 服务将在不同的机器上运行,则无法使用此绑定.在这种情况下,您可以使用 NetTcpBinding 作为一个替代品.要将 NetTcpBinding 替换为 NetNamedPipeBinding,您只需更改端点的地址和绑定,如下所示:

Note that the AdditionService WCF service has two endpoints. The metadata exchange endpoint is used for code generation by the client, so ignore it for now. The first endpoint is configured to use the NetNamedPipeBinding. This is the binding to use if your UI and Windows service will be running on the same machine (see here for a flowchart on selecting the appropriate binding to use). However, this binding cannot be used if your UI and Windows service will be running on different machines. In that case, you could use the NetTcpBinding as a replacement. To substitute the NetTcpBinding for the NetNamedPipeBinding, you would simply need to change the address and binding of the endpoint, like this:

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

无需更改代码!进行更改,重新启动您的服务,您的 WCF 服务现在可用于远程计算机.如果需要,您甚至可以为同一个 WCF 服务允许多个端点.关键是,app.config 文件提供了极大的灵活性,而无需更改代码.

No code changes are required! Make the change, restart your service, and your WCF service is now available to remote machines. You can even allow multiple endpoints for the same WCF service if you so desired. The point is, the app.config file offers a tremendous amount of flexibility without requiring changes to the code.

就是这样!现在,您的 Windows 服务中托管了一个 WCF 服务,可供您的 UI 使用.

That's it! You now have a WCF service hosted inside your Windows service available for use by your UI.

那么 UI 端(即客户端)是如何工作的?

So how does the UI side, i.e., the client side, work?

这就是 WCF 真正发挥作用的地方.开始使用 WCF 时,最简单的方法是利用 Visual Studio 的代码生成功能.确保您的 Windows 服务(托管 AdditionService 的服务)正在运行.在您的 UI 项目中,在解决方案资源管理器中右键单击您的项目,然后选择添加服务引用... 菜单选项.在地址框中,输入net.pipe://localhost/AdditionService,然后单击Go按钮.您应该会看到 AdditionService 出现在 Services 列表中.在 Namespace 框中,输入 AdditionService 并单击 OK 按钮.

This is where the real power of WCF comes into play. When getting started with WCF, the easiest thing to do is leverage Visual Studio's code generation capabilities. Make sure that your Windows service (the one hosting the AdditionService) is running. In your UI project, right-click on your project in the Solution Explorer and select the Add Service Reference... menu option. In the Address box, type net.pipe://localhost/AdditionService, and click the Go button. You should see the AdditionService show up in the Services list. In the Namespace box, type AdditionService and click the OK button.

执行这些步骤将生成一个客户端代理和一个正确定义的 app.config 文件,这些文件被添加到您的 UI 项目中.此客户端代理成为您的客户端 AdditionService API,您可以像这样使用它:

Performing these steps will generate a client proxy and a properly defined app.config file that are added to your UI project. This client proxy becomes your client-side AdditionService API, and you use it like this:

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结果.

Notice how simple this is. Basically, a client proxy, AdditionServiceClient, is instantiated. Then two Complex objects are created. Finally, the Add() method on the client proxy is invoked, and a Complex result is returned.

幕后发生的事情是,客户端代理的Add() 方法实际上是将两个Complex 对象传递给托管在视窗服务.AdditionService 执行加法,然后返回结果.所有这一切都发生在一个命名管道上,但请注意,这里根本没有命名管道特定的代码! WCF 已经将所有这些复杂性抽象到由 IAdditionService 接口定义的编程模型背后.

What is going on behind the scenes is that the Add() method of the client proxy is actually passing the two Complex objects to the AdditionService WCF service hosted in the Windows service. The AdditionService performs the addition and then returns the result. All of this happens over a named pipe, but notice that there is no named pipe-specific code here at all! WCF has abstracted all of that complexity behind a programming model that is defined by the IAdditionService interface.

我知道有很多信息需要消化,但我希望 WCF 的强大和易于使用是显而易见的.当然,此示例仅涉及 WCF 中可用的所有内容的一小部分.

I know this is a lot of information to digest, but I hope it is evident just how powerful and easy-to-use WCF can be. Of course, this example only hits a small subset of everything that is available within WCF.

但最终,WCF 应该是您用来在 UI 和 Windows 服务之间进行通信的机制.有关更多信息,我强烈推荐 Juval Lowy 的书 Programming WCF ServicesWCF 的所有东西.您还可以访问他的网站 IDesign.net,获取免费的 WCF 代码示例.有关 WCF 的更多介绍,请在 dnrTV 上观看此免费视频.它涵盖了 WCF 的用途,并通过一些易于理解的示例演示了 WCF 编程.

In the end, though, WCF should be the mechanism you use to communicate between your UI and your Windows service. For more information, I would highly recommend Juval Lowy's book Programming WCF Services for all things WCF. You can also visit his website, IDesign.net, for free WCF code samples. For more introduction to WCF, watch this free video at dnrTV. It covers the purpose of WCF and demonstrates WCF programming through some easy-to-follow examples.

这篇关于创建用于监视正在运行的 Windows 服务并与之交互的用户界面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆