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();

そして今、オブジェクトへのポインターを取得して、そのメソッドの1つを呼び出そうとしています:

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から派生したものであることに注意してください 。コレクション クラスについてより詳しく説明しています。#define を typedef に置き換えました。

ヴァンス

4

2 に答える 2

1

QueryInterfacefor ItemInterface/でクエリを実行するためIField、取得するポインターはこの型になります (おそらく、COM オブジェクト自体がメソッドを正しく実装している場合)。

したがって、次に行うことは、/IFieldからの無効な reinterpret_castです。これは機能しません。行によって例外があり、そこに無効な非 NULL ポインターがあります。ItemClassPtrCComObject<CField>*pField

ここで発生するもう 1 つの問題は、肥大化した参照カウントです。生のポインター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 の方法に似ているが、より簡単に実行できる別のより安全なオプションは次のとおりです。

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;

これはシンプルで参照カウンターに適しています。上記は、生の C++ ポインターCOM_INTERFACE_ENTRYを介して公開する偽のインターフェイス エントリを作成します。これは、他の何かによって実装された場合にクラッシュすることなく、正常にエラーQueryInterfaceを返します。E_NOINTERFACEIField

于 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 に答える