none
Accessing COM object from a managed thread RRS feed

  • Question

  • Hi

    My C# client makes access to my C++ COM object (STA apartment) successfully from the main thread but it fails to call the same methods from a managed thread.

    // successfull call from the main thread:
    // method TestNoParams is called and it puts a line into a log file

    comObj.TestNoParams();

    The thread is started from the WinForms button click handler with the code like the following:

    // start thread
    TestRandom testRandom = new TestRandom();
    ManualResetEvent done = new ManualResetEvent(false);

    Thread thread = new Thread(new ParameterizedThreadStart(testRandom.Run));
                  thread.SetApartmentState(ApartmentState.STA);
                  thread.Start(comObj);

    done.WaitOne();

    ////////////////////////////////
    class TestRandom
    {
        public void Run(Object param)
        {
            IComObj comObj = (IComObj)param;

            // failing call from a managed thread
            // method TestNoParams is not called at all
            // no exception is thrown by CLR
            // call returns silently
            //
            // other similar methods taking parameters
            // or returning results
            // throw access violation exception
            comObj.TestNoParams();
        }
    }

    Could anyone say what the reason is?

    Tuesday, July 28, 2009 11:36 AM

Answers

  • Your done.WaitOne() call is the problem.  COM marshals the method call back to the main thread through the message loop.  That won't work, you've blocked the main thread.  Deadlock is the result.  It is not legal to block STA threads.  Perhaps more to the point, there isn't much point in calling single-threaded COM objects from a thread, there is no concurrency.

    Blocking is out, the best way to handle this is to marshal a Completed callback from the thread to the main thread with Control.BeginInvoke().

    Hans Passant.
    Tuesday, July 28, 2009 12:10 PM
    Moderator

All replies

  • Your done.WaitOne() call is the problem.  COM marshals the method call back to the main thread through the message loop.  That won't work, you've blocked the main thread.  Deadlock is the result.  It is not legal to block STA threads.  Perhaps more to the point, there isn't much point in calling single-threaded COM objects from a thread, there is no concurrency.

    Blocking is out, the best way to handle this is to marshal a Completed callback from the thread to the main thread with Control.BeginInvoke().

    Hans Passant.
    Tuesday, July 28, 2009 12:10 PM
    Moderator
  • I've removed all waiting ( and made local variable "Thread thread" a member to keep the thread until the application exit).
    But the result is the same.

    Replacing
          thread.SetApartmentState(ApartmentState.STA);
    with
          thread.SetApartmentState(ApartmentState.MTA);

    also does not help.
    What's wrong?
    Tuesday, July 28, 2009 12:53 PM
  • Is your app actually pumping a message loop?  Look in the Main() method, is there a call to Application.Run()?  A thread is MTA by default, changing the apartment type has no effect.  What matters is the apartment type of the thread that created the COM object.  It has to be STA for yours, it will be when the Main() method has the [STAThread] attribute.

    Long story short: this won't work if your app is a Console mode app.

    Hans Passant.
    • Marked as answer by mpoleg Tuesday, July 28, 2009 2:38 PM
    • Unmarked as answer by mpoleg Tuesday, July 28, 2009 2:39 PM
    Tuesday, July 28, 2009 1:07 PM
    Moderator
  • [

     

    STAThread]
    static void Main()
    {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
    }
    • Edited by mpoleg Tuesday, July 28, 2009 1:19 PM
    Tuesday, July 28, 2009 1:18 PM
  • The time between 

        thread.Start

    and
       
        testRandom.Run

    running is around 20 secs.
    May be it is timeout?
    Tuesday, July 28, 2009 1:23 PM
  • There's no timeout.  As long as you keep the UI thread alive, this should work without problems.

    Hans Passant.
    Tuesday, July 28, 2009 1:44 PM
    Moderator
  • I've managed to create a DAO database file using the same approach as being discussed.

    I replaced my C++ COM object assembly with third-party DAO one.

    Object of type dao.DBEngineClass was created in the main thread.
    And object of type dao.Database was created in a thread. Successfully.

    So it's obvious that the problem is with my C++ COM object.

    I had created interop dll for this COM object using tlbimp.exe
    and added this interop dll to the list of references of my C# client
    (instead of COM object dll itself). Maybe this is a reason?

    Could you, please, investigate what the problem is with this C++ COM object?

     

    Tuesday, July 28, 2009 2:37 PM