Cannot call COM object created from STAThread from

2019-03-27 15:13发布

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?

2条回答
疯言疯语
2楼-- · 2019-03-27 16:04

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.

查看更多
欢心
3楼-- · 2019-03-27 16:09

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

查看更多
登录 后发表回答