本文转自:http://www.diybl.com/course/4_webprogram/asp.net/asp_netxl/2007125/90477.html
A:
private void button1_Click(object sender, EventArgs e)
{
System.Threading.Thread s = new System.Threading.Thread(new System.Threading.ThreadStart(test));
s.ApartmentState = System.Threading.ApartmentState.STA;
s.Start();
}
public void test()
{
System.Windows.Forms.FolderBrowserDialog dlg = new FolderBrowserDialog();
dlg.ShowDialog();
}
以上代码简单的演示了FolderBrowserDialog在子线程中的使用,其中设置线程的ApartmentState为System.Threading.ApartmentState.STA是关键的语句。在.net2.0中应该使用
s.SetApartmentState(System.Threading.ApartmentState.STA);
如果没有上述设置会报如下错误
在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。
如果你了解com的线程模型的话,应该已经清楚上面的问题根本了。
[SuppressUnmanagedCodeSecurity]
internal class Shell32
{
// Methods
public Shell32();
[DllImport("shell32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SHBrowseForFolder([In] UnsafeNativeMethods.BROWSEINFO lpbi);
[DllImport("shell32.dll")]
public static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out FileDialogNative.IShellItem ppsi);
public static int SHGetFolderPathEx(ref Guid rfid, uint dwFlags, IntPtr hToken, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath, uint cchPath);
[DllImport("shell32.dll", EntryPoint="SHGetFolderPathEx")]
private static extern int SHGetFolderPathExPrivate(ref Guid rfid, uint dwFlags, IntPtr hToken, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath, uint cchPath);
[DllImport("shell32.dll")]
public static extern int SHGetMalloc([Out, MarshalAs(UnmanagedType.LPArray)] UnsafeNativeMethods.IMalloc[] ppMalloc);
[DllImport("shell32.dll", CharSet=CharSet.Auto)]
public static extern bool SHGetPathFromIDList(IntPtr pidl, IntPtr pszPath);
[DllImport("shell32.dll")]
public static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, ref IntPtr ppidl);
[DllImport("shell32.dll")]
public static extern int SHILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] string pszPath, out IntPtr ppIdl, ref uint rgflnOut);
}
COM提供的线程模型共有三种:Single-Threaded Apartment(STA 单线程套间)、Multithreaded Apartment(MTA 多线程套间)和Neutral Apartment/Thread Neutral Apartment/Neutral Threaded Apartment(NA/TNA/NTA 中立线程套间,由COM+提供)。
STA 一个对象只能由一个线程访问,相当于windows的消息循环,实现方式也是通过消息循环的,ActiveX控件、OLE文档服务器等有界面的,都使用STA的套间。MTA 一个对象可以被多个线程访问,即这个对象的代码在自己的方法中实现了线程保护,保证可以正确改变自己的状态。
所以创建和访问一个activex或者ole对象时,必须设置线程模式为sta。
稍微有些多线程使用经验的人会发现用Control.Invoke方法也可以成功调用ole对象,比如上面的例子改为
private void Form1_Load(object sender, EventArgs e)
{
System.Threading.Thread s = new System.Threading.Thread(new System.Threading.ThreadStart(test));
//s.SetApartmentState(System.Threading.ApartmentState.STA);
s.Start();
}
public delegate void dtest();
public void test()
{
this.Invoke(new dtest(invokeTest));
}
public void invokeTest()
{
System.Windows.Forms.FolderBrowserDialog dlg = new FolderBrowserDialog();
dlg.ShowDialog();
}
其实使得这个调用成功的原因不是在于Invoke,还是线程模式。如果把main函数上边的[STAThread] 去掉的话,文章开始处的错误仍然会发生。Invoke只是让主线程来执行子线程的调用函数。[STAThread]在程序入口处即将主线程置为sta模式,如果没有这句话将置为mta模式。而且线程模型一旦确定将不可以更改,所以你无法在其他地方用代码来设置主线程的线程模型。