An simple example using a List<Task>
to generate a sequence of Ping requests, using a collection of IP Addresses (in the form of strings) provided by the caller and uses an IProgress<T> delegate to update the UI (Progress<T>
captures the current SynchronizationContext, so code executed by the delegate, is executed in Thread that initialized it; the UI Thread, here).
For each address passed to the method, a PingAsync()
Task is added to the list.
The PingAsync()
method calls Ping.SendPingAsync() and reports back the results, either success or failure, as an object that can represent a PingReply, a PingException or a SocketException in the form of a SocketError
(the Progress()
method translates the SocketError
to an IPStatus, to handle just one type of result. Add more cases if you need a more verbose / detailed response).
The Tasks generate a sequence (an int
value) that is sent to the Progress<T>
delegate, in case it needs it. Here, it's used to select a specific Control from the collection passed to the PingAll()
method.
You can then handle these results in the Progress<T> delegate, to see what happened to the current Ping request and update your Controls.
Task.WhenAll() is then awaited. It will return when all Tasks complete. A Task completes when Ping succeeds or fails or when the specified timeout has elapsed.
The 3 images used to display the result status:
- Green - IPStatus.Success and RoundtripTime <= 30
- Yellow - IPStatus.Success and RoundtripTime > 30
- Red - IPStatus != IPStatus.Success
are taken from the Project's resources. Better not get them from the File System here, you may introduce unnecessary complexity without any advantage.
Assume you initialize the MassPing
class and await the results of PingAll()
using the handler of a Button.Click (note that the handler is 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;
}
Note: for simplicity, the PingAll()
method creates an IProgress<T>
delegate all by itself. You may prefer to pass a delegate to this method instead, from the procedure that initializes the MassPing
class.
This way, you don't need to pass a collection of Controls to the method.
It doesn't matter much if you use this class in a WinForms app, it does (or may) matter if you want to move the class to a Library.
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();
}
}
}