Reputation: 1091
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:
Upvotes: 2
Views: 1249
Reputation: 942428
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.
Upvotes: 2
Reputation: 16162
If the normal call -without start new thread- to that method does not have any problem, then it is mostly because you don't invoke that method from the same thread that the com object was created on. to create a custom invoking mechanism see this.
Upvotes: 0
Reputation: 36082
I seem to recall something about COM requiring an active message loop running in the thread on which COM interop is used. I don't know the details of .NET's COM interop implementation, but in good old Win32 if you tried to make an interprocess call on COM after doing all the special initialization steps it would still freeze at the COM call, just as you describe. Add a simple peekmessage loop to the background thread and the call would go through. Perhaps something similar needs to be done in your .NET code?
Background: COM interprocess comms rely on SendMessage via a window handle created by COM. If that window handle is created on your main thread, then messages sent to that window handle will be processed by your main thread's message loop and all is well. If that window handle is created on a background thread, messages sent to that window handle can only be retrieved by a message loop running in that thread.
Try this: Make one COM call from your main thread before making COM calls on the background thread. This will force COM to initialize on your main thread where you have a message loop. See if this unblocks the background COM calls. Just a thought.
Upvotes: 3