在线程中调用SaveFileDialog
在线程中调用SaveFileDialog
在多线程编程中,有时候可能需要在单独线程中执行某些操作。例如,调用SaveFileDialog类保存文件。首先,我们在Main方法中创建了一个新线程,并将其指向要执行的委托SaveFileAsyn。在SaveFileAsyn方法中,我们像平时做的一样,声明一个SaveFileDialog的新实例,并调用ShowDialog方法显示文件保存对话框。
<span style="color:#4b4b4b"> <span style="color:blue">class </span><span style="color:#2b91af">Program</span><span style="color:black">{</span><span style="color:blue">static void </span><span style="color:black">Main(</span><span style="color:blue">string</span><span style="color:black">[] args){</span><span style="color:#2b91af">Thread </span><span style="color:black">t = </span><span style="color:blue">new </span><span style="color:#2b91af">Thread</span><span style="color:black">(SaveFileAsyn); t.Start();}</span><span style="color:blue">static void </span><span style="color:black">SaveFileAsyn(){</span><span style="color:blue">var </span><span style="color:black">dialog = </span><span style="color:blue">new </span><span style="color:#2b91af">SaveFileDialog</span><span style="color:black">();dialog.ShowDialog();}} </span></span>
<span style="color:#4b4b4b"><span style="color:black">直接看上述代码,我们不会觉得有什么问题。</span><span style="color:black">但是,在运行程序时,你会遇到“未处理ThreadStateException”的异常。噢,shit~。</span></span>
<span style="color:#4b4b4b"><a data-cke-saved-href="https://images0.cnblogs.com/blog/4074/201402/081622028455689.png" href="https://images0.cnblogs.com/blog/4074/201402/081622028455689.png"><img alt="image" data-cke-saved-src="https://images0.cnblogs.com/blog/4074/201402/081622035944060.png" src="https://images0.cnblogs.com/blog/4074/201402/081622035944060.png" width="444" height="356" /></a></span>
<span style="color:#4b4b4b"><span style="color:black">不过,仔细检查,你会发现在异常消息中有“请确保您的 Main 函数带有 STAThreadAttribute 标记”的提示,你会感觉找到一丝希望,你迫不及待的按照提示去做,就像这样。</span></span>
<span style="color:#4b4b4b"> <span style="color:blue">class </span><span style="color:#2b91af">Program</span><span style="color:black">{[</span><span style="color:#2b91af">STAThread</span><span style="color:black">]</span><span style="color:blue">static void </span><span style="color:black">Main(</span><span style="color:blue">string</span><span style="color:black">[] args){</span><span style="color:#2b91af">Thread </span><span style="color:black">t = </span><span style="color:blue">new </span><span style="color:#2b91af">Thread</span><span style="color:black">(SaveFileAsyn);t.Start();}</span><span style="color:blue">static void </span><span style="color:black">SaveFileAsyn(){</span><span style="color:blue">var </span><span style="color:black">dialog = </span><span style="color:blue">new </span><span style="color:#2b91af">SaveFileDialog</span><span style="color:black">();dialog.ShowDialog();}}</span></span>
然后,重新运行程序,期望讨厌的异常走开。但是,很不幸的是,你依然会遇到同样的“未处理ThreadStateException”的异常。你会很奇怪,觉得微软欺骗了你。你遇到了异常,你按照提示的那样修改代码,但是程序却没有像你想象的那样运行,你一定很恼火。是的,我可以确定,我当时非常非常的恼火!
但是,这里的问题是,“请确保您的 Main 函数带有 STAThreadAttribute 标记”是一个过于直接的,以致于令人感觉带有欺骗性的提示。
<span style="color:#4b4b4b"> <span style="color:blue">static void </span><span style="color:black">Main(</span><span style="color:blue">string</span><span style="color:black">[] args){</span><span style="color:blue">var </span><span style="color:black">dialog = </span><span style="color:blue">new </span><span style="color:#2b91af">SaveFileDialog</span><span style="color:black">();dialog.ShowDialog();}
</span></span>
如果我们的代码像上面一样,直接在Main方法中SaveFileDialog类的ShowDialog方法,那么当我们添加STAThread标记后,确实是可以解决问题的(感兴趣的朋友可以自己试一下:)。而我们之前的代码,是在一个新建的Thread线程中执行SaveFileDialog类的ShowDialog方法。
添加STAThread标记解决的,是在主线程调用SaveFileDialog类的ShowDialog方法的问题,而不能直接解决在新建Thread线程中调用ShowDialog方法的问题。但是,它提供了一些有益的线索。
其实,在上面的异常消息中,最重要的是“必须将当前线程设置为单线程单元(STA)模式”这句话。在Main方法上添加STAThread标记是解决方法之一。它解决的是将主线程设置为单线程单元的问题,而不是解决将新建线程设置为单线程单元的问题。这就是为什么我们直接在Main方法上添加STAThread标记后,仍然提示同样错误的原因。所以,要解决我们最初的问题,就必须将新建线程设置为单线程单元。我们可以使用Thread类的SetApartmentState方法将线程设置为STA单线程单元状态。
<span style="color:#4b4b4b"> <span style="color:blue">class </span><span style="color:#2b91af">Program</span><span style="color:black">{</span><span style="color:blue">static void </span><span style="color:black">Main(</span><span style="color:blue">string</span><span style="color:black">[] args){</span><span style="color:#2b91af">Thread </span><span style="color:black">t = </span><span style="color:blue">new </span><span style="color:#2b91af">Thread</span><span style="color:black">(SaveFileAsyn);t.SetApartmentState(</span><span style="color:#2b91af">ApartmentState</span><span style="color:black">.STA); </span><span style="color:green">// 设置为单线程单元(STA)状态</span><span style="color:black">t.Start();}</span><span style="color:blue">static void </span><span style="color:black">SaveFileAsyn(){</span><span style="color:blue">var </span><span style="color:black">dialog = </span><span style="color:blue">new </span><span style="color:#2b91af">SaveFileDialog</span><span style="color:black">();dialog.ShowDialog();}} </span></span>
将新建线程的单元状态设置为STA单线程单元后,就可以在新建线程中运行SaveFileDialog类的ShowDialog方法了。
单元状态
那么,什么是单元状态呢?单元状态是COM组件用于同步资源访问的一种机制。.NET Framework本身不需要单元状态。但是,当与COM对象进行互操作时,则必须创建并初始化Thread线程的单元状态。STA单线程单元通常在UI组件中使用,Windows窗口消息即是其中之一。
下面是.NET类库中ApartmentState枚举的定义。ApartmentState枚举包含STA、MTA和Unknown3个成员。Thread线程初始化后,其ApartmentState单元状态默认是MTA,即多线程单元。
<span style="color:#4b4b4b"><span style="color:black"> [</span><span style="color:#2b91af">Serializable</span><span style="color:black">][</span><span style="color:#2b91af">ComVisible</span><span style="color:black">(</span><span style="color:blue">true</span><span style="color:black">)]</span><span style="color:blue">public enum </span><span style="color:#2b91af">ApartmentState</span><span style="color:black">{</span><span style="color:green">// System.Threading.Thread 将创建并进入一个单线程单元。</span><span style="color:black">STA = 0,</span><span style="color:green">// System.Threading.Thread 将创建并进入一个多线程单元。</span><span style="color:black">MTA = 1,</span><span style="color:green">// 尚未设置 System.Threading.Thread.ApartmentState 属性。</span><span style="color:black">Unknown = 2,}
</span></span>
SaveFileDialog作为.NET Framework默认的几种对话框之一,目的是为开发人员提供与Windows操作系统一致的用户体验的组件,其底层调用的是Windows操作系统的COM组件。也就是说,SaveFileDialog是对底层COM组件的封装,与SaveFileDialog交互,就是与底层COM组件交互。因此,调用SaveFileDialog类的Thread线程的单元状态必须是STA单线程单元,需要使用SetApartmentState方法将其单元状态设置为ApartmentState.STA。
结论
通过上面的介绍,我们对线程的单元状态有了初步了解,大致可以归纳为以下几点:
- .NET Framework本身不需要单元状态。
- Thread线程的单元状态默认为MTA多线程单元。
- 可以通过Thread线程实例的SetApartmentState方法设置线程的单元状态。
- 如需与COM对象进行互操作,必须创建并初始化Thread线程的单元状态。
- 如需操作的COM对象是UI组件,如Windows窗口,则Thread线程的单元状态必须设置为STA多线程单元。
由此可见,在大多数开发环境中,开发人员无需操心线程的单元状态。只有当与COM对象互操作,并且涉及Windows窗口相关的UI组件时,才需要将线程的单元状态设置为STA单线程单元。
在线程中调用SaveFileDialog相关推荐
- DllMain中不当操作导致死锁问题的分析——线程中调用GetModuleFileName、GetModuleHandle等导致死锁
之前的几篇文章已经讲解了在DllMain中创建并等待线程导致的死锁的原因.是否还记得,我们分析了半天汇编才知道在线程中的死锁位置.如果对于缺乏调试经验的同学来说,可能发现这个位置有点麻烦.那么本文就介 ...
- 线程中使用SaveFileDialog不能弹出窗体
在子线程中使用 SaveFileDialog 无法弹出窗体,主要是我们需要用主线程去处理 SaveFileDialog , 我们可以将子线程进行如下设置: public partial class F ...
- springboot项目在线程中调用service访问数据库
创建类ApplicationBeanFactory package com.xxx.comm.ApplicationBeanFactory;import org.springframework.bea ...
- 在线程中调用OpenFileDialog/SaveFileDialog报错处理方法
当在一个线程中使用 OpenFileDialog ofd = new OpenFileDialog(); ofd.ShowDialog(); 会报如下错误: 在可以调用 OLE 之前,必须将当前线程设 ...
- aardio学习笔记-在线程中调用数据
废话不多说,直接开干! 有个需求就是在线程中,获取数据 于是乎,线程只有匿名行数 请看文档代码! //打开控制台 io.open()//定义一个函数 func = function(){ return ...
- Android入门第37天-在子线程中调用Handler
简介 前一章我们以一个简单的小动画来解释了Handler. 这章我们会介绍在子线程里写Handler.如果是Handler写在了子线程中的话,我们就需要自己创建一个Looper对象了:创建的流程如下: ...
- Java线程 构造函数在那个线程中调用
在学习Java的ThreadLocal机制时,偶然发现这个问题,所以记录下来 public class ThreadConstruct {public static void main(String[ ...
- 在线程中调用PJSIP中的呼叫出现提示注册线程pj_thread_register的解决方法
在PJSIP的相关函数中(例如pjsua_call_make_call等),都增加了线程注册的判断, 下面以pjsua_call_make_call为例说明: 如果执行pjsua_call_make_ ...
- MFC子线程中调用updatedata()函数报错解决方法
在XXXDlg.h顶部添加 #define WM_UPDATEDATA WM_USER+100 关联自定义消息,在XXXDlg.cpp中消息处理函数中添加 ON_MESSAGE(WM_UPDATEDA ...
最新文章
- react引入多个图片_重新引入React:v16之后的每个React更新都已揭开神秘面纱。
- Android 轮询最佳实践 Service + AlarmManager+Thread
- 深度洞见|起底元宇宙风潮,如何重塑未来数字营销?
- boost::hana::make_range用法的测试程序
- 模式的秘密-观察者模式(二)
- modbus 台达a2_驱控智造未来 台达重磅发布多款工业自动化新品
- ANT出现“警告: 编码 GBK 的不可映射字符”解决方法
- jquery access方法 有什么用
- linux getopt_long函数,新手疑问:getopt_long()重入问题
- 1.1音响系统放大器设计
- 利用pytorch实现图像分类
- 企业微信群发消息提醒
- C语言,移动鼠标获得当鼠标当前位置坐标
- 删除的vue怎么找回_vue详情 恢复 删除
- cpy几天爬出密道问题
- 云服务器微信faq,微信公众平台常见问题FAQ
- 单细胞分析:质控实操(五)
- Oracle--rename
- 利用FME自动生成CAD图框
- spring data redis 配置