【问题标题】:C#: GUI to display realtime messages from Windows ServiceC#:用于显示来自 Windows 服务的实时消息的 GUI
【发布时间】:2011-05-11 14:26:29
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: c# winforms windows-services event-log


    【解决方案1】:

    我发现命名管道与系统托盘应用程序的通信是显示来自 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 服务中。

    管道详情请见Example of Named Pipes 对于系统托盘应用程序,请参阅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

    【讨论】:

      【解决方案2】:

      我知道这已经被提及,但使用 Windows Communication Foundation (WCF)。具体来说,使用Juval LowyProgramming WCF Services 的作者开发的Publish-Subscribe Framework。详细描述在this excellent MSDN article,源代码在Lowy's website免费提供。

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

      我在我的项目中使用了这个设置,效果非常好。

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          【解决方案4】:

          您可以做的是让 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

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

          【讨论】:

          • 很好的答案,用于使用平台标准机制并提供全面的文档参考。
          【解决方案5】:

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

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

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

          【讨论】:

          • 我个人喜欢管道,但这可能是因为我对它们最熟悉。您还可以使用命名管道而不是标准管道。
          • 文件方法实际上是我想到的一个想法,但找不到一种干净的方法。 FileSystemWatcher 看起来是一个非常好的解决方案!我不认为 Pipes 为我提供了一个干净的解决方案......如果我理解正确,该服务将需要在启动时了解 GUI(重定向它的 StandardOutput),我不确定它会......是这是正确的,还是我错过了什么?
          • 如果您使用命名管道,则不会。您创建一个具有全局唯一名称的管道(通常类似于“\\.\pipe\Company Name\Application Name\Pipe Name”)。该服务在启动时创建管道,并且 GUI 应用程序可以通过名称连接到该管道。
          【解决方案6】:

          观察者模式!

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              【解决方案8】:

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

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2019-10-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-02-08
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多