为什么要加上tr.SetApartmentState(ApartmentState.STA);这句。否则会报下面那个错!
请详细回答!
using System;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(Form1_Load);
}
private Thread tr;
void Form1_Load(object sender, EventArgs e)
{
tr = new Thread(new ThreadStart(Do));
tr.SetApartmentState(ApartmentState.STA);
tr.IsBackground = true;
tr.Start();
}
private void Do()
{
System.Windows.Forms.SaveFileDialog s = new SaveFileDialog();
if (s.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
MessageBox.Show("123");
}
//在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。
//请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常。
}
}
}
private void Do()
{
this.Invoke(new MethodInvoker(delegate
{
System.Windows.Forms.SaveFileDialog s = new SaveFileDialog();
if (s.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
MessageBox.Show("123");
}
}));
}
你可以不要tr.SetApartmentState(ApartmentState.STA); 试试我这写的,主要是跨线程的原因吧。
多线程访问了控件就会出现这个问题,
请使用BeginInvoke或Invoke方法
你换成System.Windows.Forms.TextBox t = new TextBox();就不会啦。。根本性不是这个原因...
这是你所要访问的第三方组件就是“单线程单元模型”。所以你访问他你的线程也应该是单线程单元模式
能否说详细点
System.Windows.Forms.SaveFileDialog s = new SaveFileDialog();这个控件是系统自带的。不是第三方哦。。。
@KeVinDurant:
什么是OLE:http://zhidao.baidu.com/question/5347035.html
System.Windows.Forms.SaveFileDialog s = new SaveFileDialog();
System.Diagnostics.Debug.Write(s.ShowDialog().ToString());
我看了下问题是出在ShowDialog()上面,用reflector看内部代码是:
[SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected abstract bool RunDialog(IntPtr hwndOwner);
调用Win32内部方法,该方法肯定访问了现成的OLE组件,且这个组件是以SAT模型公开的...
调用SAT的COM(即OLE调用),只能用对应的单元线程模型调用,这也是处于安全性考虑。
详见:http://support.microsoft.com/kb/150777/zh-cn (中文可能翻译的不好,可以找原文看看)
COM 中的线程模型为使用不同线程结构的组件提供一起工作的机制。同时还为需要它们的组件提供同步服务。例如,某个特定对象可能被设计成只能由单线程调用, 并且可能无法同步来自客户的并行调用。如果这种对象被多个线程同时调用,则将导致系统崩溃或产生错误。COM 提供处理这种线程结构互操作性的机制。
选择线程模型
组件可以选择支持 STA 模型、MTA 模型或使用混合线程模型支持这两种模型的组合。例如,执行大量 I/O 的对象可以选择支持 MTA,这样便可通过允许在 I/O 等待时间内进行接口调用,为客户提供最大程度的响应。或者,与用户相互作用的对象几乎总是选择支持 STA,以便用其 GUI 操作同步传入的 COM 调用。由于 COM 提供同步,因此支持 STA 模型比较容易。由于对象必须实现同步,因此支持 MTA 模型比较困难,但因为同步只是用于小部分代码,而不是用于由 COM 所提供的整个接口调用,所以到客户的响应较好。
@滴答的雨: 用reflector看内部代码是??这句话什么意思?怎么操作。。??
@KeVinDurant: reflector.exe可以看到.NET封装的一些底层代码
@滴答的雨: 调用Win32内部方法,该方法肯定访问了现成的OLE组件,且这个组件是以SAT模型公开的...
这句怎么解释。。从哪里可以看出来?
@KeVinDurant: 推测。因为这种对话框本身就可能是之前留下来的组件啊,不会说每个组件都C#重新开发一次。喜欢研究的话把clr源码下下来研究下
@滴答的雨: 怎么看出来是不是用c#开发的...还是用的其它组件??怎么查看?
@KeVinDurant: reflector.exe能看到的就是C#开发的,当你发现这工具看不到的函数就是其他语言开发的
@滴答的雨:
[SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
public abstract void Reset();
[SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected abstract bool RunDialog(IntPtr hwndOwner);
public DialogResult ShowDialog()
{
return this.ShowDialog(null);
}
public DialogResult ShowDialog(IWin32Window owner)
{
System.Windows.Forms.IntSecurity.SafeSubWindows.Demand();
if (!SystemInformation.UserInteractive)
{
throw new InvalidOperationException(System.Windows.Forms.SR.GetString("CantShowModalOnNonInteractive"));
}
NativeWindow window = null;
IntPtr zero = IntPtr.Zero;
DialogResult cancel = DialogResult.Cancel;
try
{
if (owner != null)
{
zero = Control.GetSafeHandle(owner);
}
if (zero == IntPtr.Zero)
{
zero = System.Windows.Forms.UnsafeNativeMethods.GetActiveWindow();
}
if (zero == IntPtr.Zero)
{
window = new NativeWindow();
window.CreateHandle(new CreateParams());
zero = window.Handle;
}
if (helpMsg == 0)
{
helpMsg = System.Windows.Forms.SafeNativeMethods.RegisterWindowMessage("commdlg_help");
}
System.Windows.Forms.NativeMethods.WndProc d = new System.Windows.Forms.NativeMethods.WndProc(this.OwnerWndProc);
this.hookedWndProc = Marshal.GetFunctionPointerForDelegate(d);
IntPtr userCookie = IntPtr.Zero;
try
{
this.defOwnerWndProc = System.Windows.Forms.UnsafeNativeMethods.SetWindowLong(new HandleRef(this, zero), -4, d);
if (Application.UseVisualStyles)
{
userCookie = System.Windows.Forms.UnsafeNativeMethods.ThemingScope.Activate();
}
Application.BeginModalMessageLoop();
try
{
cancel = this.RunDialog(zero) ? DialogResult.OK : DialogResult.Cancel;
}
finally
{
Application.EndModalMessageLoop();
}
return cancel;
}
finally
{
IntPtr windowLong = System.Windows.Forms.UnsafeNativeMethods.GetWindowLong(new HandleRef(this, zero), -4);
if ((IntPtr.Zero != this.defOwnerWndProc) || (windowLong != this.hookedWndProc))
{
System.Windows.Forms.UnsafeNativeMethods.SetWindowLong(new HandleRef(this, zero), -4, new HandleRef(this, this.defOwnerWndProc));
}
System.Windows.Forms.UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);
this.defOwnerWndProc = IntPtr.Zero;
this.hookedWndProc = IntPtr.Zero;
GC.KeepAlive(d);
}
}
finally
{
if (window != null)
{
window.DestroyHandle();
}
}
return cancel;
}刚反编译了。从这段代码怎么看出调用了win32 API? OLE?
SAT模型公开?
@KeVinDurant:
[SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected abstract bool RunDialog(IntPtr hwndOwner);
下面可能是C++写的了。有没有调用Win32 API和SAT都是根据你的错误信息推测。想看真相,去找clr源码下载来看看
@滴答的雨: clr源码??
@滴答的雨: 在哪里能看出调用了c++??
@KeVinDurant: 也可能是delphi。只是说已经不是C#了
@滴答的雨: 怎么看出不是C#???
@KeVinDurant: reflector.exe能看到的就是C#开发的,当你发现这工具看不到的函数就是其他语言开发的
@滴答的雨: 但这样也不能说明SaveFileDialog不是用c#写的吧。。能举例其中哪个方法是调用了C++或者delphi的吗??
@KeVinDurant:
[SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected abstract bool RunDialog(IntPtr hwndOwner);
@滴答的雨: 看到了。调用了外部函数。。怎么跟调用win32 api不同的。。??没有看到引入命名空间什么之类的???