C# SaveFileDialog:InvalidOperationException由于“无效”;“业主”;多线程应用程序中的参数
很抱歉发了这么长的帖子,但我尽量详细地解释了这个问题,以免引起混淆。最后一句包含实际问题 我正在用C#/.NET编写一个多线程应用程序 该应用程序由一个主窗口组成,该窗口显示来自压力传感器的数据。传感器数据在自己的线程中获取 数据还记录在类C# SaveFileDialog:InvalidOperationException由于“无效”;“业主”;多线程应用程序中的参数,c#,.net,winforms,multithreading,savefiledialog,C#,.net,Winforms,Multithreading,Savefiledialog,很抱歉发了这么长的帖子,但我尽量详细地解释了这个问题,以免引起混淆。最后一句包含实际问题 我正在用C#/.NET编写一个多线程应用程序 该应用程序由一个主窗口组成,该窗口显示来自压力传感器的数据。传感器数据在自己的线程中获取 数据还记录在类列表视图的实例中: 可以通过“保存”按钮将记录的数据保存到磁盘上的文件中(应打开.NET类的实例SaveFileDialog) 这个SaveFileDialog也在自己的线程中运行。 现在调用方法SaveFileDialog.ShowDialog()时出现问
列表视图的实例中:
可以通过“保存”按钮将记录的数据保存到磁盘上的文件中(应打开.NET类的实例SaveFileDialog
)
这个SaveFileDialog
也在自己的线程中运行。
现在调用方法SaveFileDialog.ShowDialog()
时出现问题:
System.InvalidOperationException未处理
Message=“跨线程操作无效:从创建控件的线程以外的线程访问控件“tlpMain”
Source=“System.Windows.Forms”
出现问题的原因是SaveFileDialog的所有者(主窗口)正在另一个线程中运行
下面是为SaveFileDialog()创建线程的代码:
OpenSaveFileDialog()方法的代码:
只有在使用Visual Studio的调试器运行应用程序时才会引发/显示InvalidOperationException。到目前为止,在“正常”运行应用程序时没有问题
但我想避免这个问题
我尝试构建包装器方法(SaveFileDialog):
包装器方法:
private void SaveFileDialog(SaveFileDialog saveFileDialog, Control owner)
{
if (owner.InvokeRequired)
BeginInvoke(new dSaveFileDialog(SaveFileDialog), new object[] { saveFileDialog, owner });
else
{
DialogResult pressedButton = saveFileDialog.ShowDialog(owner);
...
这会导致出现TargetInvocationException
,尽管Main()
方法标记为[stathreadtribute]
:
InnerException:System.Threading.ThreadStateException
Message=“在进行OLE调用之前,必须将当前线程设置为单线程单元(STA)模式。请确保主函数上标记了STAThreadAttribute。只有在将调试器附加到进程时才会引发此异常。”
Source=“System.Windows.Forms”
有没有人知道如何打开SaveFileDialog
,这样主窗口就会被阻塞(“不可访问”),而不会出现(线程)问题
谢谢。调试期间出现的跨线程异常是一个错误。它们通常不在调试器之外处于活动状态。这解释了为什么在VisualStudio之外运行应用程序时看不到
看起来您自己已经发现,除了主UI线程之外,您根本无法从其他线程对UI元素执行任何操作。您可以使用ISynchronizeInvoke
方法,即Invoke
或BeginInvoke
,将操作的执行封送到UI线程上,以便可以安全地访问UI元素
但是我仍然发现你的代码有问题。在工作线程上运行的OpenSaveFileDialog
方法中,您正在调用SaveFileDiaglog
的构造函数,当然,它是一个UI元素。你不能这么做。值得重复。您不能从工作线程对窗体或控件执行任何操作。这包括调用构造函数。很抱歉回复太晚
首先,感谢您快速而有益的回复
提示:这是不可能的
对来自的窗体或控件执行任何操作
工作线程
帮了我很多忙
我通常不为微软的Windows做GUI编程,所以我对它不太熟悉
所以我重新考虑了之前的源代码,因为我想解决实际问题
(不从工作线程执行GUI操作),并且希望有一个干净的逻辑代码结构
因此,我阅读了Window的组件对象模型(COM)和使用的线程模型的主题:
- 什么是COM:
- 了解和使用COM线程模型:
- 了解COM单线程单元第1部分:
现在代码如下所示:
主窗口(“UI线程”)在ApartmentState STA
...
ThreadStart threadStart = delegate { RunMainWindow(mainWindow); };
Thread mainWindowThread = new Thread(threadStart);
mainWindowThread.SetApartmentState(ApartmentState.STA);
mainWindowThread.Start();
...
“保存”按钮事件处理程序(主窗口):
方法“OpenSaveFileDialog”(主窗口):
仍然有优化的空间(当然),但我对这个初步结果感到满意
非常感谢您的帮助。请关注以下microsoft博客:
只要两种方法,你就完成了 为什么要尝试使用多线程UI?单线程UI无法解决的问题是什么?应用程序可以处理多个主窗口,因此通常由三个线程组成:一个线程用于“应用程序管理器”,一个线程用于主窗口,另一个线程用于获取数据(从传感器)。现在应该有另一个可以打开“SaveFileDialog”的对话框了。用户一次不能做3件事,允许他们一次做1件事。他们会有更好的体验。@Benedikt为什么文件对话框需要一个单独的线程。这些都是从UI线程运行的。可能重复的
private void OpenSaveFileDialog()
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
...
SaveFileDialog(saveFileDialog, this.Parent);
}
private void SaveFileDialog(SaveFileDialog saveFileDialog, Control owner)
{
if (owner.InvokeRequired)
BeginInvoke(new dSaveFileDialog(SaveFileDialog), new object[] { saveFileDialog, owner });
else
{
DialogResult pressedButton = saveFileDialog.ShowDialog(owner);
...
...
ThreadStart threadStart = delegate { RunMainWindow(mainWindow); };
Thread mainWindowThread = new Thread(threadStart);
mainWindowThread.SetApartmentState(ApartmentState.STA);
mainWindowThread.Start();
...
private void bSave_Click(object sender, EventArgs e)
{
OpenSaveFileDialog();
}
private void OpenSaveFileDialog()
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
...
DialogResult pressedButton = saveFileDialog.ShowDialog();
...
}