在Windows窗体项目中,我有一个按钮处理程序,可以在记事本中打开文件进行编辑。记事本关闭后,我调用函数RefreshTextBox()
来解析文本文件并根据值更新TextBox。以下是打开记事本并在关闭后调用refresh方法的方法:
private void button_Click(object sender, EventArgs e)
{
Process p = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
FileName = "NOTEPAD.EXE",
Arguments = _path,
WindowStyle = ProcessWindowStyle.Maximized,
CreateNoWindow = false
}
};
p.Exited += (a, b) =>
{
RefreshTextBox();
p.Dispose();
};
p.Start();
}
刷新文本框的代码:
private void RefreshTextBox()
{
using (StreamReader reader = File.OpenText(_appSettingsPath))
{
string text = reader.ReadToEnd();
// Code to parse text looking for value...
// InvalidOperationException thrown here:
textBox.Text = reader.Value.ToString();
}
}
这会抛出一个异常,尝试从创建它之外的线程更新Control。我很难理解为什么。我不会在新的任务或背景工作者或类似的事情中这样做。显然,记事本在另一个线程中运行,但刷新方法直到它的进程退出后才会被调用。
编辑:我应该补充一点,在Visual Studio(作为管理员)中进行调试时,此错误会抛出致命异常弹出窗口。它自己运行应用程序时不显示弹出窗口,要么是静默吞下,要么就不会出现异常。
答案 0 :(得分:6)
根据文档,如果未设置Process SynchronizingObject
,它将在系统线程池中执行exited事件以避免这种情况,并在UI线程中运行该事件处理程序,您需要将SynchronizingObject
设置为Form Instance
当SynchronizingObject为null时,将在系统线程池的线程上调用处理Exited事件的方法。有关系统线程池的更多信息,请参阅
ThreadPool
。
如果您设置
p.SynchronizingObject = WindowsFormName;
然后它将在同一个线程中运行,否则它将在系统线程池线程中执行,这将导致交叉线程异常。
答案 1 :(得分:0)
private void button_Click(object sender, EventArgs e)
{
Process p = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
FileName = "NOTEPAD.EXE",
Arguments = _path,
WindowStyle = ProcessWindowStyle.Maximized,
CreateNoWindow = false
}
};
//p.SynchronizingObject = this;
p.Exited += (a, b) =>
{
RefreshTextBox();
p.Dispose();
};
p.Start();
}
private void RefreshTextBox()
{
using (StreamReader reader = File.OpenText(_appSettingsPath))
{
string text = reader.ReadToEnd();
// Code to parse text looking for value...
//textBox.Text = text; // reader.Value.ToString();
threadSafeControlUpdate(textBox, text);
}
}
public delegate void updateUIfunc(Control c, object v);
public void threadSafeControlUpdate(Control c, object v)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new updateUIfunc(threadSafeControlUpdate), c, v);
return;
}
if (c is TextBox && v is string)
{
c.Text = (string)v;
}
}
答案 2 :(得分:0)
我建议捕获同步上下文并将RefreshTextBox调用发布到它上面。类似的东西:
private void button_Click(object sender, EventArgs e)
{
var _synchronizationContext = WindowsFormsSynchronizationContext.Current;
Process p = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
FileName = "NOTEPAD.EXE",
Arguments = _path,
WindowStyle = ProcessWindowStyle.Maximized,
CreateNoWindow = false
}
};
p.Exited += (a, b) =>
{
_synchronizationContext.Post(_=> RefreshTextBox(), null);
p.Dispose();
};
p.Start();
}