如何在 C# 中使用 IPC 命名管道向 WPF 应用程序发送命令以显示气球提示和系统托盘图标?

问题描述 投票:0回答:1

我正在尝试使用 WPF 创建一个服务,该服务将与 WPF 通信,告诉 WPF 显示图标托盘和气球提示。

我设计了以下代码:

App.xaml.cs

namespace Service_UI
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public LogWriter logwriter = new LogWriter();
        System.Windows.Forms.NotifyIcon nIcon = new System.Windows.Forms.NotifyIcon();
        Server IPCS = new Server();

        protected override void OnStartup(StartupEventArgs e)
        {
            nIcon.Icon = new Icon($@"{AppDomain.CurrentDomain.BaseDirectory}/app.ico");
            nIcon.Text = "Service";
            
            Task.Run(async () => await ServerMessages());

            MainWindow mainWindow = new MainWindow();
            mainWindow.Hide(); // Start hidden
        }

        private async Task ServerMessages()
        {
            await IPCS.Connect();

            while (true)
            {
                string serverMessage = await IPCS.ReadMessages();
                if (serverMessage == "Service_Start")
                {
                    Dispatcher.Invoke(() =>
                    {
                        nIcon.Visible = true;
                        nIcon.ShowBalloonTip(5000, "S3ID NetTime Sync", "UI will open up Shortly", System.Windows.Forms.ToolTipIcon.Info);

                        nIcon.ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();
                        nIcon.ContextMenuStrip.Items.Add("Settings", Image.FromFile(@"Resources\cogs_icon.ico"), OnSettingsClicked);
                        nIcon.ContextMenuStrip.Items.Add("Exit", Image.FromFile(@"Resources\exit_icon.ico"), OnExitClicked);

                        // This section is about selecting the View MainWindow and running the application:
                        MainWindow = new MainWindow();
                        MainWindow.Show();
                        logwriter.LogWrite("Application is running now", MessageLevels.Info);
                        //base.OnStartup(e);
                    });
                }
                else if (serverMessage == "Service_Stop")
                {
                    Dispatcher.Invoke(() =>
                    {
                        nIcon.Dispose();
                        //base.OnExit(e);
                        IPCS.Dispose();
                        //base.OnExit(e);
                        MainWindow.Hide();
                    });
                }
            }
        }

        private void OnExitClicked(object sender, EventArgs e)
        {
            nIcon.Dispose();

            MainWindow.Hide();
            logwriter.LogWrite("Application has been minimised", MessageLevels.Info);
        }

        private void OnSettingsClicked(object sender, EventArgs e)
        {
            MainWindow.Show();
            logwriter.LogWrite("Application is displayed", MessageLevels.Info);
        }
    }
}

服务文件

namespace MainService
{
    public partial class MainService : ServiceBase
    {
        Timer tmr = new Timer();
        LogWriter logWriter = new LogWriter();
        Client IPCC = new Client();
        public MainService()
        {
            InitializeComponent();
            this.ServiceName = "MainService";
        }

        protected override  void OnStart(string[] args)
        {
            logWriter.LogWrite($"Service initiated on {DateTime.Now}", MessageLevels.Info);
            tmr.Elapsed += new ElapsedEventHandler(OnElapsedTime);
            tmr.Interval = 5000;
            tmr.Enabled = true;

            logWriter.LogWrite($"Service will initiate the WPF on {DateTime.Now}", MessageLevels.Info);
            Process.Start(@"Service UI.exe");
            
            Task.Run(async () => await IPCC.Connect("Service_Start"));
        }

        protected override void OnStop()
        {
            logWriter.LogWrite($"Service stopped running on {DateTime.Now}", MessageLevels.Info);
            Task.Run(async () => await IPCC.Connect("Service_Stop"));
        }

        private void OnElapsedTime(object source, ElapsedEventArgs e)
        {
            logWriter.LogWrite($"Service started running on {DateTime.Now}", MessageLevels.Info);
        }
    }
}

服务器.cs

namespace InterProcessCommunication
{
    public class Server : IDisposable
    {
        public LogWriter Logwriter = new LogWriter(); 
        public bool ServerConnectionSuccessfull;
        public NamedPipeServerStream ServerPipe;
        private bool disposed = false;

        public Server() { }

        public async Task Connect()
        {
            try
            {
                while (true)
                {
                    ServerPipe = new NamedPipeServerStream("Pipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);

                    Logwriter.LogWrite("Waiting for client connection...", MessageLevels.Info);
                    await ServerPipe.WaitForConnectionAsync();

                    ServerConnectionSuccessfull = true;
                    Logwriter.LogWrite("Client connected.", MessageLevels.Info);

                    // Read the messages from the client
                    string message = await ReadMessages();
                    Logwriter.LogWrite($"Received from client: {message}", MessageLevels.Info);

                    // Close the connection
                    ServerPipe.Disconnect();
                    ServerPipe.Dispose();
                }
            }
            catch (Exception ex)
            {
                Logwriter.LogWrite($"Exception: {ex.Message}", MessageLevels.Error);
                Logwriter.LogWrite($"Stack Trace: {ex.StackTrace}", MessageLevels.Error);
            }


        }

        public void Dispose()
        {
            if (!disposed)
            {
                try
                {
                    ServerPipe?.Dispose();
                }
                catch (Exception ex)
                {
                    Logwriter.LogWrite($"Exception during Dispose: {ex.Message}", MessageLevels.Error);
                    Logwriter.LogWrite($"Stack Trace: {ex.StackTrace}", MessageLevels.Error);
                }
                finally
                {
                    disposed = true;
                }
            }
        }

        public async Task<string> ReadMessages()
        {
            try
            {
                if (ServerPipe == null || !ServerPipe.IsConnected)
                {
                    Logwriter.LogWrite("Pipe server is not initialized.", MessageLevels.Error);
                    return null;
                }

                using var sr = new StreamReader(ServerPipe);
                string message = await sr.ReadToEndAsync();
                return message;
            }
            catch (IOException ioEx)
            {
                Logwriter.LogWrite($"IOException: {ioEx.Message}", MessageLevels.Error);
                Logwriter.LogWrite($"Stack Trace: {ioEx.StackTrace}", MessageLevels.Error);
                return null;
            }
            catch (Exception ex)
            {
                Logwriter.LogWrite($"Exception: {ex.Message}", MessageLevels.Error);
                Logwriter.LogWrite($"Stack Trace: {ex.StackTrace}", MessageLevels.Error);
                return null;
            }
        }
    }
}

客户端.cs

namespace InterProcessCommunication
{
    public class Client
    {
        public LogWriter Logwriter = new LogWriter();
        public bool ClientConnectionSuccessfull;

        public Client() { }

        public async Task Connect(string message)
        {
            while (!ClientConnectionSuccessfull)
            {
                try
                {
                    using (var client = new NamedPipeClientStream(".", "Pipe", PipeDirection.Out, PipeOptions.None))
                    {
                        Logwriter.LogWrite("Connecting to server...", MessageLevels.Info);
                        await client.ConnectAsync();

                        Logwriter.LogWrite("Connected to server.", MessageLevels.Info);
                        ClientConnectionSuccessfull = true;

                        // write data through the pipe
                        await SendMessages(client, message);
                    }
                }
                catch (Exception ex)
                {
                    Logwriter.LogWrite($"Error Message: {ex}", MessageLevels.Error);
                    await Task.Delay(5000); // Retry after 5 seconds if the connection fails
                    ClientConnectionSuccessfull = false;
                }
            }
        }

        public async Task SendMessages(NamedPipeClientStream target,string message)
        {
            try
            {
                using var sw = new StreamWriter(target);
                sw.AutoFlush = true;
                //sw.WriteLine(message);
                await sw.WriteLineAsync(message);

            }
            catch (Exception ex)
            {
                Logwriter.LogWrite($"Exception: {ex.Message}", MessageLevels.Error);
                Logwriter.LogWrite($"Stack Trace: {ex.StackTrace}", MessageLevels.Error);
            }
        }
    }
}

对于我的情况,我想要做的是建立一个开放的连接,客户端在不同时间(基于条件语句)向服务器发送消息,服务器告诉 WPF 要显示什么气球提示以及要显示什么图标(例如带有信息气球提示的启动服务显示图标;当服务关闭时,图标将带有错误气球提示等)。

我似乎无法让它发挥作用。我的代码可能存在什么问题?

我遇到的问题示例:

Thursday, 2024-07-25 13:35:55 LevelI: Service initiated on 25/07/2024 13:35:55
Thursday, 2024-07-25 13:35:55 LevelI: Service will initiate the WPF on 25/07/2024 13:35:55
Thursday, 2024-07-25 13:35:55 LevelI: Connecting to server...
Thursday, 2024-07-25 13:35:55 LevelI: Waiting for client connection...
Thursday, 2024-07-25 13:35:55 LevelI: Connected to server.
Thursday, 2024-07-25 13:35:55 LevelI: Client connected.
Thursday, 2024-07-25 13:35:55 LevelI: Received from client: Service_Start

Thursday, 2024-07-25 13:35:55 LevelI: Waiting for client connection...
Thursday, 2024-07-25 13:36:00 LevelI: Service started running on 25/07/2024 13:36:00
Thursday, 2024-07-25 13:36:05 LevelI: Service started running on 25/07/2024 13:36:05
Thursday, 2024-07-25 13:36:10 LevelI: Service started running on 25/07/2024 13:36:10
Thursday, 2024-07-25 13:36:15 LevelI: Service started running on 25/07/2024 13:36:15
Thursday, 2024-07-25 13:36:20 LevelI: Service started running on 25/07/2024 13:36:20
Thursday, 2024-07-25 13:36:25 LevelI: Service started running on 25/07/2024 13:36:25
Thursday, 2024-07-25 13:36:30 LevelI: Service started running on 25/07/2024 13:36:30
Thursday, 2024-07-25 13:36:35 LevelI: Service started running on 25/07/2024 13:36:35
Thursday, 2024-07-25 13:36:40 LevelI: Service started running on 25/07/2024 13:36:40
Thursday, 2024-07-25 13:36:45 LevelI: Service started running on 25/07/2024 13:36:45
c# wpf ipc named-pipes notifyicon
1个回答
0
投票

第一个问题是,在连接方法中,您创建服务器管道,读取一条消息,然后销毁管道。您可能希望服务器管道长期有效。我可能会为服务器使用一个专用线程,并使用一个事件来通知 UI 消息。

另一个潜在问题似乎是缺乏消息框架。如果没有消息框架,作者可能会写多条消息,或者只写一半消息。

您自己当然可以做到这一点,但我更喜欢使用库来处理所有复杂的事情。通常与 Json 或 Protobuf 等序列化库结合使用来发送任意对象,而不仅仅是字符串。库主要可以分为请求-响应或发布-订阅。 http 和 gRPC 是前者的例子,MQTT 是后者的例子,但还有更多。 这篇文章似乎还有一些建议

© www.soinside.com 2019 - 2024. All rights reserved.