Cannot call COM object created from STAThread from oher STA threads - c#

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.

Related

How to deal with COM QueryInterface failure in class library

I have a problem in a utility library, which does some COM interop. It keeps references to COM objects which are used between calls.
If all methods are called from threads using the same COM threading model, the class works fine.
But if the calls that create COM objects use a different threading model than used for subsequent calls, QueryInterface fails with E_NOINTERFACE.
We only found this when we added async branches to our unit tests; prior to this it was running fine in all-MTA apps all-STA unit tests...
I think I understand the reason for the failure (via COM docs, Chris Brumme's blog) - the COM objects being used support "both" threading models which causes C# to create a fence between the STA and MTA-created instances.
However from the library's point of view, the only fixes I can think of are a bit rubbish:
Make it an unwritten rule that this library is for MTA threads only
Change the library to detect calls from STA threads and fail (using e.g. CurrentThread.ApartmentState)
Change the library to create its own MTA threads for all COM interop (or perhaps just when incoming calls are on an STA thread)
Are there any cleaner/easier options? Here's a MCVE:
class Program
{
[ComImport, Guid("62BE5D10-60EB-11d0-BD3B-00A0C911CE86")] class SystemDeviceEnum { };
[ComVisible(true), ComImport, Guid("29840822-5B84-11D0-BD3B-00A0C911CE86"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface ICreateDevEnum { [PreserveSig] int CreateClassEnumerator([In] ref Guid pType, [Out] out IEnumMoniker ppEnumMoniker, [In] int dwFlags); }
static ICreateDevEnum createDeviceEnum;
static Guid VideoInputDeviceCategory = new Guid("860BB310-5D01-11d0-BD3B-00A0C911CE86");
static void Prepare()
{
var coSystemDeviceEnum = new SystemDeviceEnum();
createDeviceEnum = (ICreateDevEnum)coSystemDeviceEnum;
}
static int GetDeviceCount()
{
IEnumMoniker enumMoniker;
createDeviceEnum.CreateClassEnumerator(ref VideoInputDeviceCategory, out enumMoniker, 0);
if (enumMoniker == null) return 0;
int count = 0;
IMoniker[] moniker = new IMoniker[1];
while (enumMoniker.Next(1, moniker, IntPtr.Zero) == 0) count++;
return count;
}
[STAThread]
static void Main(string[] args)
{
RunTestAsync().Wait();
}
private static async Task RunTestAsync()
{
Prepare();
await Task.Delay(1);
var count = GetDeviceCount();
Console.WriteLine(string.Format("{0} video capture device(s) found", count));
}
}
COM threading is notoriously poorly understood. Actually much, much easier to get going than threading .NET classes. Just about everybody knows that, say, the List<> or Random class is not thread-safe. Not that many know how to use them in a thread-safe way. The COM designers had much loftier goals and assumed that programmers in general don't know how to write thread-safe code and that the Smart People should take care of it.
It does require taking care of a few details. First and foremost, you must tell COM what kind of support you are willing to provide to coclasses that are not thread-safe but are used from a worker thread anyway. And there you committed a horrible, horrible crime. When you use [STAThread] then you make a promise. Two things you must do: you must never block the thread and you must pump a message loop (aka Application.Run). Note how you broke both requirements. Never ever lie about it, very bad things happen when you do. But you didn't get that far yet.
The kind of threading support you can expect from the coclass you are using is easy to discover. Start up Regedit.exe and navigate to HKLM\Software\Wow6432Node\Classes\CLSID. Locate the {guid} you use and look at the ThreadingModel value you see in the InProcServer32 key. It is "Both" for the one you are using. Means that it was written to work both from an STA thread and a thread that doesn't support thread-safety at all and runs in the MTA. Like your main thread and your Task. And as you discovered, it works fine from either. Do beware that this isn't very usual, the vast majority of COM servers only support the "Apartment" threading model. Microsoft usually goes the extra thousand miles to support both.
So you created the enumerator object on an STA thread and use it on a thread in the MTA. Now the COM runtime must do something quite non-trivial, it must make sure that any callbacks (aka events) that might be invoked from the method you call run on the same STA thread so that any code in the callback is thread-safe as well. In other words, it must marshal the call from the worker thread back to your main thread. The equivalent of Control.Invoke or Dispatcher.Invoke in a .NET app. Done completely automatically in COM.
That requires doing something that's very easy in .NET but very hard in unmanaged code. The arguments of the method must be copied from one stack frame to another so the call can be made on the other thread. Easy to do in .NET thanks to Reflection. Not nearly as easy to do for unmanaged code, it requires an oracle that knows what the method parameter types are, a substitute for the missing metadata.
That oracle is found in the registry as well. Use Regedit and navigate to the HKLM\Software\Wow6432Node\Classes\Interface key. Locate the interface guid there, {29840822-5B84-11D0-BD3B-00A0C911CE86} as the exception message tells you. You'll notice the problem: it isn't there. Yes, the exception message is pretty lousy. The real E_NOINTERFACE is reported because the COM runtime can't find another way either, no support for IMarshal. If it would be there then you'd get to deal with the [STAThread] lie, your thread will deadlock.
That's unusual btw, COM object models that use a ThreadingModel of "Both" almost always also support marshaling. Just not for the specific one you are trying to use. DirectShow has been deprecated for the past 10 years and replaced by Media Foundation. You found one good reason why Microsoft decided to retire it.
So this is just something you need to know. A detail that isn't very different from having to know that the Random class isn't thread-safe. It is not well documented in MSDN, but as noted it is easy to discover by yourself.

Using COM Object in C++ dll

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.

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.

CastError when calling interop Excel functions in a thread?

I have an excel addin class that has this function:
public void testRibbon()
{
Excel.Workbook workbook = this.Application.ActiveWorkbook;
Excel.Worksheet activeSheet = workbook.Sheets[1];
Excel.Range range = activeSheet.get_Range(JOB_ID_FIELD + HEADER_ROW, TOTAL_STATUS_PERCENTAGE_KEY_FIELD + (10 + 1).ToString());
range.get_Range("C4").Value = "test Ribbon complete";
}
In the ribbon I added a button that when pressed will call testRibbon in a thread:
private void process_Click(object sender, RibbonControlEventArgs e)
{
Thread processThread = new Thread(delegate(){
Globals.ExcelAddin.testRibbon();
});
processThread.Start();
}
This causes a cast error:
Unable to cast COM object of type 'System.__ComObject' to interface
type 'Microsoft.Office.Interop.Excel._Workbook'. This operation failed
because the QueryInterface call on the COM component for the interface
with IID '{000208DA-0000-0000-C000-000000000046}' failed due to the
following error: Error loading type library/DLL. (Exception from
HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY)).
The cast error doesn't happen if I don't use a new thread.
Edit:Tried using task factory, same error:
var task2 = Task.Factory.StartNew(
() =>
{
Globals.BobStats.testRibbon();
});
Using multiple threads in Excel makes no sense because Excel's COM interface is basically single threaded (see this article). There might be further restrictions from within an add-in.
Yet I have to say I don't really understand what the error message is trying to say.
I think at the moment, you are being connected by COMObject to interface type 'Workbook'... it will automatically create a new unmanaged thread and that will crash with your thread when you Start(). To specifically, when you create thread object it will save some data structures to shared memory and did not use Start() so thread was not executing at this time
When you use an Excel interface method from a worker thread then the method call needs to be marshaled. In other words, the call needs to be made from the thread that created the Application object. Same idea as using Control.Invoke() or Dispatcher.Invoke() in a .NET gui app.
To make that work, COM needs to know what the arguments are for the method so it can copy them properly. That is the kind of info that requires the equivalent of Reflection in .NET. Which requires metadata, the metadata that describes a COM method is stored in a type library. The type library for Excel is stored in Excel.exe as a resource.
Finding the type library is what is going wrong here. This info is stored in the registry and it is damaged on your machine for some reason. The most likely key that got whacked is HKLM\SOFTWARE\Classes\TypeLib\{00020813-0000-0000-C000-000000000046}\1.7\0\win32 albeit that it depends on the exact version of Office. You can get more insight from SysInternals' ProcMon utility, you'll see your program searching for the key.
This kind of mishap is rarely limited to just one key. You'll need to get your machine healthy again and reinstall Office.
Oh, and keep in mind that the code doesn't actually run on the worker thread. That requires creating the Application object on that worker thread and calling SetApartmentState() to make it STA before you start it.

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.

Categories