如何从ATL对象集合中检索对象指针?

时间:2013-05-16 14:08:59

标签: c++ com atl

我有一组定义为的对象:

typedef IField                  ItemInterface;
typedef CComObject<CField>*     ItemClassPtr;
typedef CAdapt< CComPtr<ItemInterface> > ItemType;
typedef std::vector< ItemType > ContainerType;

我通过一系列调用创建了几个CField对象(忽略了hresult):

IField* ppField = 0;
hresult = CField::CreateInstance(&ppField);
ItemType spField = ppField;
m_coll.push_back(spField);
ppField->Release();

现在我正在尝试检索指向对象的指针,以便我可以调用其中一个方法:

ItemClassPtr pField;
short type1;
m_coll[index].m_T->QueryInterface( __uuidof(ItemInterface), (void **)&pField ) );
pField->get_Type(&type1);

并且在get_Type调用时它在访问冲突时崩溃。 响应响应者的帖子,这被改变了:

short type1;
IField * ppField = m_coll[index].m_T;
CComQIPtr<CField, &__uuidof(IField)> pField = ppField;
pField->get_Type(&type1);

但是当我尝试跟踪get_Type调用时它仍然崩溃。

以下是CField类定义的前言:

class ATL_NO_VTABLE CField : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CField, &CLSID_Field>,
    public ISupportErrorInfo,
    public IFieldAccess,
    public IDispatchImpl<IField, &IID_IField, &LIBID_SQLite02>
{
    friend class CFields;
    friend class CrecordSet;
public:
    CField();
    ~CField();

DECLARE_REGISTRY_RESOURCEID(IDR_FIELD)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CField)
    COM_INTERFACE_ENTRY(IField)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
    COM_INTERFACE_ENTRY_IID(__uuidof(IField), CField)
END_COM_MAP()

请帮忙!

请注意,此问题是之前ATL问题In ICollectionOnSTLImpl implementation, can't access m_T or item object's members的分支 这更全面地描述了集合类。我已经用typedef替换#defines。

万斯

2 个答案:

答案 0 :(得分:1)

您使用QueryInterface查询ItemInterface / IField,因此您获得的指针属于此类型(假设COM对象本身正确实现了该方法)。

接下来你要做的是从IFieldItemClassPtr / CComObject<CField>*的无效reinterpret_cast。这不起作用:在pField处有异常的行中有非空的非NULL指针。

你在这里遇到的另一个问题是膨胀的引用计数:你的原始指针ppField收到一个带有递增计数器的指针,我不认为你在这里发布它,我想你可能在其他地方管理引用错误同样。如果您的代码已被销毁并且您对指向已销毁对象的指针进行了调用,那么您的代码上可能会出现其他类似的访问冲突问题,最终可能会对代码产生非常类似的访问冲突。

此时解决问题的有用之处在于异常位置的调用堆栈。从目前的描述来看,目前尚不清楚你在get_Type电话中的深度。

顺便说一句,如果你试图从接口指针恢复C ++类指针,如果不涉及编组就很容易,否则很可能是一个问题。要在CField*离开时IField*,请在此处执行此操作:

IField* pField = ...
CField* pNativeField = static_cast<CField*>(pField);

请注意,类指针的生命周期依赖于COM对象本身的生命周期,因此执行此操作会更安全:

CComPtr<IField> pField = ...
CField* pNativeField = static_cast<CField*>((IField*) pField);
// NOTE: pNativeField is valid until at least pField is released

这很简单,但假设您手上的IFieldCField实现,而不是其他任何内容。否则,static_cast会成功并获得指针,但它将无效。另一种类似于雷米的更安全的选择,但更容易做到的是:

class ATL_NO_VTABLE CField :
// ...
BEGIN_COM_MAP(CField)
  //...
  COM_INTERFACE_ENTRY_IID(CLSID_Field, CField)
END_COM_MAP( )

//...

IField* pField = ...
CComQIPtr<CField, &CLSID_Field> pNativeField = pField;

这很简单且参考计数器友好。上面的COM_INTERFACE_ENTRY创建了一个虚假的接口条目,它通过QueryInterface公开了一个原始C ++指针。如果E_NOINTERFACE由其他内容实现,这将优雅地返回IField错误而不会崩溃。

答案 1 :(得分:0)

您无法使用QueryInterface()获取指针,只能使用接口指针。而且你也不能将接口指针类型转换为类指针。访问实现类的唯一安全方法是定义类实现的单独私有接口,并使该接口公开返回类对象的this指针的方法。例如:

class CField;

interface DECLSPEC_UUID("...") IFieldAccess : public IUnknown
{
public:
    virtual CField* get_ClassPtr() = 0;
};

class ATL_NO_VTABLE CField : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CField, &CLSID_Field>,
    public IDispatchImpl<IField, &IID_IField, &LIBID_SQLite02>,
    public ISupportErrorInfo,
    public IFieldAccess,
{
    friend class CFields;
    friend class CrecordSet;
public:
    CField();
    ~CField();

DECLARE_REGISTRY_RESOURCEID(IDR_FIELD)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CField)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IField)
    COM_INTERFACE_ENTRY(IFieldAccess)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()
};

CField* CField::get_ClassPtr()
{
    return this;
}

CComPtr<IFieldAccess> pFieldAccess;
CField* pField;
short type1;
m_coll[index].m_T->QueryInterface( __uuidof(IFieldAccess), (void **)&pFieldAccess) );
pField = pFieldAccess->get_ClassPtr();
pField->get_Type(&type1);