【问题标题】:Cross-thread operation not valid when using Invoke使用 Invoke 时跨线程操作无效
【发布时间】:2016-02-05 18:08:23
【问题描述】:

我做了一个聊天系统,我收到了这个错误:

跨线程操作无效:'MessageBox'等

我做了什么:我添加了一个调用。这是代码:

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

问题是,它基本上是从另一个用户那里发送一条空白的消息。这是因为我在本地连接到聊天。这是一张图片:

接收消息:

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);
    }
}

发送消息:

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;
}

【问题讨论】:

    标签: c# sockets invoke required


    【解决方案1】:

    当您尝试访问表单主线程以外的其他线程中的控件时,就会发生此异常。

    我使用的是bellow simple log机制,这个只适合简单的应用。

    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);
        }
    }
    

    正如我所说,代码可以在任何地方使用并且很简单。但是对于更复杂的日志记录机制和更好的性能,请使用 Log4Net 或 NLog。

    【讨论】:

    • 感谢您的评论。它很高兴工作!唯一的事情是......它在每条消息后留下一个空格:/它只是删除了另一个评论。有什么建议么?再次感谢! - 理查德
    • 帮助别人是我的荣幸。我使用此代码登录 TextBoxes 而不是 ListBoxes,并且运行良好。拜托,你能写下代码,让我们看看你是如何实现的吗?
    • 非常感谢您的耐心等待。
    • 我检查了代码,什么也没找到。但是你能做一件事吗?如果你放一个断点并进入调试器模式并查看 string receivedMessage = aEncoding.GetString(receivedData); receivedMessage 中有什么数据?
    【解决方案2】:

    如果 receivedMessage 变量的值在调用的 Action 执行之前发生变化,则将使用该变量的新值 - 如果它的运行方式与您的 messageTb.Text 示例相同,则该值会立即被清除,并且调用的动作只会看到清除后的空白值。尝试将 usersName.Text + ": " + receivedMessage 字符串复制到一次性变量中并在 Invoke 中引用它,例如

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

    【讨论】:

    • 感谢您的回复,但很遗憾没有成功。非常感谢您的解释。我再看看。 - 理查德
    猜你喜欢
    • 2012-09-12
    • 2011-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多