如何告诉我的.NET程序集COM对象的详细信息存储在注册表中?

Tar*_*aal 4 .net c# com

我遇到了这个问题中描述的问题: 无法转换类型异常的COM对象 ,表现为错误:

无法将"System .__ ComObject"类型的COM对象强制转换为接口类型"IMyInterface".此操作失败,因为对于具有IID"{GUID}"的接口的COM组件的QueryInterface调用由于以下错误而失败:不支持此类接口

我的WPF应用程序正在调用最终调用COM对象的.NET库.这工作正常,但它在主线程上运行并阻止UI.当我生成一个新线程并从中调用库时,我收到了这个错误.在另一个问题上,这些解决方案都没有对我有用.我试图了解运行时如何加载类型信息,但不能在线程之间共享它.

我知道WPF应用程序是STA,我知道这意味着在线程之间移动的任何对象都将被COM编组.我不明白类型信息如何说"这是一个COM对象,它的GUID就是这个"可以在AppDomain中,但是第二个线程无法访问.

加载类型信息在哪里?它在AppDomain中,还是每个线程?无论如何,我如何让线程共享类型信息?我怎样才能解决这个问题?

我读过这个:
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
和一些其他讨论COM互操作的东西,但没有帮我修复它.

根据Hans Passant的回答,我在第二个STA线程中创建了我的库对象:

        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();
Run Code Online (Sandbox Code Playgroud)

我知道我可能需要调用Application.Run()来启动消息队列,如果事件无法触发但是我没有那么远:它一旦尝试创建COM对象就会崩溃.AeroServerWrapper位于一个单独的DLL中,该DLL调用第二个DLL,最终尝试实例化COM对象.

任何提示或文章将不胜感激.我想解决这个问题:我的计划B是将库包装在控制台应用程序中,从UI生成控制台应用程序,并通过命名管道从中获取状态消息.那会起作用,但看起来很难看.

Han*_*ant 6

这是错误的,因为您从另一个线程调用COM接口方法.COM确保以线程安全的方式调用声明自身不是线程安全的COM类.注册表中的ThreadModel键指定了这一点,一个非常常见的值是"Apartment"(或缺失),表明该类不是线程安全的.

因此,如果从另一个线程调用接口方法,则COM将介入并对调用创建该对象的线程进行编组,从而确保线程安全.一个非常好的功能,完全没有.NET类.但是,COM在调整接口方法参数时需要帮助.必需,因为调用是在另一个线程上进行的,并且需要复制方法调用参数值.反射不是COM功能.它做的第一件事是查看注册表,HKCR\Interface\{guid}ProxyStubClsid键的键,一个知道如何序列化参数的助手类的guid.

很明显,您的机器上缺少钥匙.接下来要做的就是向COM对象询问IMarshal接口.显然,您的COM服务器没有实现它.这会产生E_NOINTERFACE错误.产生良好的错误消息从来就不是COM的强项.

那么,写作就在墙上.您正在使用的COM类不是线程安全的,并且缺少使其称为线程安全所需的所有管道.通常很容易提供代理/存根,但组件的作者并没有打扰,并非完全不常见.并不是说如果他这样做会有所帮助,那个管道确保方法在UI线程上运行,当然首先是你想要避免的.

您可以做的唯一合理的事情是创建自己的线程,调用其SetApartmentState()方法切换到STA并在该线程上创建对象,以便以线程安全的方式使用该类.没有并发性,但至少它与其余代码并发.该线程通常还必须泵送一个消息循环,Application.Run(),你可能会侥幸逃脱.当你看到死锁或事件没有被解雇时,你会知道你需要抽水.您可以在这篇文章中找到示例代码.