常规
我正在尝试在C#中编写一个非常简单的TCPIP客户端服务器以连接到具有端口号的IP地址,并询问非常简单的行命令,然后将回复放在网格箱,图形或其他显示选项中
我已经在网上找到了一个可以下载的实用程序,由Jayan Nair编写,这似乎正确地发送消息,并且收到回复确定。
当我尝试将回复数据加载到richtext或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),并且与服务器发送的消息一致 我也尝试使用文本框而不是richtext,结果相同
感兴趣的人
这是Microsoft链接
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
}
答案 0 :(得分:5)
您收到的InvalidOperationException
可能会有内部异常“调用线程无法访问此对象,因为其他线程拥有它”。
为了便于调试,您可以快速将CheckForIllegalCrossThreadCalls
设置为false。这将阻止运行时抛出异常,但仅仅因为你没有收到异常并不意味着它们没有发生,所以这对于分布式软件来说是一种不好的做法,但是非常方便调试。
为了解决您的问题,您需要调用与 UI Thread 上的RichTextBox
交互的代码,即创建控件的线程,以及允许的唯一线程运行与控件交互的代码。
要调用UI线程上的代码,您可以使用以下简洁的语句:
richTextRxMessage.BeginInvoke(new MethodInvoker(() => richTextRxMessage.Text = szData));
很简单,你在这里所做的就是通过委托传递你想在UI线程上调用的语句。
答案 1 :(得分: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);
编辑:正如其他人所说的那样,这个错误很可能是由于访问了一个不是创建它的线程的控件,这就是为什么需要调用。
答案 2 :(得分:0)
微软的答案并非完全不正确,但还有其他方法可以做到这一点,虽然他们看起来不那么简单。
这是我自己的代码中的一个snippit,可能会派上用场。
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语句,但是它的条件是'cost',它比直接调用属性慢。
当然,如果您在后台线程上开始,这只会产生影响。如果没有,那么您需要调查错误本身。也许返回文本有问题,将其写入文件并查看它,看看是否可以发现问题。尝试手动设置RTB的文本,看看它是否允许这种情况发生。等等等。
答案 3 :(得分:0)
问题可能是因为MS论坛上建议的多线程。当您以异步方式请求此操作时,它将创建另一个将处理输入的线程,以便您的主应用程序线程不会阻塞。
要解决此问题,您必须Invoke
您希望更改其属性的控件。你需要这个,因为你的主应用程序线程拥有UI,并且UI上的任何操作都不能从另一个线程完成。
调用并不复杂,可以在几行中完成。
更改以下行
richTextRxMessage.Text = szData;
到
richTextRxMessage.Invoke((MethodInvoker) delegate {this.Text = szData;});
此代码将要求主ui线程更新richTextRxMessage
的文本,而不是自行完成。