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

发布于 2024-10-02 14:41:54 字数 573 浏览 5 评论 0原文

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

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

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

更新
很多有用的答案,我喜欢有很多方法可以解决问题!我想我将实现一个基于自托管 WCF 的解决方案。我仍然对细节很了解,因为我正在尝试了解 WCF(我相信它在其他项目中对我来说非常有用)...但到目前为止,我已经找到了视频 此处是最有帮助的介绍性操作方法。

I've written a C# windows service which can write messages to a custom EventLog or to any number of files. These messages are all marked with some priority (so, for example, only ERRORs and WARNINGs get stored in the EventLog, but if desired a lot more can be stored to a file).

What I'd like to do now is create a GUI that can listen for these messages and display them in real-time. Allowing a user to watch the current messages (at whatever their desired priority level), without the need to store everything to a file. I assume this is a separate program with some form of hook into the service, but I'm unsure of where to start.

This is my first real windows service, so I seem to be missing some keywords for finding out how to do this... Are there any code samples, tutorials, references, etc. for how to do something like this?

UPDATE
A lot of helpful answers, I love it when there's many ways to solve a problem! I think I'm going to implement a self-hosting WCF based solution. I'm still very light on the details as I'm trying to learn about WCF (I believe it will prove quite useful for me in other projects)... but thus far, I've found the videos here to be the most helpful as an intro how-to.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

堇年纸鸢 2024-10-09 14:41:54

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

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

What you can do is have the windows service have way of registering for an event (you can do this through using Windows Communication Foundation). When your error comes up, it fires that event, and your winforms app will be notified. It's called a duplex contract:

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

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

Actually the really cool thing is that you can have multiple applications listening this way too. So you can display it on screen, and have another application log it etc. without the two external apps knowing anything about each other.

江湖彼岸 2024-10-09 14:41:54

我知道这已经被提及,但请使用 Windows Communication Foundation (WCF)。具体来说,请使用发布-订阅框架Juval Lowy 开发,对 WCF 服务进行编程这篇优秀的 MSDN 文章 中描述了详细信息,以及源代码可从 Lowy 网站免费获取。

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

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

I know this has already been mentioned, but use Windows Communication Foundation (WCF). Specifically, use the Publish-Subscribe Framework developed by Juval Lowy, author of Programming WCF Services. The details are described in this excellent MSDN article, and the source code is available for free at Lowy's website.

The neat thing about this framework is that it decouples the publisher, e.g., your Windows service, from any subscribers, e.g., your GUI. The publisher "publishes" events that are of interest to the Pub/Sub Service, which is always available. From the publisher's point of view, it doesn't matter if there are any subscribers or not. The Pub/Sub Service takes care of routing events to any and all registered subscribers. In this way, your Windows service publishes events as they occur, your GUI will subscribe/unsubscribe to the Pub/Sub Service when it loads/exits, and the Pub/Sub Service will notify your GUI as events occur.

I have used this setup in my project, and it works extremely well.

一身仙ぐ女味 2024-10-09 14:41:54

alt text

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

alt text

I've actually used the BitFactory Logger that has a socket logger that you can use for this purpose.

孤独难免 2024-10-09 14:41:54

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

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

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

What you're describing is inter-process communication, which can get messy.

The easiest and most elegant, but probably least reactive, is to have the service write entries as small text files (or append to a log), and have your GUI use a FileSystemWatcher to detect new files or updates to the log file, and read the file. You have to ensure that the service opens the file for appending in a "shared" manner, allowing read-only access while it's writing. Otherwise, you'll block one process or the other, probably causing lost messages.

Processes can communicate through some built-in pipelines. if your service writes messages to its StandardOutput pipe, the GUI can remotely attach a listener and receive events when messages are written. This is probably the most elegant non-file way to do what you want. Research the Process class, especially the OutputDataReceived event. You'll have to go look for the process from your GUI by some uniquely identifying information, using GetProcess().

诗酒趁年少 2024-10-09 14:41:54

我发现与系统托盘应用程序的命名管道通信是显示来自 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,步骤%203.% 20添加%20an%20icon%20到%20the%20NotifyIcon
以下是有关使用 TopShelf NuGet 包的提示,该包允许您将 Windows 服务作为控制台应用程序进行调试:https://www.codeproject.com/Articles/881511/SignalR-with-Self-hosted-Windows-Service

I've found that a Named Pipe communication with a System Tray application was the simplest way to display notifications from a Windows Service. This is because in Windows 10 services run with different permissions than the logged in user, so the notification app needs to perform IPC with the service.

Here you could put this into the server:

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();
                }
            });
        }
    }
}

Then put this into your client:

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());
            }
        }
    }
}

Then change your ConsoleServerApp to a Winforms application so that it can display the notification whenever the windows service sends it a message:

    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();
            }
        });
    }

Then put the ConsoleClientApp into your Windows Service.

For details on the pipe please see Example of Named Pipes
For the System Tray application please see 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
Here are tips on using the TopShelf NuGet package which allows you to debug your Windows Service as a Console Application: https://www.codeproject.com/Articles/881511/SignalR-with-Self-hosted-Windows-Service

雄赳赳气昂昂 2024-10-09 14:41:54

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

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

You need to look for "synchronization" and "inter-process communication". In your case the service would use the global event or semaphore to signal presence of data, and GUI process would check event/semaphore state and read the updates from event log or from file.

There exist more complicated scenarios, but the above is a good starting point.

淡紫姑娘! 2024-10-09 14:41:54

观察者模式!

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

Observer pattern!

Perhaps a delegate for all observable models that you can hook into with your service?

心意如水 2024-10-09 14:41:54

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

.NET remoting over IPC channel.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文