0

I have a time consuming task that tests a couple of network connections. In my example below I have confined it to one connection. Normally the connection returns quickly but it could happen that the connection cannot be made so that the socket times out. During this time I would like to display an "idler" gif in the Form, when the connection succeeds the app should change the Image in the Form to some green check icon or in case of a failing connection a red icon "stopper" should be displayed.

Somehow I cannot get the idler gif become visible and animated. To simulate a failing connection one can enter an invalid port # or non existent address.

Any clues what I'm missing or doing wrong?

    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        ///         
     #region BackgroundWorker

        private System.ComponentModel.BackgroundWorker backgroundWorker = new System.ComponentModel.BackgroundWorker();
        private delegate void SomeLongRunningMethodHandler(object sender, EventArgs e);

     #endregion
        private System.ComponentModel.IContainer components = null;

        Button button,button2;
        static Socket socket;
        static bool success;
        private static bool done;
        private Label lbl1;
        private Label lbl2;
        private TextBox address;
        private TextBox port;
        private PictureBox p;
        private static String port_number,host;


        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void RunTest(object o,EventArgs e)
        {
            p.Visible = true;
            SomeLongRunningMethodHandler synchronousFunctionHandler =
                default(SomeLongRunningMethodHandler);
            synchronousFunctionHandler = 
                TestConnection;
            synchronousFunctionHandler.Invoke(o, e);
        }

        private void TestConnection(object o, EventArgs e)
        {
            host = address.Text;
            port_number = port.Text;
           if (null != socket)
            {
                socket.Close();
            }

             Thread.Sleep(1000);
             IPEndPoint myEndpoint = new IPEndPoint(0, 0);


            IPHostEntry remoteMachineInfo = Dns.GetHostEntry(host);
            IPEndPoint serverEndpoint = new IPEndPoint(remoteMachineInfo.AddressList[0],
                int.Parse(port_number));



            socket = new Socket(myEndpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                socket.Connect(serverEndpoint);
                success = true;
                p.Image = global::BlockingUI.Properties.Resources.accept;
            }
            catch
            {
                success = false;
                p.Image = global::BlockingUI.Properties.Resources.stopper;
            }
            done = true;
        }  


        private void ExitApp(object o, EventArgs e)
        {
            Application.Exit();
        }

    }
Sven Grosen
  • 5,616
  • 3
  • 30
  • 52
Krischu
  • 1,024
  • 2
  • 15
  • 35

2 Answers2

3

If you truly want to use a BackgroundWorker, this (or something close to it), should point you in the right direction. You are creating a BackgroundWorker object but then doing nothing with it. BackgroundWorker objects are not allowed to access UI elements as they are owned by the UI thread, but you can pass in UI values, as I do here with a Tuple (you could create your own class to hold these values if you want too) and then modify the UI from the UI thread once the worker is complete.

private struct ConnectionProperties
{
    public string Address;
    public string Port;
}

private void RunTest(object o, EventArgs e)
{
    BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();

    worker.RunWorkerCompleted += TestComplete;
    worker.DoWork += TestConnection;

    p.Visible = true;

    //worker.RunWorkerAsync(new Tuple<string, string>(address.Text, port.Text));
    worker.RunWorkerAsync(new ConnectionProperties{ Address = address.Text, Port = port.Text });
}

private void TestConnection(object sender, DoWorkEventArgs e)
{
    bool success = false;
    //var connection = e.Argument as Tuple<string, string>;
    var connection = (ConnectionProperties)e.Argument;

    if (null != socket)
    {
        socket.Close();
    }

    Thread.Sleep(1000);

    IPEndPoint myEndpoint = new IPEndPoint(0, 0);
    IPHostEntry remoteMachineInfo = Dns.GetHostEntry(connection.Address);
    IPEndPoint serverEndpoint = new IPEndPoint(remoteMachineInfo.AddressList[0],
      int.Parse(connection.Port));

    socket = new Socket(myEndpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

    try
    {
        socket.Connect(serverEndpoint);
        success = true;
    }
    catch
    {
        success = false;
    }

    e.Result = success;
}

// Define other methods and classes here
private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error == null)
    {
        var success = (bool)e.Result;
        if (success)
        {
            p.Image = global::BlockingUI.Properties.Resources.accept;
        }
        else
        {
            p.Image = global::BlockingUI.Properties.Resources.stopper;
        }
    }
    else
    {
        //unexpected error, show message or whatever
    }
}
Sven Grosen
  • 5,616
  • 3
  • 30
  • 52
  • Thanks. I wanted to adopt your suggestions and when trying to use the line: var connection = e.Argument as Tuple; I'm getting a compilation error: The type or namespace 'Tuple' could not be found. Are you missing a using directive or assemby reference? – Krischu Mar 14 '14 at 14:51
  • What version of .NET are you using? `Tuple` is only available in 4.0+. As I mentioned above, you don't have to use this built-in class, you could build your own simple class or struct that has Address and Text members. If you need more detail on that, I could update the answer. – Sven Grosen Mar 14 '14 at 14:58
  • Thanks. I could change over to VS 2010 with .NET 4.0 or 4.5 but for the moment it would be helpful if you could update the solution and point the way to either pass an array of strings or use class members. – Krischu Mar 14 '14 at 15:47
  • @Krischu I've updated my answer to provide an implementation that uses a simple `struct`. That will work in any version of .NET that has `BackgroundWorker`. – Sven Grosen Mar 14 '14 at 15:57
  • I'm getting `code Error 2 The as operator must be used with a reference type or nullable type ('BlockingUI.Form1.ConnectionProperties' is a non-nullable value type) ` – Krischu Mar 14 '14 at 16:08
  • @Krischu sorry, I forgot to update my cast, it should be `var connection = (ConnectionProperties)e.Argument;`, I updated my answer. – Sven Grosen Mar 14 '14 at 16:31
  • Thanks, I noticed already and I got my example working. Now one more question: If I have three such background tasks which I would like to process in parallel, would I have to create three workers or can I add the three tasks to one worker? – Krischu Mar 15 '14 at 10:02
1

Well, I had done this with my own thread. Normally, you run the thread with the long running task, and when needed, call Control.Invoke() with a delegate pointing to a function that will operate over the UI. It looks that you're changing the UI using the background worker thread, which is not allowed.

Also, in Winforms is necessary to call Control.Invalidate() to force a re-painting of the UI and show the new icons.

Oscar
  • 13,594
  • 8
  • 47
  • 75