views:

1165

answers:

4

Hi guys,

I'm writing an application that uses a COM library. I have a class library that wraps the calls to the COM library and adds some more functionality. Eventually this will run as a Windows Service. For now I test it using a Winforms test harness. When the class library is created by the test harness everything seems to be working OK. The problems start when I try to run it as a service. It is created OK and even the first call to the COM is OK. The COM object then raises an event that I handle and in response to the result in the event I call another function in the COM library. The function is invoked successfully in the case when I run it from the test harness but when running as a Service an exception is thrown:

System.InvalidCastException occurred Message="Unable to cast COM object of type '' to interface type ''. This operation failed because the QueryInterface call on the COM component for the interface with IID '{350ADD2A-18CB-4D9C-BE28-48D53F14E6FB}' failed due to the following error: The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))."

I can see that there are threading issues. In the test harness case all this calls happen on the main thread and in the case of the Windows Service both the Service OnStart override and the COM event handler are on different threads. I've been trying different things without success. Any suggestions?

Rad.

+1  A: 

Some COM components must be accessed only by STA threads. If this is the case for yours, you can do your COM work in an STA thread like so:

RunInSTAThread( () => com_object.DoSomething() );

private static void RunInSTAThread(ThreadStart thread_start)
        {
            Exception threadEx = null;
            ThreadStart wrapped_ts = () =>
                                         {
                                             try
                                             {
                                                 thread_start();
                                             }
                                             catch (Exception ex)
                                             {
                                                 MethodInfo preserveStackTrace =
                                                     typeof(Exception).GetMethod("InternalPreserveStackTrace",
                                                                                 BindingFlags.Instance | BindingFlags.NonPublic);
                                                 preserveStackTrace.Invoke(ex, null);
                                                 threadEx = ex;
                                             }
                                         };
            Thread thread = new Thread(wrapped_ts);
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();
            if (threadEx != null)
            {
                throw threadEx;
            }
        }

This may not be the best use of threads (a new thread for every call) for your situation, but it's a starting point.

jlew
@Jeremy Lew: It's also completely not needed. When creating a RCW, the CLR will place the object in it's own thread if the creation happens on a non-STA thread. You are also not pumping messages on the STA thread, which is a MUST for dealing with STA COM objects.
casperOne
These are some good points. It still remains unclear to me why it works when started from Winforms and not when started from the Service.
Radoslav Hristov
A: 

Is the Win service running under the same user account as the Winforms application?

Igor Brejc
It's running as LocalSystem.
Radoslav Hristov
I just tried it as User and the result is pretty much the same.
Radoslav Hristov
+1  A: 

It's resolved now. I had to re-arrange the threads. Now the COM object and all the calls to it are on the same thread and there are no inter-threading problems. Still the question how to deal with it in the common case remains unclear.

Radoslav Hristov
A: 

Hi mr. Rad, can you tell me please how did you solve this error?

Thanks in advance!

Carlos Lopez

Carlos Lopez
I've modified my program so that all calls to the COM object are in the same thread where the instance of the object was created. It's the cross thread calls that were causing trouble. I'm sure that there might be a better, more elegant way of doing it but that's what I did. Just make sure that all the calls/uses of the com object are in the same thread and you will have no trouble.
Radoslav Hristov