繁体   English   中英

C#:用于显示来自 Windows 服务的实时消息的 GUI

[英]C#: GUI to display realtime messages from Windows Service

我编写了一个 C# windows 服务,它可以将消息写入自定义 EventLog 或任意数量的文件。 这些消息都标有某些优先级(例如,只有错误和警告会存储在 EventLog 中,但如果需要,可以将更多信息存储到文件中)。

我现在想做的是创建一个 GUI 来监听这些消息并实时显示它们。 允许用户观看当前消息(以他们所需的任何优先级),而无需将所有内容存储到文件中。 我认为这是一个单独的程序,具有某种形式的挂钩到服务,但我不确定从哪里开始。

这是我的第一个真正的 Windows 服务,所以我似乎缺少一些关键字来找出如何执行此操作...是否有任何代码示例、教程、参考资料等来说明如何执行此类操作?

更新
很多有用的答案,当有很多方法可以解决问题时,我喜欢它! 我想我将实施一个基于自托管 WCF 的解决方案。 当我试图了解 WCF 时,我仍然对细节非常了解(我相信它会在其他项目中证明对我非常有用)......但到目前为止,我发现这里的视频是最多的作为介绍操作方法很有帮助。

你可以做的是让 Windows 服务有注册事件的方式(你可以通过使用 Windows Communication Foundation 来做到这一点)。 当您的错误出现时,它会触发该事件,并且您的 winforms 应用程序将收到通知。 它被称为双工合同:

http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/0eb69998-0388-4731-913e-fb205528d374/

http://msdn.microsoft.com/en-us/library/ms731184.aspx

实际上,真正酷的是您也可以让多个应用程序以这种方式进行监听。 因此,您可以在屏幕上显示它,并让另一个应用程序记录它等,而无需两个外部应用程序相互了解。

我知道这已经提到过,但是使用 Windows Communication Foundation (WCF)。 具体来说,请使用Programming WCF Services 的作者Juval Lowy开发的发布-订阅框架 详细信息在这篇优秀的 MSDN 文章中进行了描述,源代码可在Lowy 的网站上免费获得。

这个框架的巧妙之处在于它将发布者(例如您的 Windows 服务)与任何订阅者(例如您的 GUI)分离。 发布者“发布”对始终可用的发布/订阅服务感兴趣的事件。 从发布者的角度来看,是否有订阅者并不重要。 Pub/Sub 服务负责将事件路由到任何和所有注册订阅者。 这样,您的 Windows 服务会在事件发生时发布事件,您的 GUI 将在加载/退出时订阅/取消订阅 Pub/Sub 服务,并且 Pub/Sub 服务将在事件发生时通知您的 GUI。

我在我的项目中使用了这个设置,它运行得非常好。

替代文字

我实际上使用了BitFactory Logger ,它有一个可以用于此目的的套接字记录器。

您所描述的是进程间通信,这可能会变得混乱。

最简单、最优雅但可能反应最少的方法是让服务将条目作为小文本文件写入(或附加到日志),并让您的 GUI 使用 FileSystemWatcher 来检测新文件或日志文件的更新,并读取文件。 您必须确保服务以“共享”方式打开文件以进行附加,从而在写入时允许只读访问。 否则,您将阻塞一个或另一个进程,可能会导致消息丢失。

进程可以通过一些内置管道进行通信。 如果您的服务将消息写入其 StandardOutput 管道,则 GUI 可以远程附加侦听器并在写入消息时接收事件。 这可能是做你想做的最优雅的非文件方式。 研究 Process 类,尤其是 OutputDataReceived 事件。 您必须使用 GetProcess() 通过一些唯一标识信息从 GUI 中查找进程。

您需要寻找“同步”和“进程间通信”。 在您的情况下,服务将使用全局事件或信号量来表示数据的存在,并且 GUI 进程将检查事件/信号量状态并从事件日志或文件中读取更新。

存在更复杂的场景,但以上是一个很好的起点。

观察者模式!

也许您可以将所有可观察模型与您的服务挂钩?

.NET 通过 IPC 通道进行远程处理。

我发现与系统托盘应用程序的命名管道通信是显示来自 Windows 服务的通知的最简单方法。 这是因为在 Windows 10 中,服务以与登录用户不同的权限运行,因此通知应用程序需要对服务执行 IPC。

在这里,您可以将其放入服务器:

using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleServerApp
{
    class Program
    {
        static void Main(string[] args)
        {
            StartServer();
            Task.Delay(1000).Wait();
        }

        static void StartServer()
        {
            Task.Factory.StartNew(() =>
            {
                var server = new NamedPipeServerStream("PipesOfPiece");
                server.WaitForConnection();
                StreamReader reader = new StreamReader(server);
                StreamWriter writer = new StreamWriter(server);
                while (true)
                {
                    var line = reader.ReadLine();
                    writer.WriteLine(String.Join("", line.Reverse()));
                    writer.Flush();
                }
            });
        }
    }
}

然后将其放入您的客户端:

using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleClientApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //Client
            var client = new NamedPipeClientStream("PipesOfPiece");
            client.Connect();
            StreamReader reader = new StreamReader(client);
            StreamWriter writer = new StreamWriter(client);

            while (true)
            {
                string input = Console.ReadLine();
                if (String.IsNullOrEmpty(input)) break;
                writer.WriteLine(input);
                writer.Flush();
                Console.WriteLine(reader.ReadLine());
            }
        }
    }
}

然后将您的 ConsoleServerApp 更改为 Winforms 应用程序,以便它可以在 Windows 服务向其发送消息时显示通知:

    public Form1()
    {
        InitializeComponent();

        StartServer();
        Task.Delay(_threadJoinTimeout).Wait();

    }

    public void DisplayMessage()
    {
        this.notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
        this.notifyIcon1.BalloonTipText = "Welcomd!";
        this.notifyIcon1.BalloonTipTitle = "Title";
        this.notifyIcon1.ShowBalloonTip(2000);
    }

    void StartServer()
    {
        Task.Factory.StartNew(() =>
        {
            var server = new NamedPipeServerStream("PipesOfPiece");
            server.WaitForConnection();
            StreamReader reader = new StreamReader(server);

            while (true)
            {
                var line = reader.ReadLine();
                DisplayMessage();
            }
        });
    }

然后将 ConsoleClientApp 放入您的 Windows 服务中。

有关管道的详细信息,请参见命名管道示例对于系统托盘应用程序,请参见http://www.tutorialspanel.com/create-system-tray-icon-windows-forms-application-using-c-vb-net/ #:~:text=Below%20is%20an%20example%20of%20how%20to%20create,Step%203.%20Add%20an%20icon%20to%20the%20NotifyIcon以下是使用 TopShelf NuGet 包的提示将您的 Windows 服务作为控制台应用程序进行调试: https : //www.codeproject.com/Articles/881511/SignalR-with-Self-hosted-Windows-Service

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM