首页 > 解决方案 > Ping往返时间超过50ms时如何更改PictureBox的图像?

问题描述

我正在开发一个 ping 多个主机的应用程序。主机列表是从 CSV 文件中读取的。
当有响应时,程序会显示一个绿色勾号,当 ping 失败时会显示一个红叉。

这很好用,但是当往返时间超过50ms.

这是我目前的代码:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Thread.Sleep(500);
    Parallel.For(0, ipAddress.Count(), (i, loopState) =>
    {
        Ping ping = new Ping();
        PingReply pingReply = ping.Send(ipAddress[i].ToString());
        this.BeginInvoke((Action)delegate ()
        {
            pictureboxList[i].BackgroundImage = (pingReply.Status == IPStatus.Success) ? Image.FromFile(@"C:\Users\PC\Downloads\green.png") : Image.FromFile(@"C:\Users\PC\Downloads\red.png");
        });
    });
}

有没有办法做到这一点?

标签: c#.netwinformsping

解决方案


一个简单的示例,使用 aList<Task>生成一系列 Ping 请求,使用调用者提供的 IP 地址集合(以字符串的形式)并使用IProgress<T>委托来更新 UI(Progress<T>捕获当前 SynchronizationContext,因此代码由委托执行,在初始化它的线程中执行;这里是 UI 线程)。
对于传递给该方法的每个地址,都会将一个PingAsync()任务添加到列表中。

PingAsync()方法调用Ping.SendPingAsync()并将结果(成功或失败)报告为一个对象,该对象可以以 a的形式表示PingReplyPingExceptionSocketExceptionSocketError(该Progress()方法将 转换SocketErrorIPStatus,仅用于处理一种结果。如果您需要更详细/详细的回复,请添加更多案例)。

任务生成一个序列(一个int值),发送给Progress<T>委托,以备不时之需。PingAll()在这里,它用于从传递给方法的集合中选择特定的 Control 。

然后,您可以在Progress<T>委托中处理这些结果,以查看当前 Ping 请求发生了什么并更新您的控件。

然后等待Task.WhenAll() 。它会在所有任务完成后返回。当 Ping 成功或失败或指定的超时时间已过时,任务完成。

用于显示结果状态的 3 个图像:

  • 绿色 - IPStatus.Success 和 RoundtripTime <= 30
  • 黄色 - IPStatus.Success 和 RoundtripTime > 30
  • 红色 - IPStatus != IPStatus.Success

取自项目的资源。最好不要从此处的文件系统中获取它们,您可能会引入不必要的复杂性而没有任何优势。

假设您初始化MassPing类并等待PingAll()使用 Button.Click 的处理程序的结果(注意处理程序是async):

private async void btnMassPing_Click(object sender, EventArgs e)
{
    btnMassPing.Enabled = false;
    // Create a collection of existing Controls that have a BackgroundImage property
    var controls = new Control[] { /* a list of existing Controls */ };
    // The Addresses count must match the Controls'
    var addresses = [An array of strings representing IpAddresses or Host names]
    var massPing = new MassPing();
    await massPing.PingAll(addresses, controls, 2000);
    btnMassPing.Enabled = true;
}

注意:为简单起见,该PingAll()方法IProgress<T>自己创建了一个委托。您可能更愿意将委托从初始化MassPing类的过程传递给此方法。
这样,您无需将控件集合传递给该方法。
如果您在 WinForms 应用程序中使用此类并不重要,如果您想将该类移动到库中,它确实(或可能)重要。

using System.Collections.Generic;
using System.Drawing;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Windows.Forms;

public class MassPing
{
    private Bitmap imageRed = Properties.Resources.Red;
    private Bitmap imageGreen = Properties.Resources.Green;
    private Bitmap imageYellow = Properties.Resources.Yellow;

    public async Task PingAll(string[] addresses, Control[] controls, uint timeout = 2000)
    {
        // Add more checks on the arguments
        if (addresses.Length != controls.Length) {
            throw new ArgumentException("Collections length mismatch");
        }

        var obj = new object();
        var tasks = new List<Task>();

        var progress = new Progress<(int sequence, object reply)>(report => {
            lock (obj) {
                // Use the reply Status value to set any other Control. In this case, 
                // it's probably better to have a UserControl that shows multiple values
                var status = IPStatus.Unknown;
                if (report.reply is PingReply pr) {
                    status = pr.Status;
                    Bitmap img = status is IPStatus.Success
                        ? pr.RoundtripTime > 30 ? imageYellow : imageGreen
                        : imageRed;
                    controls[report.sequence].BackgroundImage?.Dispose();
                    controls[report.sequence].BackgroundImage = img;
                }
                else if (report.reply is SocketError socErr) {
                    if (socErr == SocketError.HostNotFound) {
                        status = IPStatus.DestinationHostUnreachable;
                    }
                    controls[report.sequence].BackgroundImage?.Dispose();
                    controls[report.sequence].BackgroundImage = imageRed;
                }
            }
        });

        // Add all tasks
        for (int seq = 0; seq < addresses.Length; seq++) {
            tasks.Add(PingAsync(addresses[seq], (int)timeout, seq, progress));
        }
        // Could use some exception handling 
        await Task.WhenAll(tasks);
    }

    private async Task PingAsync(string ipAddress, int timeOut, int sequence, IProgress<(int seq, object reply)> progress)
    {
        var buffer = new byte[32];
        var ping = new Ping();

        try {
            var options = new PingOptions(64, true);
            PingReply reply = await ping.SendPingAsync(ipAddress, timeOut, buffer, options);
            progress.Report((sequence, reply));
        }
        catch (PingException pex) {
            if (pex.InnerException is SocketException socEx) {
                progress.Report((sequence, socEx.SocketErrorCode));
            }
        }
        finally {
            ping.Dispose();
        }
    }
}

推荐阅读