【问题标题】:c# System.InvalidOperationExceptionc# System.InvalidOperationException
【发布时间】:2013-03-02 21:16:18
【问题描述】:

常规

我正在尝试用 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)和回车(1​​3),与服务器发送的消息一致 我也尝试过使用文本框而不是富文本,结果相同

对于那些感兴趣的人

这是微软链接

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
                }
    
  • 【问题讨论】:

    • 我已经做出了回答,已经受到 Microsoft 回答的影响,但最大的问题仍然存在:这是从后台线程运行的。这是一个非常重要的问题,它将彻底改变我的答案。
    • 我认为您应该使用 MSDN reference to the StackOverflow answer seriously。 Invoke 是一个单行修复程序,而不是一个项目本身。您还应该能够判断您是否看到了跨线程问题。是否存在内部异常?

    标签: c# asynchronous tcp client


    【解决方案1】:

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

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

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

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

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

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

    【讨论】:

      【解决方案2】:

      这是我在一些东西中使用的函数;

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

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

      【讨论】:

        【解决方案3】:

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

        这是我自己的代码片段,可能对你有用。

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

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

        以这种方式更改您的调用的方式是:

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

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

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

        【讨论】:

        • 你可以做dirtyImage.Invoke((MethodInvoker)delegate {Visible = RigX.IsDirty; Refresh();});。此外,我个人会为超过 1 个命令的任何内容创建一个 ...Invoked 方法,因为您只需检查/更改 1 个代码和平。
        • 旧代码,我现在知道了……但从来没有权限重新进入并重构它。该死的业务规则和需要许可的生产环境:)
        【解决方案4】:

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

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

        调用并不复杂,几行就可以搞定。

        更改以下行

        richTextRxMessage.Text =  szData;
        

        到

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-06-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-10
          相关资源
          最近更新 更多