我有一组定义为的对象:
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。
万斯
答案 0 :(得分:1)
您使用QueryInterface
查询ItemInterface
/ IField
,因此您获得的指针属于此类型(假设COM对象本身正确实现了该方法)。
接下来你要做的是从IField
到ItemClassPtr
/ CComObject<CField>*
的无效reinterpret_cast。这不起作用:在pField
处有异常的行中有非空的非NULL指针。
你在这里遇到的另一个问题是膨胀的引用计数:你的原始指针ppField
收到一个带有递增计数器的指针,我不认为你在这里发布它,我想你可能在其他地方管理引用错误同样。如果您的代码已被销毁并且您对指向已销毁对象的指针进行了调用,那么您的代码上可能会出现其他类似的访问冲突问题,最终可能会对代码产生非常类似的访问冲突。
此时解决问题的有用之处在于异常位置的调用堆栈。从目前的描述来看,目前尚不清楚你在get_Type
电话中的深度。
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
会成功并获得指针,但它将无效。另一种类似于雷米的更安全的选择,但更容易做到的是:
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);