DotNet编程-星光伴我行

滴滴真谛 水滴石穿

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

当我在XNA程序中,应用OpenFileDialog执行选择文件操作时,出现以下错误:

Current thread must be set to single thread apartment (STA) mode before OLE  calls can be made. Ensure that your Main function has STAThreadAttribute    marked on it.

原因:

经查原因为 xna 程序为 STA程序,而应用OpenFileDialog执行选择文件操作与Xna程序不能共享一个线程的资源,故报错。

解决方法:

另一个STA线程。

            Thread t = new Thread(new ThreadStart(wcf.SendFile));
            t.IsBackground = true;
            t.ApartmentState = ApartmentState.STA;
            t.Start();

 

 

附:STA简介

C#的关键字 [STAThread]即 single-threaded apartment
一:
每个Thread都有一个关于ApartmentState的属性,可以把它设置为:STA或者MTA,或者UNKNOWN。
当你想指定工程的启动窗口的时候,你需要在该窗口类中申明一个Main()方法,并为这个方法设置[STAThread]属性。
详细信息,清查阅MSDN中关于Threading和COM Interop和COM+ Apartment Model的文章:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguidnf/html/cpconmanagedunmanagedthreading.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguidnf/html/cpconadvancedcominterop.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/htm/pgservices_synchronization_8703.asp

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

三:
C#中,[STAThread]代表什么意思?如何用?
Single Thread Apartment

>Why is STAThread required?

it changes the apartment state of the current thread to be single threaded

>Single Thread Apartment vs MultiThread Apartment?

Correct: With the STAThread attribute, you will be interacting with COM processes in a
"Single Threading Apartment" model. Without it, you will be interacting with COM processes
in the "Multiple Threading Apartment" model.

> so why do I need it....or why would I want it at some point?

You may want to interact with a COM process in a MTA model for performance reasons. You may
want to interact with a COM process in a STA model because of a design requirement. For example,
to use the Windows clipboard (System.Windows.Forms.Clipboard) you must be calling from a thread
running in a STA. If the calling thread was started by your application you can set the ApartmentState
(System.Threading.ApartmentState) before starting, but if you want to use the clipboard from your a
pplication's main thread, you need to use the System.STAThread attribute on your Main method.

> why does Main( ) only function as an entry point when it is declared static?

The simple answer is that is just the way that Microsoft designed the language. One way you can look at
this though, is there should only be 1 "instance" of your Main method - the main method has nothing to do
with any specific instances of the class it is defined in, and should therefore be static. In my opinion
it might have been a good idea to give the Main method a property similar to a static contructor where it is
executed once, and only once. Anyway, because the Main method is static, you can execute your program without
having to create any arbitrary objects.

 

 

===============================================================================

[STAThread]
STAThread:Single     Thread     Apartment Thread.(单一线程单元线程)
[]是用来表示Attributes;

[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,则不初始化该线程。

以下是找到的一个资料介绍:
Q. When I create a c# project from scratch in VS.NET, the generated code always have a [STAThread] attribute above the main routine. What does the STAThread attribute really do? Can I change it to MTAThread instead? I have searched website and books, no one seems to explain this well.

Asked by anon. Answered by the Wonk on February 17, 2003


A.

The STAThreadAttribute marks a thread to use the Single-Threaded COM Apartment if COM is needed. By default, .NET won't initialize COM at all. It's only when COM is needed, like when a COM object or COM Control is created or when drag 'n' drop is needed, that COM is initialized. When that happens, .NET calls the underlying CoInitializeEx function, which takes a flag indicating whether to join the thread to a multi-threaded or single-threaded apartment.

A multi-threaded apartment (MTA) in COM is more efficient, since any of a number of RPC threads from a pool can be used to handle a request. However, an object on the MTA thread needs to protect itself from multiple threads accessing it at the same time, so that efficiency comes at a cost.

The single-thread apartment (STA) in COM is inherently single-threaded and therefore no additional thread synchronization is needed. The STA is implemented using the thread's Windows message queue, which is how requests to objects on an STA are serialized. Because of how the STA thread is implemented, calls to objects on that thread are serialized with Windows message handling on that thread, making sure that everything, both the COM objects and the underlying windowing objects, e.g. HWNDs, are all synchronized. This is necessary for UI-oriented COM objects, like controls and drag 'n' drop, which must also be synchronized together with the UI.

When COM is needed .NET will call CoInitializeEx, picking the MTA by default because it's more efficient. However, to get the synchronization needed for controls, windows and drag 'n' drop, you need to mark a thread's entry point with the STAThreadAttribute to let .NET know to initialize the UI thread on the STA. All of the VS.NET project templates put that attribute in to make sure you don't forget:
大致意思是:由于很多COM在.NET环境下如果使用多线程的话,会导致引用的COM不能正常运行,而如果不声明程序为STAThread的话,.NET就会自动使用多线程来提高效率,这样就会导致不可预知的后果。


以下引用另一同辈的发言:http://blog.csdn.net/qilang/archive/2006/06/06/775605.aspx
STA不是单线程的意思.英文为single threaded apartment.是一种套间(或译为公寓)线程模式.

sta thread并不表明应用程式的类型,和应用程序不搭界,恰相反,一个应用程序可以有多个线程.每个线程也可以有多个组件或对象.以前win16位系统的组件线程模式才真正是单线程.这是一种被淘汰了的模式.
线程模式用于处理组件在多线程的环境里并行与并互的方式.比如套间线程(STAThread)模式中接口跨线程传递必须被调度(Marshal),不调度直传肯定会失败!而MTA或FreeThread模式中的接口可以不经调度直接传递.
这种调度在特定的环境中非常影响性能(可有几百倍之差).如VB里只支持STAThread模式.FreeThread模式的组件会在里面表现成和跨进程一样慢!
线程模式是微软的COM基础中的极其重要的概念.一定要吃透!
我对.net真是一窍不通(没空去弄,对不起微软去年的奖赏).但我可以肯定,C#中的[STAThread]属性是应用程序的套间初始化代码.可以直接理解成SDK里的
CoInitialize(NULL);
初始一个STA套间实际上是相当于开了一个消息窗口,所有调用经此窗口过程调度到组件内.
同理[MTAThread](不知有没有这个属性,自已去查)
可以理解成
CoInitializeEx(NULL,COINIT_MULTITHREADED )
这经常是一个对初入com大门的人来说,有一定难度但必须过的一道关.
=====================================================================================

C#学习笔记(一)-- 入门的困惑
简单的我就不写了,主要写一下C#学习中的要点和难点。
1.由HelloWorld开始
先看一段基本上每本C#书里都会讲到的例子,很老土。
using System;
namespace test
{
     class Class1
     {
         [STAThread]
         static void Main(string[] args)
         {
              System.Console.WriteLine("Hello,World!");
         }
     }
}
先引用一个命名空间System,再定义一个自己的命名空间test,里面有一个类Class1,属性[STAThread],一个入口的Main方法,注意:跟JAVA不一样,Main首名母是大写,Main必须是static的。不然怎么开始呢?难倒要实例化才行?哈哈,定义为static就是把它放在椎里。这里规举。
 
2.命名空间
再来看看System.Console.WriteLine("Hello,World!");输一名语名到控制台。调用System命名空间里的Console类的WriteLine方法。System命名空间是前面我们已经引用了的using System;
 
你也可以在引用的时候改个名字output,那么在调用的时候就是output.Console.WriteLine("Hello,World!");
试一试:
using output=System;
 
namespace test
{
     class Class1
     {
         [STAThread]
         static void Main(string[] args)
         {
              output.Console.WriteLine("Hello,World!");
         }
     }
}
运行报错:F:\mydoc\Visual Studio Projects\test\Class1.cs(7): 找不到类型或命名空间名称“STAThread”(是否缺少 using 指令或程序集引用?)
嘿嘿,是[STAThread]惹的祸。干掉它。再试,搞定。
[STAThread]是Single  Thread  Apartment单线程套间的意思。是一种线程模型。其它的好像还是MTA(多线程套间)、Free  Thread(自由线程)。这个属性要加在主  Main  上。这个属性只在  Com  Interop  所用,如果全部是  managed  code  则无用。简单的说法:[STAThread]指示应用程序的默认线程模型是单线程单元 (STA)。启动线程模型可设置为单线程单元或多线程单元。如果未对其进行设置,则该线程不被初始化。
也就是说如果你用的.NET Framework,并且没有使用COM Interop,一般不需要这个Attribute。
 
明白了吧。
 
注意,using指令是用于命名空间的。变化着用一下,也可以为类创建别名:
using output=System.Console;
 
namespace test
{
     class Class1
     {
         //[STAThread]
         static void Main(string[] args)
         {
              output.WriteLine("Hello,World!");
         }
     }
}
这样也行。。。
 
命令空间是可以嵌套的。如:
using System;
 
namespace test
{
     namespace t1
     {
         class Class1
         {
              static void Main(string[] args)
              {
                   System.Console.WriteLine("t1.Class1");
              }
         }
     }
 
     namespace t2
     {
         class Class2
         {
              static void Main(string[] args)
              {
                   System.Console.WriteLine("t2.Class2");
              }
         }
     }
}
运行,报错。我是故意的(台下:大骗子)。J不要扔砖头啊。不要这么容易就放弃嘛,要执着。
看错误F:\mydoc\Visual Studio Projects\test\Class1.cs(9): 程序“F:\mydoc\Visual Studio Projects\test\obj\Debug\test.exe”定义了不止一个入口点:“test.t1.Class1.Main(string[])”
 
因为你的命名空间test里定义了二个Main方法,所以呢,不用我说了吧。
using System;
 
namespace test
{
     namespace t1
     {
         class Class1
         {
              static void Main(string[] args)
              {
                   System.Console.WriteLine("t1.Class1");
                   System.Console.WriteLine(t2.Class2.MyFunction());
              }
         }
     }
 
     namespace t2
     {
         class Class2
         {
              public static string MyFunction()
              {
                   return "t2.Class2";
              }
         }
     }
}
 
外部程序引用的时候就是这样:using test.t1;或using test.t2;
 
入门就这些问题。
打开Visual Studio .NET 2003 命令提示键入ILDASM,这个程序可以查看编译后的元数据。
 
网上查一下Reflector这个软件。干什么用的。反编译呀。。。。寒。
=========================================================================
单线程套间(STAThread) vs. 多线程套间(MTAThread )
[原文链接]:  STA Thread vs. MTAThread(WHost)

 

          最近我遇到一个有趣的线程问题,想跟大家分享一下,以免大家也遇到这种容易混淆的问题。

          打开一个其他人写的C#程序,为了下面的讲解,我把这个程序叫作”DeltaEngine”。DeltaEngine会调用一个本地的程序集,并处理其中某些事件。然后建一个工程,把DeltaEngine作为库项目引用,这个工程又被一个VB程序引用。我把这个VB程序叫做"VBApp"。所以,引用结构就像:

         VBApp (VB) -> DeltaEngine (C#) -> NativeCode

      当我把VBApp作为启动程序运行时,它会一直等待NativeCode里面的事件在DeltaEngine中被处理。我注意到,如果我把DeltaEngine作为启动程序来运行的话,事件就会像预期那样得到处理。我花了很多时间想解决这个问题,但还是很困惑。

      最后有人指出,VB项目的默认线程模型是单线程套间(SingleThreaded  Apartment),而C#项目的默认线程模型是多线程套间 (Multithreaded Apartment)。因为DeltaEngine最初是作为C#工程开发的,被默认为使用MTA。当使用VBApp作为启动程序调用DeltaEngine时,DeltaEngine就会使用STA。因为这样,DeltaEngine就会开始等待其实它已经唤起的事件,所以DeltaEngine会一直等待下去。如果我把DeltaEngine作为启动程序运行,它就会使用MTA并且会像预期那样工作。

      解决的方法就是把MTAThreadAttribute加进VBApp的主函数里面。如下:

    <MTAThread> Sub Main()

      类似,如果你想把STAThread attribute加进C#的主方法里面,如下:

      [STAThread]

      static void Main()

      WindowsForms程序要求使用STA,所以创建C# Winapp后,你可以在Program.cs看见像这样的代码。

      我就不重复一些已经有的关于STATread 和MTAThread的文件,所以我给出下面的一些link可以学到更多关于STATread 和MTAThread的东西。

     STAThreadAttribute

      http://msdn2.microsoft.com/en-us/library/system.stathreadattribute(VS.71).aspx                         

      http://blogs.msdn.com/jfoscoding/archive/2005/04/07/406341.aspx

      MTAThreadAttribute

      http://msdn2.microsoft.com/en-us/library/system.mtathreadattribute(VS.71).aspx

======================================================================================
C# 中WindowsForm 的初始化信息 [STAThread]
static class Program

    {

        /// <summary>

        /// The main entry point for the application.

        /// </summary>

        [STAThread]

        static void Main()

        {

            Application.EnableVisualStyles();

            Application.SetCompatibleTextRenderingDefault(false);

            Application.Run(new Form1());

        }

 

 

    }

 

以上的代码是VS 2005中默认的初始化信息.

Application.EnableVisualStyles();

简单的说就是让你的控件显示出来.当然是在WindowsForm 中

此方法为应用程序启用可视样式。如果控件和操作系统支持视觉样式,则控件将以视觉样式进行绘制。若要使 EnableVisualStyles 生效,必须在应用程序中创建任何控件之前调用它;EnableVisualStyles 通常是 Main 函数的第一行.

 

下面的代码示例演示如何在 Main 函数中调用 EnableVisualStyles 来启用应用程序的视觉样式

对于支持   FlatStyle   属性的控件,请确保将   FlatStyle   属性设置为   FlatStyle.System   值。

 


using System;

using System.Drawing;

using System.Windows.Forms;
namespace VStyles

{

    public class Form1 : System.Windows.Forms.Form

    {

      private System.Windows.Forms.Button button1;
     [STAThread]

        static void Main()

        {

            Application.EnableVisualStyles();

            Application.Run(new Form1());

        }

 

        public Form1()

        {

            this.button1 = new System.Windows.Forms.Button();

            this.button1.Location = new System.Drawing.Point(24, 16);

            this.button1.Size = new System.Drawing.Size(120, 100);

            this.button1.FlatStyle = FlatStyle.System;

            this.button1.Text = "I am themed.";

 

            // Sets up how the form should be displayed and adds the controls to the form.

            this.ClientSize = new System.Drawing.Size(300, 286);

            this.Controls.Add(this.button1);

 

            this.Text = "Application.EnableVisualStyles Example";

 

        }

    }

}

 

msdn介绍

http://msdn.microsoft.com/en-us/library/windows/desktop/ms680112(v=vs.85).aspx

Using single-threaded apartments (the apartment model process) offers a message-based paradigm for dealing with multiple objects running concurrently. It enables you to write more efficient code by allowing a thread, while it waits for some time-consuming operation to complete, to allow another thread to be executed.

Each thread in a process that is initialized as an apartment model process, and that retrieves and dispatches window messages, is a single-threaded apartment thread. Each thread lives within its own apartment. Within an apartment, interface pointers can be passed without marshaling, and therefore, all objects in one single-threaded apartment thread communicate directly.

A logical grouping of related objects that all execute on the same thread, and therefore must have synchronous execution, could live on the same single-threaded apartment thread. However, an apartment model object cannot reside on more than one thread. Calls to objects in other processes must be made within the context of the owning process, so distributed COM switches threads for you automatically when you call on a proxy.

The interprocess and interthread models are similar. When it is necessary to pass an interface pointer to an object in another apartment (on another thread) within the same process, you use the same marshaling model that objects in different processes use to pass pointers across process boundaries. By getting a pointer to the standard marshaling object, you can marshal interface pointers across thread boundaries (between apartments) in the same way you do between processes. (Interface pointers must be marshaled when passed between apartments.)

Rules for single-threaded apartments are simple, but it is important to follow them carefully:

  • Every object should live on only one thread (within a single-threaded apartment).
  • Initialize the COM library for each thread.
  • Marshal all pointers to objects when passing them between apartments.
  • Each single-threaded apartment must have a message loop to handle calls from other processes and apartments within the same process. Single-threaded apartments without objects (client only) also need a message loop to dispatch the broadcast messages that some applications use.
  • DLL-based or in-process objects do not call the COM initialization functions; instead, they register their threading model with the ThreadingModel named-value under the InprocServer32 key in the registry. Apartment-aware objects must also write DLL entry points carefully. There are special considerations that apply to threading in-process servers. For more information, see In-Process Server Threading Issues.

While multiple objects can live on a single thread, no apartment model object can live on more than one thread.

Each thread of a client process or out-of-process server must call CoInitialize, or call CoInitializeEx and specify COINIT_APARTMENTTHREADED for the dwCoInit parameter. The main apartment is the thread that calls CoInitializeEx first. For information on in-process servers, see In-Process Server Threading Issues.

All calls to an object must be made on its thread (within its apartment). It is forbidden to call an object directly from another thread; using objects in this free-threaded manner could cause problems for applications. The implication of this rule is that all pointers to objects must be marshaled when passed between apartments. COM provides the following two functions for this purpose:

These functions wrap calls to CoMarshalInterface and CoUnmarshalInterface functions, which require the use of the MSHCTX_INPROC flag.

In general, the marshaling is accomplished automatically by COM. For example, when passing an interface pointer as a parameter in a method call on a proxy to an object in another apartment, or when calling CoCreateInstance, COM does the marshaling automatically. However, in some special cases, where the application writer is passing interface pointers between apartments without using the normal COM mechanisms, the writer must handle the marshaling manually.

If one apartment (Apartment 1) in a process has an interface pointer and another apartment (Apartment 2) requires its use, Apartment 1 must call CoMarshalInterThreadInterfaceInStream to marshal the interface. The stream that is created by this function is thread-safe and must be stored in a variable that is accessible by Apartment 2. Apartment 2 must pass this stream to CoGetInterfaceAndReleaseStream to unmarshal the interface and will get back a pointer to a proxy through which it can access the interface. The main apartment must remain alive until the client has completed all COM work (because some in-process objects are loaded in the main apartment, as described in In-Process Server Threading Issues). After one object has been passed between threads in this manner, it is very easy to pass interface pointers as parameters. That way, distributed COM does the marshaling and thread switching for the application.

To handle calls from other processes and apartments within the same process, each single-threaded apartment must have a message loop. This means that the thread's work function must have a GetMessage/DispatchMessage loop. If other synchronization primitives are being used to communicate between threads, the MsgWaitForMultipleObjects function can be used to wait both for messages and for thread synchronization events. The documentation for this function has an example of this sort of combination loop.

COM creates a hidden window using the Windows class "OleMainThreadWndClass" in each single-threaded apartment. A call to an object is received as a window message to this hidden window. When the object's apartment retrieves and dispatches the message, the hidden window will receive it. The window procedure will then call the corresponding interface method of the object.

When multiple clients call an object, the calls are queued in the message queue and the object will receive a call each time its apartment retrieves and dispatches messages. Because the calls are synchronized by COM and the calls are always delivered by the thread that belongs to the object's apartment, the object's interface implementations need not provide synchronization. Single-threaded apartments can implement IMessageFilter to permit them to cancel calls or receive window messages when necessary.

The object can be reentered if one of its interface method implementations retrieves and dispatches messages or makes an ORPC call to another thread, thereby causing another call to be delivered to the object (by the same apartment). OLE does not prevent reentrancy on the same thread, but it can help provide thread safety. This is identical to the way in which a window procedure can be reentered if it retrieves and dispatches messages while processing a message. However, calling an out-of-process single-threaded apartment server that calls another single-threaded apartment server will allow the first server to be reentered.

Related topics

Accessing Interfaces Across Apartments
Choosing the Threading Model
Multithreaded Apartments
In-Process Server Threading Issues
Processes, Threads, and Apartments
Single-Threaded and Multithreaded Communication

 

posted on 2011-11-24 20:12  DotNet编程  阅读(1085)  评论(0编辑  收藏  举报