Error when accessing a COM dll with Threading Model Apartment from Multithread Apartment

1

I have to communicate with a third party application and the only way to do so is by accessing the provided COM component. Because the interaction takes about 3 minutes it's mandatory that it takes place in the background. So what i tried to do is to add a reference the component with option "embedd interop-types" = true and to create a test that reads very basic data through the interface. The documented way to do so is by following Code:

System sys = new System();
if(Convert.ToBoolean(sys.Initialize()) && Convert.ToBoolean(sys.Login("John Smith", out userInstance)))
Project proj = new Project();
if (Convert.ToBoolean(proj.Open(sys, m_projName, m_scenarioName)))
    someValue = proj.Name;

this works perfectly until the BackgroundWorker is used. Then I get following error in the first line of code:

Unable to cast COM object of type 'System.__ComObject' to interface type 'ICAPILib.System'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{1F5EB3E2-35F6-11D2-A191-0060083A260B}' failed due to the following error: Error loading type library/DLL. (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY)).

I already tried ReRegistering the Component without any success.

When using the BackgroundWorker the Thread Apartment Type obviously is MTA. The COM component has ThreadingModel set to apartment. If I understood this article http://msdn.microsoft.com/en-us/library/eaw10et3.aspx correctly the interop marshalling should take care of accessing the Objects.

Does anybody have a clue what I could try to make this work?

c#
multithreading
visual-studio-2010
com
com-interop
asked on Stack Overflow May 16, 2012 by Robert

2 Answers

3

You cannot use BackgroundWorker, its thread is of the wrong type. Which can't be changed, it uses a threadpool thread and that is always MTA. COM automatically creates an STA thread to give the COM server a hospitable home and that is going to cause any calls to get marshaled. Which can't work for that component, it doesn't properly register its type library. Something you want to avoid anyway.

You must create your own Thread instead and call its SetApartmentState() method to switch it to to STA before you start it. It is also important that you create the instance of the COM object on that thread, otherwise the CLR will still try to marshal the calls. Technically you need to pump a message loop (Application.Run) but you might get away with not needing to do so. You'll find out, if a call deadlocks or an expected event doesn't fire then the message loop is required.

answered on Stack Overflow May 16, 2012 by Hans Passant
1

What has happenned is that the COM Marshaller was unable to marshal the object.

First answer: Standard marshalling requires a type library. It may be that the object's type library is not correctly registered, hence the error. Are you on x86 or x64? Try registering the library with REGTLB.

Second answer: If that doesn't work, the easy answer is to use a thread of STA apartment type. This may mean you cannot use a BackgroundWorker but may have to use a specially created thread which you destroy when completed. If we are talking about a three minute operation, the additional overhead is negligible.

Note the object must be created on the thread from which it is to be used, and the apartment type msut be compatible with the object's threading model, to avoid marshalling.

answered on Stack Overflow May 16, 2012 by Ben

User contributions licensed under CC BY-SA 3.0