C# SaveFileDialog:InvalidOperationException由于“无效”;“业主”;多线程应用程序中的参数

C# SaveFileDialog:InvalidOperationException由于“无效”;“业主”;多线程应用程序中的参数,c#,.net,winforms,multithreading,savefiledialog,C#,.net,Winforms,Multithreading,Savefiledialog,很抱歉发了这么长的帖子,但我尽量详细地解释了这个问题,以免引起混淆。最后一句包含实际问题 我正在用C#/.NET编写一个多线程应用程序 该应用程序由一个主窗口组成,该窗口显示来自压力传感器的数据。传感器数据在自己的线程中获取 数据还记录在类列表视图的实例中: 可以通过“保存”按钮将记录的数据保存到磁盘上的文件中(应打开.NET类的实例SaveFileDialog) 这个SaveFileDialog也在自己的线程中运行。 现在调用方法SaveFileDialog.ShowDialog()时出现问

很抱歉发了这么长的帖子,但我尽量详细地解释了这个问题,以免引起混淆。最后一句包含实际问题

我正在用C#/.NET编写一个多线程应用程序

该应用程序由一个主窗口组成,该窗口显示来自压力传感器的数据。传感器数据在自己的线程中获取

数据还记录在类
列表视图的实例中:

可以通过“保存”按钮将记录的数据保存到磁盘上的文件中(应打开.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();
            ...
}