.netmultithreadingcom-interop

COM call hangs when called from .Net Thread


I have a c# static function that calls into a COM component.

When calling this function from a trivial WPF application it returns correctly. The code might look something like this:

var result = MyClass.StaticCall();
Debug.WriteLine("Hurrah!");

When I call that code, the variable will be set and the debug message output as expected.

If I wrap the same call in a thread, however, it never returns. The failing code might look something like this:

var foo = new Thread(new ThreadStart(() =>
                               {
                                   var result = MyClass.StaticCall();
                                   Debug.WriteLine("Hurrah!");
                               }));
foo.Start();

while (foo.IsAlive)
{
}

In this case, StaticCall will not return and the thread is blocked indefinitely.

What might be causing this behaviour?

Additional info:


Solution

  • foo.Start();
    
    while (foo.IsAlive)
    {
    }
    

    Yes, that's guaranteed deadlock. COM objects very often have thread affinity. They can tell COM that they are not thread-safe. The vast majority are not, just like .NET classes. COM then takes care of calling them in a thread-safe manner. Very unlike .NET, it leaves providing thread safety completely up to you.

    You created the COM object on your main thread. So COM must make calls on the COM object on the main thread as well to make the safety guarantee. It cannot do that, your main thread is busy. Is looping on the foo.IsAlive property. Any calls made on the worker thread cannot complete until your main thread goes idle. Your main thread can't go idle until the worker thread completes. Deadlock city.

    One fix is this:

    foo.Start();
    foo.Join();
    

    While Thread.Join() is a blocking call as well, it actually works. The CLR implements it, it knows that blocking an STA thread is illegal. It pumps a message loop, that allows the COM calls to be marshaled and that allows the thread to complete.

    Well, that's probably not what you want to do, you are doing something inside that loop. The only other thing you can do is calling Application.DoEvents(), that also pumps the message loop. That's dangerous, you have to disable the user interface so that the user cannot close the main window and cannot again start the thread.

    This is otherwise the There Is No Free Lunch principle, you cannot magically make code that is not thread-safe support threading. There is no concurrency, the COM object methods still run on your main thread. And if they take a long time then they'll still freeze the UI.

    Which leads to the other workaround, create the COM object on your worker thread instead. It must be STA and typically needs to pump a message loop. Example.