这是由 另一个问题.

具体来说,我有一个在过程中,该类别在 CLSID注册表 作为一个 ThreadingModelBoth.

我们的过程通过 CoCreateInstance (不是 CoCreateInstanceEx, ,如果这对即时DLL服务器至关重要)

给定一个线程模型 Both并给定在 文档:

Threading model of server | Apartment server is run in
------------------------------------------------------
Both                      | Same apartment as client

考虑到汉斯在另一个答案中写的内容:

...当需要在另一个线程上进行客户调用时,就会发生安排。当Comclass元素中指定的螺纹模型需要时,可能会发生。换句话说,当在一个线程上创建com对象但在另一个线程上创建com对象时,服务器不是线程安全的。

我初步的结论是,这样的对象将 绝不 需要对其界面的呼叫进行隐式编组,因为该对象将始终与客户的客户相同。

这是正确的,即使客户端进程正在运行为 Sta?

有帮助吗?

解决方案

是的,可能会有安排。

如果您的COM课程的客户在Sta中运行,并且您试图从另一个公寓中调用您的班级,则必须向其创建的公寓进行邮寄。

COM术语可能确实令人困惑。在这种情况下,当您引用“客户端”时,您确实是指线程,而不是整个应用程序(如它所暗示的)。

Both 仅表示服务器的线程模型符合实例化的客户端。也就是说,当您实例化类时,它会采用其创建的线程的线程模型。由于您在sta中实例化服务器,因此您的服务器将使用STA,这意味着只能在创建它的线程上调用它。如果另一个线程试图调用它,它将在其创建的线程上封装。

其他提示

我不禁要发布此内容,尽管这不是对问题的直接答案。

com的黄金时代有一篇精彩的MSKB文章: 信息:OLE线程模型的描述和工作. 。仍然在那里,并拥有所有相关信息。 关键是,如果您遵守规则,您不必担心是否有编组. 。只需将您的对象注册为 ThreadingModel=Both, ,将自由线的元帅与 CoCreateFreeThreadedMarshaler, ,完成。如果需要,COM将以最好的方式进行安排。根据客户端的公寓模型,如果遵循规则,客户端代码也可以接收到界面的直接指针。

当调用接口的方法时,您可能会收到的任何“外星人”接口在呼叫的范围中都是有效的,因为您保持在同一线程上。 如果您不需要存储它,那很重要。

但是,如果您确实需要缓存“外星人”界面,那么正确的方法就是使用它来存储它 CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream:

存储它:

  • 输入关键部分;
  • 称呼 CoMarshalInterThreadInterfaceInStream 并存储 IStream 成员领域的指针;
  • 留下关键部分;

检索它

  • 输入关键部分;
  • 称呼 CoGetInterfaceAndReleaseStream 检索界面
  • 称呼 CoMarshalInterThreadInterfaceInStream 并再次将其存储为 IStream 用于以后的任何用途
  • 留下关键部分;
  • 使用当前呼叫范围中的接口

发布它:

  • 当您不再需要保留它时,只需释放存储的 IStream (在关键部分内)。

如果“外星人”对象也是免费的,并且事物在同一过程中发生,那么您可能会处理直接接口指针。 CoGetInterfaceAndReleaseStream. 。但是,您不应该做出任何假设,也不需要知道处理的对象是原始对象还是com邮政使用者代理。

通过使用可以稍微优化这 CoMarshalInterface w/ MSHLFLAGS_TABLESTRONG / CoUnmarshalInterface / IStream::Seek(0, 0) / CoReleaseMarshalData 代替 CoGetInterfaceAndReleaseStream/CoGetInterfaceAndReleaseStream, ,要根据需要多次汇总相同的接口,而无需释放流。

更复杂(可能更有效)的缓存方案是可能的,涉及线程本地存储。但是,我相信那将是过度杀伤。我没有做任何时机,但我认为 CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream真的很低。

也就是说,如果您需要保持一个状态 存储任何可能需要线程亲和力的资源或对象, ,除了上述com接口外,您 不应该 将您的对象标记为 ThreadingModel=Both 或汇总FTM。

是的,召集仍然是可能的。几个例子:

  1. 该对象是从MTA线程实例化的,因此将其放入MTA公寓中,然后将其指针传递到任何STA线程中,并将STA线程调用对象的方法。在这种情况下,STA线程只能通过编组访问对象。

  2. 该对象是从STA线程实例化的,因此将其放入属于该线程的STA公寓中,然后将其指针传递到另一个STA线程或MTA线程中。在这两种情况下,这些线程只能通过编组访问对象。

实际上,您只能在以下两种情况下只能编组:

  1. 该对象是从MTA线程实例化的,然后仅由MTA线程访问 - 既是实例化对象的螺纹,又是同一过程的所有其他MTA线程。
  2. 该对象是从sta线程实例化的,然后仅由该线程访问

在所有其他情况下,编组都将开始。

ThreadingModel =两者都意味着COM服务器作者可以保证他的代码是线程安全,但不能提供相同的保证 其他 他没有编写的代码将以线程安全的方式调用。获得此类外交代码执行的最常见情况是通过回调,连接点是最常见的示例(通常在客户端运行中称为“事件”)。

因此,如果服务器实例是在sta中创建的,则客户端程序员将期望这些事件在同一线程上运行。即使从另一个线程调用了启动此类事件的服务器方法。这就要求该呼吁被封为。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top