使用 FolderBrowserDialog 时出现异常

Posted

技术标签:

【中文标题】使用 FolderBrowserDialog 时出现异常【英文标题】:Exception when using FolderBrowserDialog 【发布时间】:2011-10-15 03:59:48 【问题描述】:

尝试使用 FolderBrowserDialog 时出现以下异常: System.Threading.ThreadStateException: Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.

我已经广泛搜索了这个问题,每个人建议的解决方案似乎是将[STAThreadAttribute] 放在 Main 方法之上,从 Debug 文件夹中删除所有 dll,或者使用 Invoke 方法。我已经尝试了所有这些,但仍然遇到同样的异常。

代码如下:

public partial class Form1 : Form

    public event EventHandler ChooseLocationHandler = null;

    public string DestFolder
    
        set  textBox1.Text = value; 
        get  return textBox1.Text; 
    

    public Form1()
    
        InitializeComponent();
    

    private void ChooseLocationButton_Click(object sender, EventArgs e)
    
        if (ChooseLocationHandler != null)
            ChooseLocationHandler(this, e);
    

在我的演示者中是以下内容:

public partial class Presenter

    Form1 myForm;
    public Presenter()
    
        myForm = new Form1();
        myForm.ChooseLocationHandler += ChooseLocationHandler;
        myForm.Show();
    

    public void ChooseLocationHandler(object obj, EventArgs e)
    
        Form1 sender = (Form1)obj;

        FolderBrowserDialog fbd = new FolderBrowserDialog();
        fbd.RootFolder = System.Environment.SpecialFolder.MyComputer;
        fbd.ShowNewFolderButton = true;
        if (fbd.ShowDialog() == DialogResult.Cancel)
            return;

        sender.DestFolder = fbd.SelectedPath;
    

我在 fbd.ShowDialog() 上遇到异常。

【问题讨论】:

【参考方案1】:

线程要么是 STA 要么是 MTA,它不能只为一种方法指定,因此该属性必须存在于入口点上。

来自STAThreadAttribute in MSDN:

将此属性应用到入口点方法(中的 Main() 方法) C# 和 Visual Basic)。它对其他方法没有影响。

如果从辅助线程调用此代码,您有 3 个选择:

重要提示:在 MTA 线程中运行(如您所愿)System.Windows.Forms 代码是不明智的,文件打开对话框(不仅是文件夹)等某些功能需要 MTA 线程才能工作。强>

更改辅助线程单元

如果您自己创建线程(并且不使用 MTA 的特殊性),您可以在开始之前更改它的单元:

var t = new Thread(...);
t.SetApartmentState(ApartmentState.STA);

 

为它创建一个线程

如果您不控制线程创建,您可以在临时线程中进行:

string selectedPath;
var t = new Thread((ThreadStart)(() => 
    FolderBrowserDialog fbd = new FolderBrowserDialog();
    fbd.RootFolder = System.Environment.SpecialFolder.MyComputer;
    fbd.ShowNewFolderButton = true;
    if (fbd.ShowDialog() == DialogResult.Cancel)
        return;

    selectedPath = fbd.SelectedPath;
));

t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
Console.WriteLine(selectedPath);

 

在另一个(STA)线程中调用

如果您的主线程还包含 System.Windows.Forms 代码,您可以在其消息循环中调用以执行您的代码:

string selectedPath = null;
Form f = // Some other form created on an STA thread;
f.Invoke(((Action)(() => 
    FolderBrowserDialog fbd = new FolderBrowserDialog();
    fbd.RootFolder = System.Environment.SpecialFolder.MyComputer;
    fbd.ShowNewFolderButton = true;
    if (fbd.ShowDialog() == DialogResult.Cancel)
        return;

    selectedPath = fbd.SelectedPath;
)), null);
Console.WriteLine(selectedPath);

【讨论】:

您的应用程序是如何启动的?只是双击它?如果是的话,这段代码是从哪里调用的?后台工作人员,辅助线程或类似的东西? (如果您不知道在 Visual Studio 中中断调用对话框的行并在此处粘贴整个调用堆栈) 调用堆栈太大,无法放入此文本框。但它是从与主线程不同的线程调用的。 所以这就是问题所在,公寓是线程状态,我会在我的答案中添加 2 或 3 个可能的解决方案。 完成,但正如我在回答中指出的那样,在大多数情况下,创建表单的线程应该是 STA。 临时线程成功了!非常感谢。如果可以的话,我会投票 100 次。【参考方案2】:

这解决了我的问题。 [STA线程] 静态无效 Main()

只是一个额外的问题:为什么微软不能让事情变得简单? 他们是不是为了让人们反感来做一些编码?

【讨论】:

这并不能真正回答他的具体问题......至少它不会增加接受的答案。并且抱怨或询问“为什么不能 XXX 到 XXXX”不适合该网站。 这个不回答问题,请限制只回答问题。 抱歉,这确实解决了我的问题。我使用的是控制台应用程序,需要访问剪贴板。添加 [STAThread] 对我来说非常棒。【参考方案3】:

如下所示:

using System.Windows.Forms;
namespace fileConverterBaset64

    class Program
    
        [STAThread]
        static void Main(string[] args)

在您的主要方法之前添加命令[STAThread]。就是这样,它会工作。

【讨论】:

能否请您重新编写代码标记并为您的答案添加一个简单的解释? 这正是 7 年前@Eric answered 所做的。【参考方案4】:

我在 ASP.NET MVC 项目中遇到了同样的问题。当我将水晶报告导出为某种格式时,它会显示错误。我所做的是替换

这个:

            SaveFileDialog browser = new SaveFileDialog();
            string fileName = "";

            browser.Filter = "Pdf|*.pdf|Txt|.txt";

            if (browser.ShowDialog() == DialogResult.OK)
            
                ExportFormatType formatType = ExportFormatType.NoFormat;
                switch (browser.FilterIndex)
                
                    case 2:
                        formatType = ExportFormatType.WordForWindows;
                        break;
                    case 1:
                        formatType = ExportFormatType.PortableDocFormat;
                        break;
                

                fileName = browser.FileName;
                crReportDocument.ExportToDisk(formatType, fileName);

进入:

Thread thread = new Thread((ThreadStart)(() =>
            
                SaveFileDialog browser = new SaveFileDialog();
                string fileName = "";

                browser.Filter = "Pdf|*.pdf|Txt|.txt";

                if (browser.ShowDialog() == DialogResult.OK)
                
                    ExportFormatType formatType = ExportFormatType.NoFormat;
                    switch (browser.FilterIndex)
                    
                        case 2:
                            formatType = ExportFormatType.WordForWindows;
                            break;
                        case 1:
                            formatType = ExportFormatType.PortableDocFormat;
                            break;
                    

                    fileName = browser.FileName;
                    crReportDocument.ExportToDisk(formatType, fileName);
                
            ));

            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();

【讨论】:

【参考方案5】:

据我所知,STAThread 属性必须在 main 前面。

【讨论】:

【参考方案6】:

我遇到了同样的问题,我删除了 3 个未使用的 Dll,它已修复...非常感谢!

【讨论】:

【参考方案7】:

现在,检查参考中的所有 dll 并删除不使用的 dll。

这太不可思议了。我无法想象那些 dll 会导致这个问题。

【讨论】:

以上是关于使用 FolderBrowserDialog 时出现异常的主要内容,如果未能解决你的问题,请参考以下文章

使用 FolderBrowserDialog 限制对某些文件夹的访问

公司网络上的 FolderBrowserDialog 以选择子文件夹

如何在 FolderBrowserDialog 中设置只读属性

是否可以在 WPF Net 6 应用程序中使用 FolderBrowserDialog?

C# 对话框之FolderBrowserDialog

folderbrowserdialog1 在 Visual Studio 2015 Enterprise Windows 10 中不起作用