遇到的问题-----------c#在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式

来源:互联网 发布:it程序员面试自我介绍 编辑:程序博客网 时间:2024/06/10 04:28


今天在一个复杂的项目中添加一个弹出新窗口点击按钮后选择文件读取文件的功能。

代码如下:

在同一个命名空间Api中:

Txt.cs

 class Txt    {                       public void OpenReadFile()        {            Form1 newForm = new Form1();            newForm.ShowDialog();                                                                   }          }
在同一个命名空间中:

Form1


Form1.cs

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.IO;using System.Threading;namespace Api{       public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();                         }                public string str="无内容";        private void button1_Click(object sender, EventArgs e)        {                                     if (this.openFileDialog1.ShowDialog() == DialogResult.OK)               {                   str = File.ReadAllText(this.openFileDialog1.FileName);               }              this.label1.Text = str;                                                                        }                                  }   }


会报错:

在可以调用 ole 之前,必须将当前线程设置为单线程单元(sta)模式。请确保您的 main 函数带有 stathreadattribute 标记。 只有将调试器附加到该进程才会引发此异常。




这种情况下要把 openFileDialog放到新的SAT线程中运行就行(错误原因分析在文章末尾)

解决方法一:

[STAThread]

public static void Main(string[] args)


解决方法二:用SAT线程打开openfiledialog 因为我这里项目太大了 找不到进入的main函数是哪一个 所以 用的方法二

改为如下代码解决问题:

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.IO;using System.Threading;namespace Api{       public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();                         }                    public string str="无内容";        private void button1_Click(object sender, EventArgs e)        {            Thread InvokeThread = new Thread(new ThreadStart(InvokeMethod));            InvokeThread.SetApartmentState(ApartmentState.STA);            InvokeThread.Start();            InvokeThread.Join();               this.label1.Text = str;                                                                        }        private void InvokeMethod()        {            OpenFileDialog InvokeDialog = new OpenFileDialog();            if (InvokeDialog.ShowDialog() == DialogResult.OK)            {                str = File.ReadAllText(InvokeDialog.FileName);            }        }                               }    }







原因:

单元是进程内部具有相同线程访问要求的对象的逻辑容器。同一单元中的所有对象都可以接收从该单元中的任何线程发出的调用。.NET Framework 不使用单元,托管对象自己负责以线程安全的方式使用所有共享资源。由于 COM 类使用单元,因此公共语言运行库需要在 COM interop 的情况下调用 COM 对象时创建并初始化一个单元。托管线程可以创建并进入只允许有一个线程的单线程单元 (STA)或者包含一个或多个线程的多线程单元 (MTA)。通过将线程的 ApartmentState 属性设置为 ApartmentState 枚举值之一,可以控制所创建的单元的类型。由于给定线程只能初始化 COM 单元一次,因此在第一次调用非托管代码之后就不能更改单元类型。


[STAThread]
是一种线程模型,用在程序的入口方法上(在C#和VB.NET里是Main()方法),来指定当前线程的ApartmentState 是STA。用在其他方法上不产生影响。在aspx页面上可以使用AspCompat = "true" 来达到同样的效果。这个属性只在  Com  Interop  有用,如果全部是  managed  code  则无用。简单的说法:[STAThread]指示应用程序的默认线程模型是单线程单元 (STA)。启动线程模型可设置为单线程单元或多线程单元。如果未对其进行设置,则该线程不被初始化。也就是说如果你用的.NET Framework,并且没有使用COM Interop,一般不需要这个Attribute。其它的还有MTA(多线程套间)、Free  Thread(自由线程)。

[STAThread] attribute指示应用程序的 COM 线程模型是单线程单元。
而于此对应的多线程单元则是 [MTAThread] (多线程单元线程)

COM 线程模型只适用于使用 COM interop 的应用程序。如果将此属性应用到不使用 COM interop 的应用程序,将没有任何效果。

COM 线程模型可设置为单线程单元或多线程单元。如果应用程序线程实际调用了 COM 组件,则仅为 COM interop 初始化该线程。如果没有使用 COM interop,则不初始化该线程。


我又在网络上找了两篇文章或许更能说明这个问题。我没有翻译,文章的大意是,由于很多COM在.NET环境下如果使用多线程的话,会导致引用的COM不能正常运行,而如果不声明程序为STAThread的话,.NET就会自动使用多线程来提高效率,这样就会导致不可预知的后果。




参考资料:

http://www.111cn.net/wy/js-ajax/37321.htm

http://blog.csdn.net/kongwei521/article/details/6871360


原创粉丝点击