views:

1058

answers:

4

I have a server application that receives information over a network and processes it. The server is multi-threaded and handles multiple sockets at time, and threads are created without my control through BeginInvoke and EndInvoke style methods, which are chained by corresponding callback functions.

I'm trying to create a form, in addition to the main GUI, that displays a ListBox item populated by items describing the currently connected sockets. So, what I'm basically trying to do is add an item to the ListBox using its Add() function, from the thread the appropriate callback function is running on. I'm accessing my forms controls through the Controls property - I.E:

(ListBox)c.Controls["listBox1"].Items.Add();

Naturally I don't just call the function, I've tried several ways I've found here and on the web to communicate between threads, including MethodInvoker, using a delegate, in combination with Invoke(), BeginInvoke() etc. Nothing seems to work, I always get the same exception telling me my control was accessed from a thread other than the one it was created on.

Any thoughts?

+2  A: 

Using BeginInvoke or Invoke should work fine. Could you post a short but complete program which demonstrates the problem? You should be able to work one up which doesn't actually need any server-side stuff - just have a bunch of threads which "pretend" to receive incoming connections.

Jon Skeet
+5  A: 

I've always used something along these lines:

        c = <your control>
        if (c.InvokeRequired)
        {
            c.BeginInvoke((MethodInvoker)delegate
            {
                //do something with c
            });
        }
        else
        {
            //do something with c
        }

I also wrote a bunch of helper extension methods to... help.

using System;
using System.ComponentModel;
public static class CrossThreadHelper
{
    public static bool CrossThread<T,R>(this ISynchronizeInvoke value, Action<T, R> action, T sender, R e)
    {
        if (value.InvokeRequired)
        {
            value.BeginInvoke(action, new object[] { sender, e });
        }

        return value.InvokeRequired;
    }
}

used like this:

     private void OnServerMessageReceived(object sender, ClientStateArgs e)
    {
        if (this.CrossThread((se, ev) => OnServerMessageReceived(se, ev), sender, e)) return;
        this.statusTextBox.Text += string.Format("Message Received From: {0}\r\n", e.ClientState);
    }
Hath
Why not just one extension method that accepts an Action or MethodInvoker? Much cleaner, and the caller can always fill in the rest in the delegate
Marc Gravell
@Marc - good point i'll change it.
Hath
+2  A: 

You have to call Invoke (or BeginInvoke) on the ListBox control you are accessing in order for the delegate to be called on the thread that created that control.

ListBox listBox = c.Controls["listBox1"] as ListBox;
if(listBox != null)
{
   listBox.Invoke(...);
}
Manga Lee
A: 

Amazing, thank you all!