您的位置:首页 > 其它

子线程使用FolderBrowserDialog的问题延伸

2007-11-29 15:27 615 查看
Q:子线程如何使用FolderBrowserDialog



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的线程模型的话,应该已经清楚上面的问题根本了。

我们先看一下 FolderBrowserDialog的实现方法,这个控件实现是通过ole的.可以用Reflector.exe看一下他的代码,调用了几个windows shell32的api。



[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模式。而且线程模型一旦确定将不可以更改,所以你无法在其他地方用代码来设置主线程的线程模型。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: