How do I tell my .NET assembly where the details of a COM object are stored in the registry?

I had the problem described in this question: It is not possible to throw an exception to a COM object of the type that appears as an error:

Unable to pass a COM object of type "System .__ ComObject" to the interface, enter "IMyInterface". This operation failed because the QueryInterface call of the COM component for the interface with the IID error '{GUID}' due to the error: There is no support for such an interface

My WPF application calls the .NET library, which ultimately calls the COM object. This works great, but works on the main thread and blocks the user interface. When I create a new thread and call the library from it, I get this error. None of these solutions on another issue work for me. I am trying to understand how the runtime can load type information, but not share it between threads.

I understand that WPF applications are STAs, and I know that this means that any objects moving between threads will be combined by COM. I don’t understand how type information says "This is a COM object, and its GUID is this" may be in AppDomain, but not available for the second stream.

Where is the type information loaded? Is it in the AppDomain or in the stream? In any case, how can I relate thread type information? How can i fix this?

I read this:
http://www.codeproject.com/Articles/9190/Understanding-The-COM-Single-Threaded-Apartment-Pa
and this:
http://msdn.microsoft.com/en-us/library/ms973913.aspx#rfacomwalk_topic10
and a bunch of other things that talk about COM interoperability but didn't help me fix this.

As a Hans Passant answer, I create my library object in a second STA thread:

var thread = new Thread(delegate() { var aeroServer = new AeroServerWrapper(Config.ConnectionString); var ct = new CancellationToken(); aeroServer._server.MessageReceived += ServerMessageReceived; aeroServer.Go(@"M:\IT\Public\TestData\file.dat", ct); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); 

I understand that I may need to call Application.Run () to start the message queue if events do not fire, but I'm not so far away: it crashes as soon as it tries to create a COM object. AeroServerWrapper is located in a separate DLL, which calls the second DLL, which finally tries to create an instance of the COM object.

Any tips or articles would be greatly appreciated. I would like to solve this problem because it is: my plan B is to wrap the library in a console application, start the console application from the user interface and receive status messages from it via the named pipe. It will work, but it seems ugly.

+4
source share
2 answers

This is not the case because you are calling the COM interface method from another thread. COM ensures that COM classes that declare themselves unsafe are invoked in a thread-safe manner. This ThreadModel key in the registry indicates that the very common value is β€œFlat” (or missing), which indicates that the class is not thread safe.

So, if you call the interface method from another thread, COM logs in and march the call to the thread on which the object was created, thereby ensuring the safety of the thread. A pretty nice feature that is completely missing from .NET classes. However, COM needs help when it marshals the arguments to an interface method. Required because the call is being made in a different thread, and the values ​​of the arguments to the method call must be copied. Reflection is not a COM function. The first thing he does is look in the registry, the HKCR\Interface\{guid} key for the ProxyStubClsid key, the guid from a helper class that knows how to serialize the arguments.

Obviously, this key is missing on your computer. The next thing he does is ask the COM object for the IMarshal interface. Obviously, your COM server does not implement it. And that causes an E_NOINTERFACE error. Getting good error messages has never been a force of COM.

Well, the letter is on the wall. The COM class that you use is not thread safe and skips all the plumbing necessary to make it thread safe. It is usually fairly easy to provide a proxy server, but the author of the component was not worried, and not entirely rare. Not that it would help if he did, this plumbing ensures that the method works in the user interface thread, of course, what you tried to avoid in the first place.

The only sensible thing you can do is create your own thread, call its SetApartmentState () method to switch to STA and create an object in that thread so that the class is used in a thread-safe manner. There is no concurrency, but at least it matches the rest of your code. This thread, as a rule, also pumps up the Application.Run () message pipeline, you can get away from pumping. You will find out what you need to download when you see a dead end or events do not fire. You can find sample code in this post .

+6
source

The .NET interface type IMyInterface, which is a wrapper for the real type of COM interface, loads in order - your problem is not that it somehow does not β€œsee” in another thread. The actual type of object you are trying to execute is, as you see, System .__ ComObject, which in any case does not implement this interface. It is not processed like any other .NET object during type checking and type checking (this is / like operators). What happens is that the .NET runtime queries the COM object for the interface that you are trying to use by calling QueryInterface . The call failed because the COM object is marshaling incorrectly (or else: the end result is that it fails). It manifests itself as an InvalidCastException because it is the closest in the world of .NET to a QueryInterface that returns E_NOINTERFACE in the COM world.

+1
source

All Articles