Richard Jacobs
Richard Jacobs

Reputation: 47

Cross-thread operation not valid when using Invoke

I've made a chat system and I got this error:

Cross-thread operation not valid: 'MessageBox' etc.

What I have done: I've added an invoke. Here is the code:

Invoke(new Action(() => messageBox.Items.Add(usersName.Text + ": " + receivedMessage)));

The problem is, it basically sends a message from the other user which is blank. This is because I'm connected to the chat locally. Here is a picture:

Chat Application

Receiving messages:

private void MessageCallBack(IAsyncResult aResult)
{
    try
    {
        byte[] receivedData = new byte[1500];
        receivedData = (byte[])aResult.AsyncState;
        ASCIIEncoding aEncoding = new ASCIIEncoding();
        string receivedMessage = aEncoding.GetString(receivedData);
        Invoke(new Action(() => messageBox.Items.Add(usersName.Text + ": " + receivedMessage)));
        buffer = new byte[1500];
        socket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref theirIp, new AsyncCallback(MessageCallBack), buffer);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Sending messages:

private void sendBtn_Click(object sender, EventArgs e)
{
    try
    {
        if (messageTb.Text == "")
        {
            return;
        }
        else
        {
            ASCIIEncoding eEncoding = new ASCIIEncoding();
            byte[] msg = new byte[1500];
            msg = eEncoding.GetBytes(messageTb.Text);
            socket.Send(msg);
            messageBox.Items.Add(yourName.Text + ": " + messageTb.Text);
            messageTb.Clear();
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    this.ActiveControl = messageTb;
}

Upvotes: 2

Views: 583

Answers (2)

Bewar Salah
Bewar Salah

Reputation: 567

This exception occurs whenever you are trying to access a control in a different thread other than the form's main thread.

I use the bellow simple log mechanism, this is only suitable for a simple application.

private delegate void LogDelegate(ListBox messageBox, string data);

private LogDelegate _logDelegate;
private void Log(ListBox messageBox, string data)
{
    if (messageBox.InvokeRequired)
    {
        LogDelegate logDelegate = Log;
        Invoke(logDelegate, messageBox, data);
    }
    else
    {
        messageBox.Items.Add(data);
    }
}

public Form1()
{
    InitializeComponent();
    //don't forget, initialize the delegate
    _logDelegate = Log;
}

private void btn_Start_Click(object sender, EventArgs e)
{
    Log("Anything to log");
}
private void MessageCallBack(IAsyncResult aResult)
{
    try
    {
        //Instead of Invoke use 
        //Invoke(new Action(() => messageBox.Items.Add(usersName.Text + ": " + receivedMessage)));
        Log("Anything to log");

    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

As I said, the code can be used anywhere and is simple. But for more complex logging mechanism and better performing use Log4Net or NLog.

Upvotes: 2

FacticiusVir
FacticiusVir

Reputation: 2077

If the value of the receivedMessage variable changes before the invoked Action executes, the new value of that variable will be used - if it's functioning in the same way as your messageTb.Text example, the value is cleared immediately, and the invoked Action will only see the post-Clear blank value. Try copying the usersName.Text + ": " + receivedMessage string into a single-use variable and referencing that in your Invoke, e.g.

string newMessage = usersName.Text + ": " + receivedMessage;
Invoke(new Action(() => messageBox.Items.Add(newMessage)));

Upvotes: 0

Related Questions