Using COM Object in C++ dll - c#

I am writing a Win32 C++ DLL that uses the COM object(B.dll) which is made in C#.
This dll(A.dll) provides CMyComObject class which creates a COM object and access to it.
Here is my code.
void CMyComObject::CMyComObject()
{
HRESULT result = CoInitialize(NULL);
...
result = CoCreateInstance(CLSID_COMDLL, NULL, CLSCTX_INPROC_SERVER, IID_COMDLL, reinterpret_cast<void**>(&MyComObject));
}
void CMyComObject::~CMyComObject()
{
..
CoUninitialize();
..
}
And then, here is a client program that loads A.dll and access to the COM object.
This program creates several threads which load A.dll and create a COM object concurrently.
In this case, Is this correct to use CoInitialize() function or Should I use CoInitializeEx() function with COINIT_MULTITHREADED parameter?
Or Is there any mistake I did?
(I registered B.dll by commanding "reg_asm.exe B.dll B.tlb /codebase")
Sorry for my poor English.
Thanks.

You are supposed to use CoInitialize[Ex]/CoUninitialize before and after any COM activity on that thread, and your choice between CoInitialize and CoInitializeEx with specific parameters depends on whether you prefer STA or MTA mode for your thread.
Having said that, your initialization:
Does not depend on whether the COM object itself creates any threads
Does not depend on other parts of your application possibly having other COM activity, including similar instantiation of the same COM class
Entirely depends on your COM activity on the thread in question
Does not normally happen in class constructor; it is typical to have COM initialization associated with top level thread code such as before windows message pump or at the very beginning of the thread procedure; putting it into constructor is an easy way to get into collision e.g. with another initialization (esp. using different apartment model) or too early uninitialization.
Summing it all once again, your initialization:
looks good if you are OK with COM single thread apartment model and you don't pass obtained pointer between threads
you would be better off moving CoInitialize and CoUninitialize calls out of constructor and associate it with thread code
be sure to check returned value to detect failures, esp. attempt to initialize mismatching apartment on the thread already having COM initialization
the part you missing is that you have to close all your COM activity before CoUninitialize call, including releasing your MyComObject pointer.

Related

Keeping a COM object alive to queue longrunning tasks

From what I understand, COM objects can only be used in the thread they were instantiated in. If that thread dies, the object becomes invalid and you can't use it anymore. The error I face when I attempt to is COM object that has been separated from its underlying RCW cannot be used. The advice I've typically seen to deal with this is just to reinitialize a new COM object for every task you want to run.
The problem is that the COM object I'm using handles communication with another program, and re-establishing the connection with the same instance of that program with a new COM object is tricky. The operations I want to run are long winded and require feedback from the user in between, so including all the operations in a single Task is not really feasible afaik.
What I want to do is build a wrapper around that COM object that puts calls to it into a queue to be invoked by the appropriate thread, that would be compatible with async/await, and I would like to know if that's possible. But to avoid the XY problem I'll ask something different:
What is the most elegant way to keep a COM object functioning, off the main thread, so that I can continue to use it for multiple things?
COM object that has been separated from its underlying RCW cannot be used.
To keep the COM object alive you need to declare a corresponding RCW at a global scope, so the reference counter is not declared when the RCW is swiped from the heap. Also you need to pay attention to any Marshal.ReleaseComObject calls that may lead to the issue you faced with. This method is used to explicitly control the lifetime of a COM object used from managed code. You should use this method to free the underlying COM object that holds references to resources in a timely manner or when objects must be freed in a specific order.
For example, if you deal with Office applications like Outlook, it may detect cross-thread calls and throw exceptions in such cases.
The best solution is create a scheduler which can be called from secondary threads and queue such calls on the main thread to communicate with a COM server. Or just consider extracting all the required information (scalar value that don't involve COM objects) which can be simply consumed from secondary threads and then process it in the way you need.

An MTA Console application calling an STA COM object from multiple threads

Although there are many questions about COM and STA/MTA (e.g. here), most of them talk about applications which have a UI. I, however, have the following setup:
A console application, which is by default Multi-Threaded Apartment (Main() explicitly has the [MTAThread] attribute).
The main thread spawns some worker threads.
The main thread instantiates a single-threaded COM object.
The main thread calls Console.ReadLine() until the user hits 'q', after which the application terminates.
A few questions:
Numerous places mentions the need of a message pump for COM objects. Do I need to manually create a message-pump for the main thread, or will the CLR create it for me on a new STA thread, as this question suggests?
Just to make sure - assuming the CLR automagically creates the necessary plumbing, can I then use the COM object from any worker thread without the need of explicit synchronization?
Which of the following is better in terms of performance:
Let the CLR take care of the marshaling to and from the COM object.
Explicitly instantiate the object on a separate STA thread, and have other thread communicate with it via e.g. a ConcurrentQueue.
This is done automagically by COM. Since your COM object is single-threaded, COM requires a suitable home for the object to ensures it is used in a thread-safe way. Since your main thread is not friendly enough to provide such guarantees, COM automatically creates another thread and creates the object on that thread. This thread also automatically pumps, nothing you have to do to help. You can see it being created in the debugger. Enable unmanaged debugging and look in the Debug + Windows + Threads window. You'll see the thread getting added when you step over the new call.
Nice and easy, but it does have a few consequences. First off, the COM component needs to provide a proxy/stub implementation. Helper code that knows how to serialize the arguments of a method call so the real method call can be made on another thread. That's usually provided, but not always. You'll get a hard to diagnose E_NOINTERFACE exception if it is missing. Sometimes TYPE_E_LIBNOTREGISTERED, a common install problem.
And most significantly, every call on the COM component will be marshaled. That's slow, a marshaled call is usually around 10,000x slower than a direct call on a method that itself takes very little time. Like a property getter call. That can really bog your program down of course.
An STA thread avoids this and is therefore the recommended way to use a single-threaded component. And yes, it is a requirement for an STA thread to pump a message loop. Application.Run() in a .NET program. It is the message loop that marshals calls from one thread to another in COM. Do note that it doesn't necessarily mean that you must have a message loop. If no call ever needs to marshaled, or in other words, if you make all the calls on the component from the same thread, then the message loop isn't needed. That's typically easy to guarantee, particularly in a console mode app. Not if you create threads yourself of course.
One more nasty detail: a single-threaded COM component sometimes assumes it is created on a thread that pumps. And will use PostMessage() itself, typically when it uses worker threads internally and needs to raise events on the STA thread. That will of course not work correctly anymore when you don't pump. You normally diagnose this by noticing that events are not being raised. The common example of such a component is WebBrowser. Which heavily uses threads internally but raises events on the thread on which it was created. You'll never get the DocumentCompleted event if you don't pump.
So putting [STAThread] on your Main() method might be enough to get happy fast code, even without a call to Application.Run(). Just keep the consequences in mind, seeing a method call deadlock or an event not getting raised is the tell-tale sign that pumping is required.
Yes, it is possible to create a STA COM object from an MTA thread.
In this case, COM (not CLR) will create an implicit STA apartment (a separate COM-owned thread) or re-use the existing one, created ealier. The COM object will be instantiated there, then a thread-safe proxy object (COM marshalling wrapper) will be created for it and returned to the MTA thread. All calls to the object made on the MTA thread will be marshalled by COM to that implicit STA apartment.
This scenario is usually undesirable. It has a lot of shortcomings and may simply not work as expected, if COM is unable to marshal some interfaces of the object. Check this question for more details. Besides, the message pump loop, run by the implicit STA apartment, pumps only a limited number of COM-specific messages. That may also affect the functionality of the COM.
You may try it and it may work well for you. Or, you may run into some unpleasant issues like deadlocks, quite difficult to diagnose.
Here is a closely related question I just recently answered:
StaTaskScheduler and STA thread message pumping
I'd personally prefer to manually control the logic of the inter-thread calls and thread affinity, with something like ThreadAffinityTaskScheduler proposed in my answer.
You may also want to read this: INFO: Descriptions and Workings of OLE Threading Models, highly recommended.
Do I need to manually create a message-pump for the main thread,
No. It is in the MTA therefore no message pump is needed.
or will the CLR create it for me on a new STA thread
If COM creates the thread (because there is no STA in the process) then it also creates the message pump (and a hidden window: can be seen with the SPY++ and similar debugging tools).
COM object from any worker thread without the need of explicit synchronization
Depends.
If the reference to the single threaded object (STO) was created in the MTA then COM will supply the appropriate proxy. This proxy is good for all threads in the MTA.
In any other case the reference will need to be marshalled to ensure it has the correct proxy.
is better in terms of performance
The only answer to this is to test both and compare.
(Remember if you create the thread for the STA and then instantiate the object locally you need to do the message pumping. It is not clear to me that there is any CLR level lightweight message pump—including WinForms just for this certainly isn't.)
NB. The only in depth explanatory coverage of COM and the CLR is .NET and COM: The Complete Interoperability Guide by Adam Nathan (Sams, January 2002). But it is based on .NET 1.1 and now out of print (but there is a Kindle edition and is available via Safari Books Online). Even this book doesn't describe directly what you are trying to do. I would suggest some prototyping.

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

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?
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.
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.

Cannot call COM object created from STAThread from oher STA threads

I am new to COM and trying to understand the difference between STA and MTA. I tried to create an example that would show that COM can manage calls to object created in STA that is not thread-safe.
MyCalcServer class here is created using ATL Simple Object. The settings used are the same as in this article:
Threading Model: Apartment
Aggregation: No
Interface: Custom
MyCalcServer COM object is used in another C# project which is:
class Program
{
[STAThread]
static void Main(string[] args)
{
MyCOMLib.MyCalcServer instance = new MyCOMLib.MyCalcServer();
string output1;
instance.ChangeValue("Gant", out output1);
Console.WriteLine(output1);
Thread t1 = new Thread(() =>
{
while (true)
{
string output;
instance.ChangeValue("Gant", out output);
Console.WriteLine(output);
}
});
t1.SetApartmentState(ApartmentState.STA);
t1.Start();
// :
// also has t2 and t3 here with similar code
// :
t1.Join(); t2.Join(); t3.Join();
}
}
However, this always results in InvalidCastException (E_NOINTERFACE) raised inside t1's code. I have also tried changing ApartmentState to MTA with no success.
Unable to cast COM object of type
'MyCOMLib.MyCalcServerClass' to
interface type
'MyCOMLib.IMyCalcServer'. This
operation failed because the
QueryInterface call on the COM
component for the interface with IID
'{B005DB8C-7B21-4898-9DEC-CBEBE175BB21}'
failed due to the following error: No
such interface supported (Exception
from HRESULT: 0x80004002
(E_NOINTERFACE)).
Could anybody please explain what I am doing wrong here?
You explicitly ask COM to create instance for main thread, then you pass this to another thread. Of course in some circumstance it is allowed (for example declare MyCalcServer as multithread).
But in your case it looks you need create proxy for another thread. In regular COM clients it is done by CoMarshalInterThreadInterfaceInStream. There is large article to clarify it http://www.codeproject.com/KB/COM/cominterop.aspx
I managed to get this resolve.
As I'm new to COM, I don't know much about Proxy/Stub and that they're needed for marshaling stuffs between STA and STA. After created a new ATL project and make sure I have "Merge Proxy/Stub" ticked. The problem vanished.
I find the info from this page useful: Why would I want to merge Proxy/Stub code with my DLL project.
Proxy/stubs providing standard
marshaling for your component. In many
cases a DLL-based component may not
need proxy/stub because it is running
in the same context of its client, and
this option may seem useless at first.
However, COM uses the marshaling
process to synchronize access to a
component in multi-threaded
situations. So, a DLL-based component
will need a proxy/stub DLL in at least
two cases:
It's running a multi-threaded client and needs to pass interface
pointer between apartments (STA to STA
or MTA to STA).
DCOM can provide a surrogate process for a DLL-based component so
that it can be accessed in a
distributed environment. In this case
a proxy/stub is needed to marshal
between machines.
By merging the proxy/stub code with
your implementation, you don't have to
distribute two DLLs, just the one.
I will mark #Dewfy's answer as accept as he has shed some light on the Proxy topic.

STAThread and multithreading

From the MSDN article on STAThread:
Indicates that the COM threading model for an application is single-threaded apartment (STA).
(For reference, that's the entire article.)
Single-threaded apartment... OK, that went over my head. Also, I read somewhere that unless your application uses COM interop, this attribute actually does nothing at all. So what exactly does it do, and how does it affect multithreaded applications? Should multithreaded applications (which includes anything from anyone using Timers to asynchronous method calls, not just threadpools and the like) use MTAThread, even if it's 'just to be safe'? What does STAThread and MTAThread actually do?
Apartment threading is a COM concept; if you're not using COM, and none of the APIs you call use COM "under the covers", then you don't need to worry about apartments.
If you do need to be aware of apartments, then the details can get a little complicated; a probably-oversimplified version is that COM objects tagged as STA must be run on an STAThread, and COM objects marked MTA must be run on an MTA thread. Using these rules, COM can optimize calls between these different objects, avoiding marshaling where it isn't necessary.
What that does it it ensures that CoInitialize is called specifying COINIT_APARTMENTTHREADED as the parameter. If you do not use any COM components or ActiveX controls it will have no effect on you at all. If you do then it's kind of crucial.
Controls that are apartment threaded are effectively single threaded, calls made to them can only be processed in the apartment that they were created in.
Some more detail from MSDN:
Objects created in a single-threaded
apartment (STA) receive method calls
only from their apartment's thread, so
calls are serialized and arrive only
at message-queue boundaries (when the
Win32 function PeekMessage or
SendMessage is called).
Objects created on a COM thread in a
multithread apartment (MTA) must be
able to receive method calls from
other threads at any time. You would
typically implement some form of
concurrency control in a multithreaded
object's code using Win32
synchronization primitives such as
critical sections, semaphores, or
mutexes to help protect the object's
data.
When an object that is configured to
run in the neutral threaded apartment
(NTA) is called by a thread that is in
either an STA or the MTA, that thread
transfers to the NTA. If this thread
subsequently calls CoInitializeEx, the
call fails and returns
RPC_E_CHANGED_MODE.
STAThread is written before the Main function of a C# GUI Project. It does nothing but allows the program to create a single thread.

Categories