Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# STAThread COMException

Tags:

c#

.net

com

sta

I have an external component (C++), which I want to call from my C# code.

The code is something like this:

using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace dgTEST
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            ExtComponentCaller extCompCaller = new ExtComponentCaller();
            result = extCompCaller.Call(input);

            Thread t = new Thread(new ThreadStart(() =>
            {
                try
                {
                    result = extCompCaller.Call(input);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }));

            t.SetApartmentState(ApartmentState.STA);
            t.Start();
            t.Join();
        }
    }
}

So the problem is that, at the first call it's working well, the external component called, I got back result.

But when I try to call it in an other thread, I got an exception: System.InvalidCastException: Unable to cast COM object of type 'System.__ComObject' ... . I'm sure this exception throwed, because of the STAThread. Because if I remove the [STAThread] attribute from the Main function, the same occurs with the first call of the external component, which was worked fine.

How can I call this external component from an other thread to get rid of this exception?

UPDATE-------------

Other crazy thing occurs now. When I start the program from Visual Studio with F5, the problem occurs in the first call as well, but when I execute directly the binary .exe file, it's working (from the other thread it isn't :( ). If I switch the build from Debug to Release and starting it from Visual Studio with F5, the first call working again.

Why does it happen?

Thanks for you help in advance!

Best Regards, Zoli

like image 986
Zoltán Barna Avatar asked Apr 19 '13 09:04

Zoltán Barna


1 Answers

Threading is never a small detail. If code isn't explicitly documented to support threading then the 99% odds are that it doesn't support it.

And clearly this component doesn't support threading. Creating another STA thread is not the magic solution, it is still a different thread. The InvalidCastException tells you that it also is missing the proxy/stub support that's required to marshal calls from a worker thread, like the one that you are trying to create. Required to make thread-safe calls to code that isn't thread-safe. Albeit that you did break the contract for an [STAThread], it must pump a message loop. It is the message loop that allows making calls from a worker thread to a component that isn't thread safe. You get a message loop from Application.Run().

This is where the buck stops. It isn't thread-safe, period. Even if fix your main thread or ask the vendor or author to supply you with the proxy/stub, you still haven't accomplished what you set out to do, it won't actually run on that worker thread you created. So it must look like this:

    static void Main(string[] args)
    {
        Thread t = new Thread(new ThreadStart(() =>
        {
             ExtComponentCaller extCompCaller = new ExtComponentCaller();
             result = extCompCaller.Call(input);
        }));

        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();
    }

Which creates the object on the same thread that you make the calls from so it is thread-safe. There's still the problem that this worker thread doesn't pump a message loop, COM components tend to rely on that. You'll find out whether that's a problem or not from deadlock or events that don't run. If it already worked okay in your test program when you called it from the main thread then you are probably okay with not pumping.

like image 175
Hans Passant Avatar answered Nov 16 '22 04:11

Hans Passant