代码改变世界

COM使用ICollectionOnSTLImpl的枚举与集合中的引用计数

2011-06-10 23:00  ubunoon  阅读(358)  评论(0编辑  收藏  举报

在ATL中实现COM接口的集合接口和枚举接口,相对比较简单,使用ICollectionOnSTLImpl基本上就可以完成,网络上相关的教程也很多。大致的代码

typedef std::vector<long> CollType;

typedef CComEnumOnSTL
<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT>, CollType > EnumType;
typedef ICollectionOnSTLImpl
<IDyVector, CollType, VARIANT, _Copy<VARIANT>, EnumType > CollectionType;

/////////////////////////////////////////////////////////////////////////////
// CDyVector
class ATL_NO_VTABLE CDyVector :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CDyVector, &CLSID_DyVector>,
public IEnumOnSTLImpl<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT>, CollType>,
public ...

上面的代码,对于只是使用long类型的数据,内部使用CollType的push方式添加数据,这个是毫无疑问是正确的。

但是当想使用VARIANT作为数据类型的时候,网络上大部分是直接给出

typedef std::vector<VARIANT> CollType;

方式的代码,如果没有碰到vt==VT_DISPATCH方式时,这个实现是没有问题的,但是当碰到VT_DISPATCH的时候,引用计数

就会出现不匹配,因为在push的时候,只是做了一个简单的operator=操作,这个操作,对于VARIANT仅仅只是简单的赋值,并不涉及引用计数问题

当使用外部变量赋值进来的时候,再次使用该变量,就有可能导致一次引用违例,之所以是有可能,是因为该变量可能在外部还被引用或者该变量虽然没有被引用,但是内存的gc还没有将其收取回来,此时访问时,不会出现问题,但是如同前面描述的,引用刚好被用尽的时候,gc将内存收回的时候,此时再次访问该数据,就会出现访问违例。

既然是一次operator=操作,那么使用CComVariant进行复制,就可以满足上面的操作,因为CComVariant进行了operator=重载,将operator=的操作修改为了CopyVariant的函数调用,该函数会在内部帮助我们判断,是需要直接复制还是需要进行引用计数+1的操作,此时CollType类型的变量,如果存在引用计数,就会保持一个引用计数+1的操作,使得内部的数据不会被gc回收,从而保证访问不出现违例。