我有一个MFC C ++ Activex控件,我从后台线程DoWork()
函数调用一个Activex类函数。我发现它阻止了主线程。另一方面,如果我在C#中用Thread.Sleep(5000)
替换对c ++ COM函数的调用(需要5秒钟才能执行),它在后台线程中工作。
我尝试了很多东西,比如在工作线程中创建activex类的实例。但似乎COM函数总是在主线程上执行。我还附上了示例项目。
/* Here's the C# code */
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void exit_Click(object sender, EventArgs e)
{
this.Close();
}
private void cpp_Click(object sender, EventArgs e)
{
this.progressBar1.Style = ProgressBarStyle.Marquee;
this.backgroundWorker1.RunWorkerAsync(1);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (Convert.ToInt16(e.Argument) == 1)
{
// C++ function that takes 5 seconds to execute.
this.axActivexComponent1.AboutBox();
}
else
{
/* Normal C# code, for this it works fine */
System.Threading.Thread.Sleep(5000);
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.progressBar1.Style = ProgressBarStyle.Blocks;
MessageBox.Show("Thread Completed!!!");
}
private void csharp_Click(object sender, EventArgs e)
{
this.progressBar1.Style = ProgressBarStyle.Marquee;
this.backgroundWorker1.RunWorkerAsync(2);
}
}
答案 0 :(得分:2)
它是公寓的COM概念 - ActiveX控件属于单线程公寓,与您引用的线程相关联,#34;主线程"。从该公寓对COM接口的所有调用都将传输到主线程,包括从后台工作线程进行的调用。
COM为您提供编组以创建透明的代理/存根通道以使用来自其他线程的那些接口,但是在它们下面它们仍然会阻塞主线程,因为无论如何都会在真实对象上进行最终调用。
要使用多个线程,您需要多个线程的公寓感知COM对象(尽管所有或几乎所有ActiveX控件都使用单线程单元模型),或者控件应该提供自定义编组器,例如自由线程编组器,能够绕过默认的线程同步。
答案 1 :(得分:1)
COM对象的threading model听起来像是STA(单线程公寓):
标记为STA的COM对象必须在STAThread上运行,并且不能传递给其他线程,这是MFC C ++中任何UI元素的情况。但是,您的程序仍然可以有很多线程。
公寓状态默认为STA。例如,您的客户端应用程序中的主要入口点很可能标记为:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
如果对COM对象使用单独的/非UI线程,则需要在STA线程中显式,而背景/线程池工作线程不是这种情况。您必须自己创建线程,然后在该线程上实例化COM对象,例如:
Thread t = new Thread(new ThreadStart(ThreadProc));
t.SetApartmentState(ApartmentState.STA);
t.Start();
此外,STA要求必须有某种形式的“消息泵”(很像主要形式的UI线程使用的那种)。有关详细信息,请参阅this。
答案 2 :(得分:0)
通常的原因是COM对象无法在工作线程上运行。 COM是一种古老的技术,在线程化之前很常见。如果从多个线程调用,很多COM对象都会中断。因此,COM默认是在主线程上调用一个对象,除非它被标记为线程安全。
您无法通过C#代码修复此问题。