views:

456

answers:

3

I had a problem discovered on another thread here, I need to access a COM component that is STA. I'll run it on a dual-core computer, a process using this component only reaches 50% of CPU. Unfortunately, the owners said they can't change the component to MTA, because the component is a hybrid system compiled at Matlab, which core is C.

So I tried to load two instances of the COM class on the same process, different threads accessing it, but I couldn't, only the last COM instance becomes usable. Do you know anything that could solve this problem?

I am considering running two processes of my service on the same computer, to achieve 100% cpu. This is not a good solution, mainly because this servers will be installed outside our infra.

A: 

Try registering a second class using the same DLL. Consider that you may actually need a separate copy of the DLL with a different name in order to be completely safe.

Just remember that the STA COM class (and perhaps its DLL) is not considered thread safe for multi-threading and there is nothing you can do about that external to the COM class.

orcmid
There's nothing inherently wrong with using STA components in multi-threaded programs. COM will properly serialize the calls to the STA object from different threads (provided the interface pointers were marshalled properly between the threads).
Franci Penov
Having a second copy of the DLL will not help at all. Calling CoCreateinstanceEx (which the new operator in C# will do behind the scenes) with different CLSID will fail, as the object class factory will fail. Calling it with the original CLSID will load the original DLL.
Franci Penov
He wants to not have to do the serialized calls so that he can get free-threading with two of them. I understand he needs to make a new CLSID for the second one. Can't he do that without having to munge the second COM DLL code?
orcmid
You can't change the CLSID for a COM component without changing the DllGetClassObject implementation. Most default implementations of DllGetClassObject compare the CLSID for CoCIEx() agaist a list of known CLSIDs in the DLL in order to return the proper class factory.
Franci Penov
Yes, I see that in DllGetClassObject. Another way to try this would be to build a DLL for a different CLSID and have its DllGetObject LoadLibrary the other DLL (Not CoGetObject) and call that DllGetClassObject entry with the correct CLSID. Neither perfect nor wonderful. Worth trying in a pinch.?
orcmid
+1  A: 

On the topic of multiple STA components

It is possible to have two instances of the same STA COM component and access them from C#. The only thing that could prevent you from such scenario is the object itself if implemented as a singleton object.

However, if both instances are on the same STA thread, an active call in one of the instances will block any other calls into that thread. Thus, if you want those two instances to work in parallel, you need to have them be on separate STA threads. Just to be on the safe side, I'd create both instances on background threads. That should prevent your UI from locking.

On the topic of STA vs. MTA for the external component

I am not sure why the component being in C would prevent it from being an MTA object. Being MTA just means the object needs to internally synchronize it's state access and management code between multiple threads.

WARNING: Ugly hack! :-) If you want to experiment a bit, you could go to the registry and change the external component threading model from Apartment to Free, just to verify that your code would work properly with an MTA. Their component will probably break, though, as they probably did not write thread-safe code, relying on COM to guard them.

Make a note on a prominent place to revert that change later, so that you don't end up with a system where their code doesn't work and spent countless hours chasing ghosts. :-)

Franci Penov
In fact I'll have around 500 processes for each 30 minutes, each one opening a thread, two threads at time.
Victor Rodrigues
I understood what you said, but how could I share 2 COM instances to all those threads if I create those instances for their background threads? I need to initialize them and put on somewhere visible globally, not on their background threads.
Victor Rodrigues
Different process will not be serialized between themselves. But your app doesn't need to have more than two threads, as any calls to a component instance in the same process will be serialized, thus you are bound to the number of instances you have.
Franci Penov
Once you've created the instance on the background thread, you can access it from any thread. COM and .Net interop will take care of switching the thread context of the call. However, you need to keep your background thread alive and run a message loop in it (STA works with messages).
Franci Penov
A: 

Franci Pernov,

I've tried work with two threads, and initialize the com instances on the context of each thread, but the error is the same: (Exception from HRESULT: 0x80004005 (E_FAIL))

I am storing and retrieving the instance through CallContext GetData and SetData.

Victor Rodrigues