2

I have a MFC C++ Activex control and I am calling an Activex class function from a background thread DoWork() function. I found that it blocks the main thread. On the other hand, if I replace call to c++ COM function(which takes 5 seconds to execute) by Thread.Sleep(5000) in C#, it works in background thread.

I have tried many things like creating the instance of an activex class in a worker thread also. But it seems that the COM function always executes on the main thread. I have also attached the sample project.

/* 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);
    }
}
1
  • 2
    COM keeps objects thread-safe when they ask for it. Most do. If you want to run its code on another thread then you must create the object on that thread. Commented Feb 12, 2015 at 11:03

3 Answers 3

2

It is the COM concept of apartments - the ActiveX control belongs to single threaded apartment associated with the thread you refer to as "main thread". All calls to COM interfaces from that apartment are transferred to the main thread, including those to make from background worker threads.

COM provides you marshaling to create transparent proxy/stub channels to use those interfaces from other threads, but underneath they still block main thread since the ultimate call on the real object is anyway taking place there.

To use multiple threads, you need multiple threaded apartment aware COM objects (although all or almost all ActiveX controls use single threaded apartment model), or the control should provide custom marshaller on its side, such as free threaded marshaler, to be able to bypass default thread synchronization.

Sign up to request clarification or add additional context in comments.

Comments

1

The COM object's threading model sounds like it is STA (single-threaded apartment):

COM objects marked as STA must be run on an STAThread, and cannot be passed to other threads, which is the case for any UI element in MFC C++. However, your program can still have many threads.

The apartment state is by default STA. For example, your main entry point from your client application is most likely marked as:

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {         

If you use a separate/non UI thread with the COM object, it needs to be explicitly in an STA thread, which isn't the case for background/thread-pooled worker threads. You have to create the thread yourself, and then instantiate the COM object on that thread E.g.:

Thread t = new Thread(new ThreadStart(ThreadProc));
t.SetApartmentState(ApartmentState.STA);

t.Start();

Also, an STA requirement is there must be some form of "message pump" (much like the one the main forms UI thread uses). See this for details.

7 Comments

Hi Jeb, Maybe I didn't mention, but I already tried creating my Thread, but it gives same result. Do you have any sample code for this?
@MohanSawant: Are you creating your COM object in the dedicated thread "t" from the code sample? I have updated my answer a little (you may need a message pump). See also: stackoverflow.com/questions/10643652/…
I want my thread to do 2 things. 1. Instantiate a COM control object and add it to Form. 2. Execute the COM function. The problem is that Windows doesn't allow to add controls to form from any other thread. If I create a dummy form in threading function and add my control to it everything works fine as shown below. private void AddControl() { axControl = new AxControlLib.AxControl(); /* Dummy form / Form frm = new Form(); frm.Visible = false; frm.Controls.Add(axControl); / This is what I want */ this.Controls.Add(axControl); axControl.AboutBox(); }
You will need to call Application.Run inside your new UI thread to create your message loop. The same cross-threading laws apply (e.g. you can only interact with the handle for a control on the thread inside which it was created), so you can't do anything on your other UI thread without switching contexts. Perhaps this will help? stackoverflow.com/questions/745057/…
Did this approach help @MohanSawant? What was your final solution?
|
0

The usual cause for this is that the COM object isn't capable of running on worker threads. COM is ancient technology, well before threading was common. A lot of COM objects would break if called from multiple threads. Therefore, the COM default is that an object is called on the main thread unless it's marked as threadsafe.

You can't fix that from your C# code.

2 Comments

Well, doesn't sound very encouraging. Do you mean to say that I need to fix it in the COM code itself instead of bandaging in C#?
Well, there's the bandage of creating a COM thread separate from your main C# thread. You'd now have this COM thread block for 5 seconds, not the main thread.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.