0

我有一个对象集合,定义为:

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 这更全面地描述了集合类。我已经用 typedefs 替换了#defines。

万斯

4

2 回答 2

1

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

所以你接下来要做的是一个无效的 reinterpret_cast from IFieldto ItemClassPtr/ CComObject<CField>*。这不起作用:通过您有异常的行,您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

这很容易,但假设IField您手头有实现CField,没有别的。否则,static_cast会成功并为您提供一个指针,但它将是无效的。另一个类似于 Remy's 但更容易做的更安全的选择是:

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上面创建了一个假接口条目,它通过原始QueryInterfaceC++ 指针公开。如果由其他东西实现,这将优雅地返回E_NOINTERFACE错误而不会崩溃。IField

于 2013-05-16T14:51:21.390 回答
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);
于 2013-05-16T14:45:58.140 回答