创建用于监视正在运行的Windows服务并与之交互的用户界面

Nat*_*ley 14 .net c# windows-services .net-3.5

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

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

Mat*_*vis 37

不要使用远程处理! 虽然它肯定会有效,但微软表示远程处理是一项传统技术,所有新的分布式应用程序都应该使用WCF开发.有关详细信息,请参见此处

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

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

一旦定义了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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

下一步是托管此WCF服务,以便UI可以使用它.由于您将使用OnStart()Windows服务,因此在Windows服务的回调中可以轻松地托管您的WCF 服务,如下所示:

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

剩下要做的唯一事情是为Windows服务定义一个app.config文件,该文件将配置WCF服务的某些必需方面.这似乎有点矫枉过正,但记住两件事.首先,当您向项目添加WCF服务类时,Visual Studio会自动为您提供基本的app.config文件.其次,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>
Run Code Online (Sandbox Code Playgroud)

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

<endpoint name="AdditionService"
          address="net.tcp://<machine hostname here>/AdditionService"
          binding="netTcpBinding"
          contract="AdditionServiceNamespace.IAdditionService" />
Run Code Online (Sandbox Code Playgroud)

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

而已!您现在可以在Windows服务中托管WCF服务,供UI使用.

那么UI方面(即客户端)如何工作?

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

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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意这是多么简单.基本上,AdditionServiceClient实例化客户端代理.然后Complex创建两个对象.最后,Add()调用客户端代理上的方法,并Complex返回结果.

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

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

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