J
James
hello,
trying to create what I thought would be a simple little tool... the nitty gritty is already taken care of by using the NetworkInformation's ping class. I am simply trying to wire it up to a windows form to create a ping utility that is supposed to be able to simultaneously ping several targets. I'm using the async method in the Ping class which I thought would be using threads other than the main UI thread, and so my UI would therefore remain responsive. I'm sure there are already tools out there that do what I'm after but I like creating my own. There is group at work that often is asked to basically throw a ping on a target and watch that it goes down, and then back up. They often have to do this for a handfull of targets at a time... and so they open multiple command windows and use the windows builtin ping.exe -t command. I'm just looking to make this app so there is one place they can do all their ping monitoring from.
so, I set out to use the existing.net ping class to do the actual pinging, a simple one form app using a listview control to display all the targets they have a ping on which lets them easily see an up or down status, and the ability to select one of the listview items to watch the real time replies come in via a textbox control.
I planned to implement start, stop, and of course remove buttons to manage the listview items, which should each represent a seperate target being pinged, each in a separate thread. The code I have so far is just enough to test out how the core funtionality will work, and it sucks. It works, but performs horribly, the UI does not remain responsive... very slow to react.
below is the code... whats wrong with it? I'm sure a lot considering I'm not that experienced in this kind of thing but would really like to learn how to make it work, correctly. Lots of questions, like do I even need to synchronize access to the listView control using the lock statement? With or without it the performance was still horrible, but no errors.
namespace Pingler
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
private object myLock = new object();
private void btnAdd_Click(object sender, EventArgs e)
{
// access to lstViewTargets must be synchronized since this is being designed
// to support multiple simultaneous pings
lock (myLock)
{
lstViewTargets.BeginUpdate();
stateObject stateBall = new stateObject();
stateBall.IP = txtIP.Text.Trim();
stateBall.MyListViewItem.Text = txtIP.Text.Trim();
stateBall.MyListViewItem.SubItems.Add("Waiting...");
stateBall.MyListViewItem.SubItems.Add("Waiting...");
lstViewTargets.Items.Add(stateBall.MyListViewItem);
lstViewTargets.EndUpdate();
// When the PingCompleted event is raised,
// the PingCompletedCallback method is called.
stateBall.pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
// Send the ping asynchronously.
stateBall.pingSender.SendAsync(stateBall.IP, stateBall.Timeout, stateBall.Buffer, stateBall.Options, stateBall);
}
}
private void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
stateObject stateBall = (stateObject)e.UserState;
PingReply reply = e.Reply;
if (stateBall.FirstTime)
{
lock (myLock)
{
//first ping sent so initialize listview control with fisrt ping result
if (reply.Status == IPStatus.Success)
{
stateBall.MyListViewItem.SubItems[1].Text = "Up";
stateBall.MyListViewItem.StateImageIndex = 1;
}
else
{
stateBall.MyListViewItem.SubItems[1].Text = "Down";
stateBall.MyListViewItem.StateImageIndex = 0;
}
stateBall.MyListViewItem.SubItems[2].Text = reply.Status.ToString();
//flip first time tracker
stateBall.FirstTime = false;
}
}
else if (!stateBall.FirstTime)
{
//not first ping sent, only update gui if something has changed
if (stateBall.LastReply != reply.Status.ToString())
{
//status changed from last ping, update listview control
lock (myLock)
{
if (reply.Status == IPStatus.Success)
{
stateBall.MyListViewItem.SubItems[0].Text = "Up";
stateBall.MyListViewItem.StateImageIndex = 0;
}
else
{
stateBall.MyListViewItem.SubItems[0].Text = "Down";
stateBall.MyListViewItem.StateImageIndex = 1;
}
stateBall.MyListViewItem.SubItems[1].Text = reply.Status.ToString();
}
}
} //end of first time or not branches
//print detail if its the selected item
if (stateBall.MyListViewItem.Selected)
{
if (reply.Status == IPStatus.Success)
{
txtOutput.Text += Environment.NewLine;
txtOutput.Text += "Address: " + reply.Address.ToString() + " - ";
txtOutput.Text += "RoundTrip time: " + reply.RoundtripTime + " - ";
txtOutput.Text += "Time to live: " + reply.Options.Ttl + " - ";
txtOutput.Text += "Don't fragment: " + reply.Options.DontFragment + " - ";
txtOutput.Text += "Buffer size: " + reply.Buffer.Length;
}
else
{
txtOutput.Text += Environment.NewLine + "failed: " + reply.Status.ToString();
}
}
//store this reply's status in stateobject for use on next iteration
stateBall.LastReply = reply.Status.ToString();
//no need to ping it to death, pause 1.5 seconds
Thread.Sleep(1500);
if (stateBall.counter < 20)
{
stateBall.pingSender.SendAsync(stateBall.IP, stateBall.Timeout, stateBall.Buffer, stateBall.Options, stateBall);
stateBall.counter++;
}
}
}
public class stateObject
{
public Ping pingSender = new Ping();
public string Data;
public byte[] Buffer;
public int Timeout;
public PingOptions Options;
public string IP;
public int counter;
public ListViewItem MyListViewItem = new ListViewItem();
public bool PrintDetail;
public string LastReply;
public bool FirstTime;
public stateObject()
{
// Create a buffer of 32 bytes of data to be transmitted.
Data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
Buffer = Encoding.ASCII.GetBytes(Data);
// Wait 6 seconds for a reply.
Timeout = 6000;
// Set options for transmission:
// The data can go through 64 gateways or routers
// before it is destroyed, and the data packet
// cannot be fragmented.
Options = new PingOptions(64, true);
counter = 0;
PrintDetail = false;
FirstTime = true;
LastReply = string.Empty;
}
}
}
trying to create what I thought would be a simple little tool... the nitty gritty is already taken care of by using the NetworkInformation's ping class. I am simply trying to wire it up to a windows form to create a ping utility that is supposed to be able to simultaneously ping several targets. I'm using the async method in the Ping class which I thought would be using threads other than the main UI thread, and so my UI would therefore remain responsive. I'm sure there are already tools out there that do what I'm after but I like creating my own. There is group at work that often is asked to basically throw a ping on a target and watch that it goes down, and then back up. They often have to do this for a handfull of targets at a time... and so they open multiple command windows and use the windows builtin ping.exe -t command. I'm just looking to make this app so there is one place they can do all their ping monitoring from.
so, I set out to use the existing.net ping class to do the actual pinging, a simple one form app using a listview control to display all the targets they have a ping on which lets them easily see an up or down status, and the ability to select one of the listview items to watch the real time replies come in via a textbox control.
I planned to implement start, stop, and of course remove buttons to manage the listview items, which should each represent a seperate target being pinged, each in a separate thread. The code I have so far is just enough to test out how the core funtionality will work, and it sucks. It works, but performs horribly, the UI does not remain responsive... very slow to react.
below is the code... whats wrong with it? I'm sure a lot considering I'm not that experienced in this kind of thing but would really like to learn how to make it work, correctly. Lots of questions, like do I even need to synchronize access to the listView control using the lock statement? With or without it the performance was still horrible, but no errors.
namespace Pingler
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
private object myLock = new object();
private void btnAdd_Click(object sender, EventArgs e)
{
// access to lstViewTargets must be synchronized since this is being designed
// to support multiple simultaneous pings
lock (myLock)
{
lstViewTargets.BeginUpdate();
stateObject stateBall = new stateObject();
stateBall.IP = txtIP.Text.Trim();
stateBall.MyListViewItem.Text = txtIP.Text.Trim();
stateBall.MyListViewItem.SubItems.Add("Waiting...");
stateBall.MyListViewItem.SubItems.Add("Waiting...");
lstViewTargets.Items.Add(stateBall.MyListViewItem);
lstViewTargets.EndUpdate();
// When the PingCompleted event is raised,
// the PingCompletedCallback method is called.
stateBall.pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
// Send the ping asynchronously.
stateBall.pingSender.SendAsync(stateBall.IP, stateBall.Timeout, stateBall.Buffer, stateBall.Options, stateBall);
}
}
private void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
stateObject stateBall = (stateObject)e.UserState;
PingReply reply = e.Reply;
if (stateBall.FirstTime)
{
lock (myLock)
{
//first ping sent so initialize listview control with fisrt ping result
if (reply.Status == IPStatus.Success)
{
stateBall.MyListViewItem.SubItems[1].Text = "Up";
stateBall.MyListViewItem.StateImageIndex = 1;
}
else
{
stateBall.MyListViewItem.SubItems[1].Text = "Down";
stateBall.MyListViewItem.StateImageIndex = 0;
}
stateBall.MyListViewItem.SubItems[2].Text = reply.Status.ToString();
//flip first time tracker
stateBall.FirstTime = false;
}
}
else if (!stateBall.FirstTime)
{
//not first ping sent, only update gui if something has changed
if (stateBall.LastReply != reply.Status.ToString())
{
//status changed from last ping, update listview control
lock (myLock)
{
if (reply.Status == IPStatus.Success)
{
stateBall.MyListViewItem.SubItems[0].Text = "Up";
stateBall.MyListViewItem.StateImageIndex = 0;
}
else
{
stateBall.MyListViewItem.SubItems[0].Text = "Down";
stateBall.MyListViewItem.StateImageIndex = 1;
}
stateBall.MyListViewItem.SubItems[1].Text = reply.Status.ToString();
}
}
} //end of first time or not branches
//print detail if its the selected item
if (stateBall.MyListViewItem.Selected)
{
if (reply.Status == IPStatus.Success)
{
txtOutput.Text += Environment.NewLine;
txtOutput.Text += "Address: " + reply.Address.ToString() + " - ";
txtOutput.Text += "RoundTrip time: " + reply.RoundtripTime + " - ";
txtOutput.Text += "Time to live: " + reply.Options.Ttl + " - ";
txtOutput.Text += "Don't fragment: " + reply.Options.DontFragment + " - ";
txtOutput.Text += "Buffer size: " + reply.Buffer.Length;
}
else
{
txtOutput.Text += Environment.NewLine + "failed: " + reply.Status.ToString();
}
}
//store this reply's status in stateobject for use on next iteration
stateBall.LastReply = reply.Status.ToString();
//no need to ping it to death, pause 1.5 seconds
Thread.Sleep(1500);
if (stateBall.counter < 20)
{
stateBall.pingSender.SendAsync(stateBall.IP, stateBall.Timeout, stateBall.Buffer, stateBall.Options, stateBall);
stateBall.counter++;
}
}
}
public class stateObject
{
public Ping pingSender = new Ping();
public string Data;
public byte[] Buffer;
public int Timeout;
public PingOptions Options;
public string IP;
public int counter;
public ListViewItem MyListViewItem = new ListViewItem();
public bool PrintDetail;
public string LastReply;
public bool FirstTime;
public stateObject()
{
// Create a buffer of 32 bytes of data to be transmitted.
Data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
Buffer = Encoding.ASCII.GetBytes(Data);
// Wait 6 seconds for a reply.
Timeout = 6000;
// Set options for transmission:
// The data can go through 64 gateways or routers
// before it is destroyed, and the data packet
// cannot be fragmented.
Options = new PingOptions(64, true);
counter = 0;
PrintDetail = false;
FirstTime = true;
LastReply = string.Empty;
}
}
}