我忍不住发布这个,虽然它不是问题的直接答案。
有一篇来自 COM 黄金时代的精彩 MSKB 文章:INFO:OLE 线程模型的描述和工作原理。仍然在那里,并拥有所有相关信息。关键是,如果你遵守规则,你不应该担心是否有编组。只需将您的对象注册为ThreadingModel=Both
,将 Free-Threaded Marshaler 与 聚合起来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。