简体   繁体   English

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

[英]How do I tell my .NET assembly where a COM object's details are stored in the registry?

I'm having the problem described in this question: Unable to cast COM object of type exception which manifests as the error: 我遇到了这个问题中描述的问题: 无法转换类型异常的COM对象 ,表现为错误:

Unable to cast COM object of type 'System.__ComObject' to interface type 'IMyInterface'. 无法将“System .__ ComObject”类型的COM对象强制转换为接口类型“IMyInterface”。 This operation failed because the QueryInterface call on the COM component for the interface with IID '{GUID}' failed due to the follow error: No such interface supported 此操作失败,因为对于具有IID“{GUID}”的接口的COM组件的QueryInterface调用由于以下错误而失败:不支持此类接口

My WPF app is calling a .NET library that eventually calls a COM object. 我的WPF应用程序正在调用最终调用COM对象的.NET库。 This works fine, but it runs on the main thread and blocks the UI. 这工作正常,但它在主线程上运行并阻止UI。 When I spawn a new thread and call the library from it, I'm getting that error. 当我生成一个新线程并从中调用库时,我收到了这个错误。 None of those solutions on the other question are working for me. 在另一个问题上,这些解决方案都没有对我有用。 I'm trying to understand how the runtime can have the type information loaded, but not share it between threads. 我试图了解运行时如何加载类型信息,但不能在线程之间共享它。

I understand that WPF apps are STA, and I know that means that any objects moving between threads will be COM marshalled. 我知道WPF应用程序是STA,我知道这意味着在线程之间移动的任何对象都将被COM编组。 I don't understand how the type information to say "This is a COM object, and its GUID is this" can be in the AppDomain, but inaccessible to the second thread. 我不明白类型信息如何说“这是一个COM对象,它的GUID就是这个”可以在AppDomain中,但是第二个线程无法访问。

Where does loaded type information live? 加载类型信息在哪里? Is it in the AppDomain, or per thread? 它在AppDomain中,还是每个线程? In any case, how can I make the threads share type information? 无论如何,我如何让线程共享类型信息? How can I fix this? 我怎样才能解决这个问题?

I've read this: 我读过这个:
http://www.codeproject.com/Articles/9190/Understanding-The-COM-Single-Threaded-Apartment-Pa 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 http://msdn.microsoft.com/en-us/library/ms973913.aspx#rfacomwalk_topic10
and a bunch of other stuff that talks about COM interop, but hasn't helped me fix it. 和其他一些谈论COM互操作的东西,但没有帮我修复它。

As per Hans Passant's answer, I'm creating my library object inside the second STA thread: 根据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();

I understand that I might need to call Application.Run() to start the message queue if events fail to fire but I'm not getting that far: it crashes as soon as it attempts to create the COM object. 我知道我可能需要调用Application.Run()来启动消息队列,如果事件无法触发但是我没有那么远:它一旦尝试创建COM对象就会崩溃。 The AeroServerWrapper is in a separate DLL, which calls a second DLL, which finally attempts to instantiate the COM object. AeroServerWrapper位于一个单独的DLL中,该DLL调用第二个DLL,最终尝试实例化COM对象。

Any hints or articles would be greatly appreciated. 任何提示或文章将不胜感激。 I'd like to solve this as it is: my Plan B is to wrap the library in a console app, spawn the console app from the UI, and get status messages from it through a named pipe. 我想解决这个问题:我的计划B是将库包装在控制台应用程序中,从UI生成控制台应用程序,并通过命名管道从中获取状态消息。 That'll work, but it seems ugly. 那会起作用,但看起来很难看。

This goes wrong because you are calling the COM interface method from another thread. 这是错误的,因为您从另一个线程调用COM接口方法。 COM ensures that COM classes that declare themselves to be not thread-safe are called in a thread-safe way. COM确保以线程安全的方式调用声明自身不是线程安全的COM类。 The ThreadModel key in the registry specifies this, a very common value is "Apartment" (or is missing), indicating that the class is not thread-safe. 注册表中的ThreadModel键指定了这一点,一个非常常见的值是“Apartment”(或缺失),表明该类不是线程安全的。

So if you call the interface method from another thread then COM steps in and marshals the call to the thread on which the object was created, thus ensuring thread-safety. 因此,如果从另一个线程调用接口方法,则COM将介入并对调用创建该对象的线程进行编组,从而确保线程安全。 A pretty nice feature, entirely missing from .NET classes. 一个非常好的功能,完全没有.NET类。 However, COM needs help when it marshals the interface method arguments. 但是,COM在调整接口方法参数时需要帮助。 Required because the call is made on another thread and the method call argument values need to be copied. 必需,因为调用是在另一个线程上进行的,并且需要复制方法调用参数值。 Reflection is not a COM feature. 反射不是COM功能。 The first thing it does is look in the registry, the HKCR\\Interface\\{guid} key for the ProxyStubClsid key, the guid of a helper class that knows how to serialize the arguments. 它做的第一件事是查看注册表,CodeStubClsid键的HKCR\\Interface\\{guid}键,知道如何序列化参数的助手类的guid。

Clearly that key is missing on your machine. 很明显,您的机器上缺少钥匙。 Next thing it does is ask the COM object for the IMarshal interface. 接下来要做的就是向COM对象询问IMarshal接口。 Clearly your COM server doesn't implement it. 显然,您的COM服务器没有实现它。 And that produces the E_NOINTERFACE error. 这会产生E_NOINTERFACE错误。 Producing good error messages has never been COM's strength. 产生良好的错误消息从来就不是COM的强项。

Well, the writing is on the wall. 那么,写作就在墙上。 The COM class you are using is not thread-safe and is missing all the plumbing required to make calling it thread-safe. 您正在使用的COM类不是线程安全的,并且缺少使其称为线程安全所需的所有管道。 It is normally pretty easy to provide the proxy/stub but the author of the component didn't bother, not entirely uncommon. 通常很容易提供代理/存根,但组件的作者并没有打扰,并非完全不常见。 Not that it would help if he did, that plumbing ensures the method runs on the UI thread, surely what you were trying to avoid in the first place. 并不是说如果他这样做会有所帮助,那个管道确保方法在UI线程上运行,当然首先是你想要避免的。

The only reasonable thing you can do is create your own thread, call its SetApartmentState() method to switch to STA and create the object on that thread so the class is used in a thread-safe manner. 您可以做的唯一合理的事情是创建自己的线程,调用其SetApartmentState()方法切换到STA并在该线程上创建对象,以便以线程安全的方式使用该类。 No concurrency, but at least it is concurrent with the rest of your code. 没有并发性,但至少它与其余代码并发。 That thread must typically also pump a message loop, Application.Run(), you might get away with not pumping. 该线程通常还必须泵送一个消息循环,Application.Run(),你可能会侥幸逃脱。 You'll know you need to pump when you see deadlock or events don't get fired. 当你看到死锁或事件没有被解雇时,你会知道你需要抽水。 You can find example code in this post . 您可以在这篇文章中找到示例代码。

The .NET interface type IMyInterface which is a wrapper for the actual COM interface type is loaded alright - your problem is not that it is somehow not "visible" in the other thread. .NET接口类型IMyInterface是实际COM接口类型的包装器,可以正常加载 - 你的问题不是它在另一个线程中不是“可见”的。 The actual type of the object you're trying to cast is, as you can see, System.__ComObject, which does not implement that interface anyway. 正如您所见,您尝试转换的对象的实际类型是System .__ ComObject,它无论如何都不实现该接口。 It is not treated like any other .NET object when casting and type checking (is/as operators). 在转换和类型检查时,它不像任何其他.NET对象那样对待(是/作为运算符)。 What happens is that .NET runtime asks the COM object for the interface you're trying to cast to by calling QueryInterface . 会发生什么是.NET运行时通过调用QueryInterface向COM对象询问您尝试强制转换的接口。 The call fails because the COM object does not correctly marshal (or otherwise: the end result is that it fails). 调用失败,因为COM对象没有正确编组(否则:最终结果是它失败)。 It manifests as a InvalidCastException because it is the closest thing in meaning in the .NET world to a QueryInterface returning E_NOINTERFACE in the COM world. 它表现为InvalidCastException,因为它是.NET世界中最接近于在COM世界中返回E_NOINTERFACE的QueryInterface的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在.Net中序列化COM对象? - How Do I serialize a COM object in .Net? 我如何知道.NET Core支持的“真正”是什么? 例如注册表 - How do I tell what is “really” supported on .NET Core? For example the registry 如何在 ASP.Net 中修改我的 xml 详细信息? - How do i modify my xml details in ASP.Net? 添加属性时,如何保留.NET程序集的COM二进制兼容性? - How do I preserve COM binary compatibility for a .NET Assembly when properties are added? 如何取消注册/删除在注册表中创建的所有COM条目 - How do I unregister/remove all COM entries created in registry Roslyn:如何判断编译中是否引用了特定的程序集? - Roslyn: How do I tell if a particular assembly is referenced in a Compilation? 如何判断 COM object 与哪个线程相关联? (STA) - How can I tell which thread a COM object is associated with? (STA) 如何使用通过 a.Net PowerShell object 运行的 PowerShell 脚本获取 windows 注册表属性值? - How do I get windows registry property values using a PowerShell script run through a .Net PowerShell object? 我如何参考安装.net程序的目录? - How do I refer to the directory where my .net program is installed? .NET:如何判断编码是否支持字符串中的所有字符? - .NET: How do I tell if an encoding supports all the chars in my string?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM