当前线程不在单线程单元中,因此无法实例化 ActiveX 控件解决办法

时间:2022-09-01 18:37:03

(一)引经据典(MSDN):

1.单元是进程内部具有相同线程访问要求的对象的逻辑容器。同一单元中的所有对象都可以接收从该单元中的任何线程发出的调用。.NET Framework 不使用单元,托管对象自己负责以线程安全的方式使用所有共享资源。

由于 COM 类使用单元,因此公共语言运行库需要在 COM interop 的情况下调用 COM 对象时创建并初始化一个单元。托管线程可以创建并进入只允许有一个线程的单线程单元 (STA) 或者包含一个或多个线程的多线程单元 (MTA)。通过将线程的 ApartmentState 属性设置为 ApartmentState 枚举值之一,可以控制所创建的单元的类型。由于给定线程只能初始化 COM 单元一次,因此在第一次调用非托管代码之后就不能更改单元类型。

2.public enum ApartmentState

成员
MTA Thread 将创建并进入一个多线程单元。 
STA Thread 将创建并进入一个单线程单元。 
Unknown 尚未设置 ApartmentState 属性。

http://www.cnblogs.com/winzheng/archive/2009/05/11/1454328.html

3.Sample:

 

当前线程不在单线程单元中,因此无法实例化 ActiveX 控件解决办法Code
using System;
using System.Threading;

class ApartmentTest
{
    
static void Main()
    {
        Thread newThread 
= new Thread(new ThreadStart(ThreadMethod));
       //ApartmentState Property is obsolete;
        下面的语句是关键
        newThread.SetApartmentState(ApartmentState.MTA);

        
// The following line is ignored since
        
// ApartmentState can only be set once.
        newThread.SetApartmentState(ApartmentState.STA);
        Console.WriteLine(
"ThreadState: {0}, ApartmentState: {1}",
            newThread.ThreadState, newThread.ApartmentState);
        newThread.Start();
        
// Wait for newThread to start and go to sleep.
        Thread.Sleep(300);
        
try
        {
            
// This causes an exception since newThread is sleeping.
            newThread.SetApartmentState(ApartmentState.STA);
        }
        
catch(ThreadStateException stateException)
        {
            Console.WriteLine(
"/n{0} caught:/n" +
                
"Thread is not in the Unstarted or Running state.",
                stateException.GetType().Name);
            Console.WriteLine(
"ThreadState: {0}, ApartmentState: {1}",
                newThread.ThreadState, newThread.GetApartmentState());
        }
    }

    
static void ThreadMethod()
    {
        Thread.Sleep(
1000);
    }
}

 

(二)其他方法问题产生的原因:

[PermissionSet(SecurityAction.Demand,   Name   =   "FullTrust")]   
[System.Runtime.InteropServices.ComVisible(true)]

如果在主线程中,在入口添加[STAThread]

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

(三)特定环境特定条件

最近在做一个简单的屏幕保护程序,功能很简单就是在机器长时间不用的情况下,间隔一定时间启动一 个屏幕保护程序。我这里有个特殊情况就是要播放一些媒体文件(.avi,.wmv)和flash文件等,因此我使用的是windows自带的 WindowsMediaPlayer来做播放器。
由于一些特殊原因,我需要在一个没有GUI的程序中启动屏幕保护程序,因此最初使用的是System.Threading.Timer来做定时器的,但是在测试中发现总是出席以下异常:
Message: 因为当前线程不在单线程单元中,故无法实例化 ActiveX 控件“6bf52a52-394a-11d3-b153-00c04f79faa6”。
Source: System.Windows.Forms
   at System.Windows.Forms.AxHost..ctor(String clsid, Int32 flags)
   at System.Windows.Forms.AxHost..ctor(String clsid)
   at AxWMPLib.AxWindowsMediaPlayer..ctor()
   ...
上面的引自博客园:http://www.cnblogs.com/winzheng/archive/2009/05/11/1454328.html下面的引自百度知道:http://zhidao.baidu.com/question/62226910 有关webbrowser的问题的
dll里的WebBrowser 调用了Navigate 而这个DLL启用的线程导致的这个错误,通过委托的方式解决,只要是跨线程刷新界面都会出错, 意思是只要线程了使用的数据赋值到界面上都会出现这样的错误.都可以通过委托解决!改写Navigate : 
public delegate void DelUserHandler(string url);

public void NavigateUrl(string url)
{
if (this.webbrowser1.InvokeRequired)
{
DelUserHandler handler = new DelUserHandler(NavigateUrl);
this.Invoke(handler, url);
}
else
{
this.webbrowser1.Navigate(url);

}}