8

一般的

我正在尝试用 C# 编写一个非常简单的 TCPIP 客户端服务器来连接到一个 IP 地址,一个端口号,并询问非常简单的行命令,然后将回复放在 Gridbox、图形或其他显示选项中。

我在网上查看并找到了一个可下载的实用程序,它由 Jayan Nair 编写,它似乎可以正确发送消息,并且可以收到回复。

当我尝试将回复数据加载到富文本或 GridView 中时,问题就出现了。

我收到的错误消息是:- System.InvalidOperationException

我问过微软论坛,他们给了我一个非常复杂、模棱两可和过度参与的指示,告诉我应该做什么,这涉及到一个叫做 INVOKE 和 BeginInvoke 的东西,它们似乎是其中的一个项目;正确的。

我所追求的只是一个有效的例子,而不是太复杂。

这是代码:-

        try
        {
            SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
            int iRx = theSockId.thisSocket.EndReceive(asyn);
            char[] chars = new char[iRx + 1];
            System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
            int charLen = d.GetChars(theSockId.dataBuffer, 0, iRx, chars, 0);
            // 
            System.String szData = new System.String(chars);
            richTextRxMessage.Text =  szData;  // fails
            //textBox1.Text = szData;          // also fails
            WaitForData();
        }

这是错误消息:-

base {System.Windows.Forms.TextBoxBase} = {Text = '((System.Windows.Forms.RichTextBox)    (((System.Windows.Forms.RichTextBox)(richTextRxMessage)))).Text' threw an exception of type   'System.InvalidOperationException'}

附加信息是:- szData 包含大约 6300 个字符,包括制表符 (9) 和返回 (13),并且与从服务器发送的消息一致我也尝试过使用文本框而不是富文本,结果相同

对于那些有兴趣的人

这是微软的链接

http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/12a67fb0-847e-4e2b-baa8-ef6ceed60aa4/


  • 这只是我尝试过的两个代码修改,都在相同的错误条件下失败
  • 我认为我需要做的是从低得多的层次开始 C#,而不仅仅是跳入并希望最好


       public void OnDataReceived(IAsyncResult asyn)
        {
    
            int InputRx;
            int charLen;
            char[] Inputchars;
            System.Text.Decoder InputDecode;
            System.String szData;
            bool IfInvokeRequired;
    
            try
    
            {
    
                SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
                InputRx = theSockId.thisSocket.EndReceive(asyn);                    // get size of input array
                Inputchars = new char[InputRx + 1];                                   // put i char array
                InputDecode = System.Text.Encoding.UTF8.GetDecoder();
                charLen = InputDecode.GetChars(theSockId.dataBuffer, 0, InputRx, Inputchars, 0);
                szData = new System.String(Inputchars);
                IfInvokeRequired = richTextRxMessage.InvokeRequired;
                if (IfInvokeRequired == true)
                {
                    richTextRxMessage.Invoke((MethodInvoker)delegate { this.Text = szData; });// fails
                    richTextRxMessage.BeginInvoke(new MethodInvoker(() => richTextRxMessage.Text = szData));//fails as well
                }
    
  • 4

    4 回答 4

    5

    您收到的InvalidOperationException可能会有内部异常“调用线程无法访问此对象,因为不同的线程拥有它”。

    为了调试,您可以快速设置CheckForIllegalCrossThreadCalls为 false。这将阻止运行时向您抛出异常,但是仅仅因为您没有收到异常并不意味着它们没有发生,因此这对于分布式软件来说是一种不好的做法,但对于调试非常方便。

    为了解决您的问题,您需要调用与 UI 线程(即创建控件的线程)交互的代码,RichTextBox以及唯一允许运行与控件交互的代码的线程。

    要调用 UI 线程上的代码,您可以使用以下简洁的语句:

    richTextRxMessage.BeginInvoke(new MethodInvoker(() => richTextRxMessage.Text = szData));
    

    非常简单,您在这里所做的只是通过委托传递您希望在 UI 线程上调用的语句。

    于 2013-03-14T13:48:23.710 回答
    1

    这是我在一些东西中使用的功能;

    public static void InvokeEx<T>(this T @this, Action<T> action) where T : ISynchronizeInvoke
    {
        if (@this.InvokeRequired)
        {
            @this.Invoke(action, new object[] { @this });
        }
        else
        {
            action(@this);
        }
    }
    

    对于处理任何类型的调用,它几乎是一个不错的小方法,并且在您使用它时可以减少代码行数。要使用它,只需使用它;

    Control.InvokeEx(f => control.DoStuff());
    

    例如,您可以使用;

    richTextRxMessage.InvokeEx(f => richTextRxMessage.Text =  szData);
    

    编辑:正如其他人所说,这个错误很可能是由于访问线程上的控件而不是创建它的线程,这就是需要调用的原因。

    于 2013-03-14T13:49:48.367 回答
    0

    Microsoft 的答案并非完全不正确,但还有其他一些更简单的方法,尽管它们看起来并不正确。

    这是我自己的代码中的一个片段,可能对您有用。

    if (dirtyImage.InvokeRequired)
    {
        dirtyImage.Invoke(new MethodInvoker(delegate { Visible = RigX.IsDirty; }));
        dirtyImage.Invoke(new MethodInvoker(delegate { Refresh(); }));
    }
    else
    {
        dirtyImage.Visible = RigX.IsDirty;
        dirtyImage.Refresh();
    }
    

    注意如何使用InvokeRequireddirtyImage.Invoke(...)使用。通过创建委托和使用方法调用程序,我使它变得更简单,这样我就可以将所有调用都放在一个衬里。委托实质上创建了一个小方法,然后使用 MethodInvoker 调用该方法。因为更改是在 UI 线程上进行的,所以如果不使用,就不能从后台线程调用它们,这是.Invoke不允许的。

    可以通过这种方式更改您的呼叫的方式是:

    richTextRxMessage.Invoke(new MethodInvoker(delegate { Text =  szData; }));
    

    该调用也直接从 UI 线程本身工作,因此您实际上不需要 if 语句,但它是“成本”的术语,它比直接调用属性要慢。

    当然,这只有在你一开始就在后台线程上时才会真正产生影响。如果不是,那么您需要调查错误本身。也许返回的文本有问题,将其写入文件并查看它,看看是否能发现问题。尝试手动设置 RTB 的文本,看看它是否允许这种情况发生。等等等等等等。

    于 2013-03-14T13:41:56.360 回答
    0

    这个问题很可能是因为 MS 论坛上建议的多线程。当您以异步方式请求此操作时,它将创建另一个线程来处理输入,以便您的主应用程序线程不会阻塞。

    要克服这个问题,您必须Invoke控制要更改其属性的控件。你需要这个,因为你的主应用程序线程拥有 UI,并且 UI 上的任何操作都不能从另一个线程完成。

    调用并不复杂,只需几行即可完成。

    更改以下行

    richTextRxMessage.Text =  szData;
    

    richTextRxMessage.Invoke((MethodInvoker) delegate {this.Text = szData;});
    

    此代码将要求主 ui 线程更新文本,richTextRxMessage而不是自己更新。

    于 2013-03-14T13:48:34.437 回答