在ATL中实现COM接口的集合接口和枚举接口,相对比较简单,使用ICollectionOnSTLImpl基本上就可以完成,网络上相关的教程也很多。大致的代码
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回收,从而保证访问不出现违例。