Aracılığıyla paylaş


TN038: MFC/OLE IUnknown Uygulaması

Dekont

Aşağıdaki teknik not, çevrimiçi belgelere ilk kez eklendiğinden beri güncelleştirilmemiştir. Sonuç olarak, bazı yordamlar ve konular güncel olmayabilir veya yanlış olabilir. En son bilgiler için, çevrimiçi belge dizininde ilgilendiğiniz konuyu aramanız önerilir.

OLE 2'nin merkezinde "OLE Bileşeni Nesne Modeli" veya COM bulunur. COM, işbirliği yapan nesnelerin birbirleriyle nasıl iletişim kuracaklarına ilişkin bir standart tanımlar. Bu, yöntemlerin bir nesneye nasıl gönderildiği de dahil olmak üzere bir "nesnenin" nasıl göründüğüne ilişkin ayrıntıları içerir. COM ayrıca tüm COM uyumlu sınıfların türetildiği bir temel sınıf tanımlar. Bu temel sınıf IUnknown'dir. IUnknown arabirimi C++ sınıfı olarak adlandırılsa da, COM herhangi bir dile özgü değildir; C, PASCAL veya COM nesnesinin ikili düzenini destekleyebilecek başka bir dilde uygulanabilir.

OLE, IUnknown'dan türetilen tüm sınıfları "arabirimler" olarak ifade eder. IUnknown gibi bir "arabirim" hiçbir uygulama taşımadığından bu önemli bir ayrımdır. Yalnızca nesnelerin iletişim kurdığı protokolü tanımlar, bu uygulamaların ne yaptığının ayrıntılarını tanımlamaz. Bu, maksimum esneklik sağlayan bir sistem için makuldür. MFC/C++ programları için varsayılan bir davranış uygulamak MFC'nin işidir.

MFC'nin IUnknown uygulamasını anlamak için önce bu arabirimin ne olduğunu anlamanız gerekir. IUnknown'un basitleştirilmiş bir sürümü aşağıda tanımlanmıştır:

class IUnknown
{
public:
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
    virtual ULONG AddRef() = 0;
    virtual ULONG Release() = 0;
};

Dekont

Bu çizimde olduğu gibi __stdcall bazı gerekli çağrı kuralı ayrıntıları dışarıda bırakılır.

AddRef ve Release üye işlevleri nesnenin bellek yönetimini denetler. COM, nesneleri izlemek için bir başvuru sayma düzeni kullanır. Bir nesneye hiçbir zaman C++ ile yaptığınız gibi doğrudan başvurulmazdı. Bunun yerine, COM nesnelerine her zaman bir işaretçi aracılığıyla başvurulur. Sahibi tarafından kullanıldığında nesneyi serbest bırakmak için nesnenin Release üyesi çağrılır (geleneksel bir C++ nesnesi için olduğu gibi işleç silme kullanımı yerine). Başvuru sayma mekanizması, tek bir nesneye birden çok başvurunun yönetilmesini sağlar. AddRef ve Release uygulaması nesne üzerinde bir başvuru sayısı tutar; nesne, başvuru sayısı sıfıra ulaşana kadar silinmez.

AddRef ve Release , uygulama açısından oldukça basittir. İşte önemsiz bir uygulama:

ULONG CMyObj::AddRef()
{
    return ++m_dwRef;
}

ULONG CMyObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

QueryInterface üye işlevi biraz daha ilginç. Tek üye işlevleri AddRef ve Release olan bir nesneye sahip olmak çok ilginç değildir; nesneye IUnknown'un sağladığından daha fazlasını yapmalarını söylemek iyi olur. QueryInterface burada kullanışlıdır. Aynı nesne üzerinde farklı bir "arabirim" elde etmenizi sağlar. Bu arabirimler genellikle IUnknown'dan türetilir ve yeni üye işlevleri ekleyerek ek işlevler ekler. COM arabirimlerinde hiçbir zaman arabirimde üye değişkenleri bildirilir ve tüm üye işlevler saf sanal olarak bildirilir. Örneğin,

class IPrintInterface : public IUnknown
{
public:
    virtual void PrintObject() = 0;
};

Yalnızca IUnknown kullanıyorsanız IPrintInterface almak için, öğesini kullanarak QueryInterface'iIID çağırınIPrintInterface. , IID arabirimi benzersiz olarak tanımlayan 128 bitlik bir sayıdır. Sizin veya OLE'nin tanımladığınız her arabirim için bir IID vardır. pUnk bir IUnknown nesnesinin işaretçisiyse, bu nesneden IPrintInterface alma kodu şu olabilir:

IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface, (void**)&pPrint) == NOERROR)
{
    pPrint->PrintObject();
    pPrint->Release();
    // release pointer obtained via QueryInterface
}

Bu oldukça kolay görünüyor, ancak hem IPrintInterface hem de IUnknown arabirimini destekleyen bir nesneyi nasıl uygulayabilirsiniz? Bu durumda, IPrintInterface doğrudan IUnknown'dan türetildiğinden , IPrintInterface uygulanarak IUnknown otomatik olarak desteklenir. Örnek:

class CPrintObj : public CPrintInterface
{
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
    virtual ULONG AddRef();
    virtual ULONG Release();
    virtual void PrintObject();
};

AddRef ve Release uygulamaları, yukarıda uygulananlarla tam olarak aynı olacaktır. CPrintObj::QueryInterface şuna benzer olacaktır:

HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

Gördüğünüz gibi, arabirim tanımlayıcısı (IID) tanınırsa nesnenize bir işaretçi döndürülür; aksi takdirde bir hata oluşur. Ayrıca başarılı bir QueryInterface'in zımni bir AddRef ile sonuçlandığını unutmayın. Elbette CEditObj::P rint de uygulamanız gerekir. IPrintInterface doğrudan IUnknown arabiriminden türetildiği için bu basittir. Ancak, her ikisi de IUnknown'dan türetilen iki farklı arabirimi desteklemek istiyorsanız aşağıdakileri göz önünde bulundurun:

class IEditInterface : public IUnkown
{
public:
    virtual void EditObject() = 0;
};

C++ çoklu devralmayı kullanma dahil olmak üzere hem IEditInterface hem de IPrintInterface'i destekleyen bir sınıf uygulamanın birkaç farklı yolu olsa da, bu not bu işlevi uygulamak için iç içe sınıfların kullanımına odaklanır.

class CEditPrintObj
{
public:
    CEditPrintObj();

    HRESULT QueryInterface(REFIID iid, void**);
    ULONG AddRef();
    ULONG Release();
    DWORD m_dwRef;

    class CPrintObj : public IPrintInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_printObj;

    class CEditObj : public IEditInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_editObj;
};

Uygulamanın tamamı aşağıda verilmiştir:

CEditPrintObj::CEditPrintObj()
{
    m_editObj.m_pParent = this;
    m_printObj.m_pParent = this;
}

ULONG CEditPrintObj::AddRef()
{
    return ++m_dwRef;
}

CEditPrintObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = &m_printObj;
        AddRef();
        return NOERROR;
    }
    else if (iid == IID_IEditInterface)
    {
        *ppvObj = &m_editObj;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

ULONG CEditPrintObj::CEditObj::AddRef()
{
    return m_pParent->AddRef();
}

ULONG CEditPrintObj::CEditObj::Release()
{
    return m_pParent->Release();
}

HRESULT CEditPrintObj::CEditObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

ULONG CEditPrintObj::CPrintObj::AddRef()
{
    return m_pParent->AddRef();
}

ULONG CEditPrintObj::CPrintObj::Release()
{
    return m_pParent->Release();
}

HRESULT CEditPrintObj::CPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

IUnknown uygulamasının çoğunun CEditPrintObj::CEditObj ve CEditPrintObj::CPrintObj içindeki kodu çoğaltmak yerine CEditPrintObj sınıfına yerleştirildiğine dikkat edin. Bu, kod miktarını azaltır ve hataları önler. Buradaki önemli nokta, IUnknown arabiriminden nesnenin destekleyebilecek herhangi bir arabirimi almak için QueryInterface'i çağırmanın mümkün olması ve bu arabirimlerin her birinden aynı işlemi yapmak mümkün olmasıdır. Bu, her arabirimden kullanılabilen tüm QueryInterface işlevlerinin tam olarak aynı şekilde davranması gerektiği anlamına gelir. Bu katıştırılmış nesnelerin "dış nesne" içinde uygulamayı çağırması için bir arka işaretçi kullanılır (m_pParent). m_pParent işaretçisi CEditPrintObj oluşturucu sırasında başlatılır. Ardından CEditPrintObj::CPrintObj::P rintObject ve CEditPrintObj::CEditObj::EditObject de uygulayabilirsiniz. Bir özellik eklemek için oldukça fazla kod eklendi: nesneyi düzenleme özelliği. Neyse ki, arabirimlerin yalnızca tek bir üye işlevine (gerçekleşse de) sahip olması oldukça nadirdir ve bu durumda EditObject ve PrintObject genellikle tek bir arabirimde birleştirilir.

Bu tür basit bir senaryo için çok fazla açıklama ve çok fazla kod. MFC/OLE sınıfları daha basit bir alternatif sağlar. MFC uygulaması, Windows iletilerinin İleti Haritalar ile sarmalanan yöntemine benzer bir teknik kullanır. Bu tesis Arabirim Haritalar olarak adlandırılmaktadır ve sonraki bölümde ele alın almaktadır.

MFC Arabirimi Haritalar

MFC/OLE, kavram ve yürütmede MFC'nin "İleti Haritalar" ve "Dağıtma Haritalar" gibi bir "Arabirim Haritalar" uygulamasını içerir. MFC'nin Arabirim Haritalar temel özellikleri şunlardır:

Ayrıca, arabirim eşlemeleri aşağıdaki gelişmiş özellikleri destekler:

  • Toplayıcı COM nesneleri oluşturma desteği

  • COM nesnesinin uygulanmasında toplama nesnelerini kullanma desteği

  • Uygulama kancalanabilir ve genişletilebilir

Toplama hakkında daha fazla bilgi için Toplama konusuna bakın.

MFC'nin arabirim eşleme desteğinin kökü sınıfındadır CCmdTarget . CCmdTarget"has-a" başvuru sayısı ve IUnknown uygulamasıyla ilişkili tüm üye işlevleri (örneğin, başvuru sayısı içindedirCCmdTarget). OLE COM'u destekleyen bir sınıf oluşturmak için, öğesinden CCmdTarget bir sınıf türetip istenen arabirimleri uygulamak için çeşitli makroların yanı sıra üye işlevlerini CCmdTarget kullanırsınız. MFC'nin uygulaması, yukarıdaki örneğe çok benzer şekilde her arabirim uygulamasını tanımlamak için iç içe sınıflar kullanır. Bu, standart bir IUnknown uygulaması ve yinelenen kodun bazılarını ortadan kaldıran bir dizi makroyla daha kolay hale gelir.

Arabirim Eşlemesi Temelleri

MFC'nin arabirim haritalarını kullanarak bir sınıf uygulamak için

  1. Doğrudan veya dolaylı olarak öğesinden bir sınıf türetme CCmdTarget.

  2. DECLARE_INTERFACE_MAP Türetilmiş sınıf tanımında işlevini kullanın.

  3. Desteklemek istediğiniz her arabirim için sınıf tanımındaki BEGIN_INTERFACE_PART ve END_INTERFACE_PART makrolarını kullanın.

  4. Uygulama dosyasında, sınıfın arabirim eşlemesini tanımlamak için BEGIN_INTERFACE_MAP ve END_INTERFACE_MAP makrolarını kullanın.

  5. Desteklenen her IID için, BEGIN_INTERFACE_MAP ile END_INTERFACE_MAP makroları arasındaki INTERFACE_PART makroyu kullanarak bu IID'yi sınıfınızın belirli bir "parçasına" eşleyin.

  6. Desteklediğiniz arabirimleri temsil eden iç içe sınıfların her birini uygulayın.

  7. Üst, CCmdTargettüretilmiş nesneye erişmek için METHOD_PROLOGUE makroyu kullanın.

  8. AddRef, Release ve QueryInterface bu işlevlerin CCmdTarget (ExternalAddRef, ExternalReleaseve ExternalQueryInterface) uygulanması için temsilci seçebilir.

Yukarıdaki CPrintEditObj örneği aşağıdaki gibi uygulanabilir:

class CPrintEditObj : public CCmdTarget
{
public:
    // member data and member functions for CPrintEditObj go here

// Interface Maps
protected:
    DECLARE_INTERFACE_MAP()

    BEGIN_INTERFACE_PART(EditObj, IEditInterface)
        STDMETHOD_(void, EditObject)();
    END_INTERFACE_PART(EditObj)

    BEGIN_INTERFACE_PART(PrintObj, IPrintInterface)
        STDMETHOD_(void, PrintObject)();
    END_INTERFACE_PART(PrintObj)
};

Yukarıdaki bildirim, öğesinden CCmdTargettüretilen bir sınıf oluşturur. DECLARE_INTERFACE_MAP makro, çerçeveye bu sınıfın özel arabirim eşlemesine sahip olacağını söyler. Buna ek olarak, BEGIN_INTERFACE_PART ve END_INTERFACE_PART makroları iç içe sınıfları tanımlar ve bu durumda CEditObj ve CPrintObj adlarıyla (X, iç içe sınıfları yalnızca "C" ile başlayan genel sınıflardan ve "I" ile başlayan arabirim sınıflarından ayırmak için kullanılır). Bu sınıfların iki iç içe üyesi oluşturulur: sırasıyla m_CEditObj ve m_CPrintObj. Makrolar AddRef, Release ve QueryInterface işlevlerini otomatik olarak bildirir; bu nedenle yalnızca bu arabirime özgü işlevleri bildirirsiniz: EditObject ve PrintObject (HEDEF platforma uygun şekilde _stdcall ve sanal anahtar sözcüklerin sağlanması için OLE makrosu STDMETHOD kullanılır).

Bu sınıf için arabirim eşlemesini uygulamak için:

BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
    INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()

Bu, IID_IPrintInterface IID'yi sırasıyla m_CPrintObj ve IID_IEditInterface m_CEditObj bağlar. CCmdTarget QueryInterface (CCmdTarget::ExternalQueryInterface) uygulaması, m_CPrintObj işaretçileri döndürmek için bu eşlemeyi kullanır ve istendiğinde m_CEditObj. için IID_IUnknownbir giriş eklemek gerekli değildir; çerçeve, istendiğinde IID_IUnknown haritadaki ilk arabirimi (bu örnekte m_CPrintObj) kullanır.

BEGIN_INTERFACE_PART makrosu Sizin için AddRef, Release ve QueryInterface işlevlerini otomatik olarak bildirse de bunları uygulamanız gerekir:

ULONG FAR EXPORT CEditPrintObj::XEditObj::AddRef()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalAddRef();
}

ULONG FAR EXPORT CEditPrintObj::XEditObj::Release()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalRelease();
}

HRESULT FAR EXPORT CEditPrintObj::XEditObj::QueryInterface(
    REFIID iid,
    void FAR* FAR* ppvObj)
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

void FAR EXPORT CEditPrintObj::XEditObj::EditObject()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    // code to "Edit" the object, whatever that means...
}

CEditPrintObj::CPrintObj uygulaması, CEditPrintObj::CEditObj için yukarıdaki tanımlara benzer olacaktır. Bu işlevleri otomatik olarak oluşturmak için kullanılabilecek bir makro oluşturmak mümkün olsa da (daha önce MFC/OLE geliştirmesinde böyle bir durum söz konusuydu), makro birden fazla kod satırı oluşturduğunda kesme noktalarını ayarlamak zorlaşır. Bu nedenle, bu kod el ile genişletilir.

İleti eşlemelerinin çerçeve uygulamasını kullanarak, yapılması gerekmeyen birkaç şey vardır:

  • QueryInterface Uygulama

  • AddRef ve Release Uygulama

  • Her iki arabiriminizde de bu yerleşik yöntemlerden birini bildirin

Ayrıca çerçeve, ileti eşlemelerini dahili olarak kullanır. Bu, belirli arabirimleri zaten destekleyen ve çerçeve tarafından sağlanan arabirimlerin yerine veya eklemeler sağlayan gibi COleServerDocbir çerçeve sınıfından türetmenizi sağlar. Çerçeve bir arabirim eşlemesini temel sınıftan devralmayı tam olarak desteklediğinden bunu yapabilirsiniz. bu nedenle BEGIN_INTERFACE_MAP temel sınıfın adını ikinci parametresi olarak alır.

Dekont

MFC'nin yerleşik OLE arabirim uygulamalarının uygulanması genellikle MFC sürümünden bu arabirimin ekli özelleştirmesini devralarak yeniden kullanılamaz. İçeren türetilmiş nesneye erişim elde etmek için METHOD_PROLOGUE makronun CCmdTargetkullanılması, eklenmiş nesnenin -derived nesnesinden CCmdTargetsabit bir uzaklığı anlamına geldiği için bu mümkün değildir. Başka bir deyişle, örneğin, XAdviseSink nesnenin üstünden belirli bir uzaklıkta olmaya bağlı olduğundan, MFC'nin uygulamasından COleClientItem::XAdviseSinkCOleClientItem eklenmiş bir XMyAdviseSink türetemezsiniz.

Dekont

Ancak, MFC'nin varsayılan davranışını istediğiniz tüm işlevler için MFC uygulamasına temsilci atayabilirsiniz. Bu, sınıfındaki MFC uygulamasında IOleInPlaceFrame (XOleInPlaceFrame) COleFrameHook gerçekleştirilir (birçok işlev için m_xOleInPlaceUIWindow temsilci olarak atanır). Bu tasarım, birçok arabirim uygulayan nesnelerin çalışma zamanı boyutunu küçültmek için seçilmiştir; bir geri işaretçi gereksinimini ortadan kaldırır (örneğin, önceki bölümde m_pParent kullanılmıştır).

Toplama ve Arabirim Haritalar

MFC, tek başına COM nesnelerini desteklemenin yanı sıra toplamayı da destekler. Toplamanın kendisi burada tartışılacak kadar karmaşık bir konudur; toplama hakkında daha fazla bilgi için Toplama konusuna bakın. Bu not, çerçeve ve arabirim haritalarına yerleşik olarak toplama desteğini açıklamaktadır.

Toplamayı kullanmanın iki yolu vardır: (1) toplamayı destekleyen bir COM nesnesi kullanma ve (2) başka bir nesne tarafından toplanabilir bir nesne uygulama. Bu özelliklere "toplama nesnesi kullanma" ve "bir nesneyi bir araya getirilebilir hale getirme" denir. MFC ikisini de destekler.

Toplama Nesnesi Kullanma

Toplama nesnesini kullanmak için, toplamayı QueryInterface mekanizmasına bağlamanın bir yolu olmalıdır. Başka bir deyişle, toplama nesnesi nesnenizin yerel bir parçası gibi davranmalıdır. Peki bu MFC'nin arabirim eşleme mekanizmasına nasıl bağlanır? İç içe nesnenin bir IID ile eşlendiği INTERFACE_PART makrosuna ek olarak, türetilmiş sınıfınızın CCmdTarget bir parçası olarak bir toplama nesnesi de bildirebilirsiniz. Bunu yapmak için INTERFACE_AGGREGATE makro kullanılır. Bu, arabirim eşleme mekanizmasıyla tümleştirilecek bir üye değişkeni (IUnknown veya türetilmiş bir sınıfın işaretçisi olmalıdır) belirtmenize olanak tanır. İşaretçi çağrıldığında CCmdTarget::ExternalQueryInterface NULL değilse, istenen nesnenin kendisi tarafından CCmdTarget desteklenen yerel IIDişlevlerden biri değilse çerçeve, toplama nesnesinin IID QueryInterface üye işlevini otomatik olarak çağırır.

INTERFACE_AGGREGATE makroyu kullanmak için

  1. Toplama nesnesine yönelik bir IUnknown*işaretçi içeren bir üye değişkeni (bir ) bildirin.

  2. Arabirim haritanıza üye değişkenine ada göre başvuran bir INTERFACE_AGGREGATE makro ekleyin.

  3. Bir noktada (genellikle sırasında CCmdTarget::OnCreateAggregates), üye değişkenini NULL dışında bir değerle başlatın.

Örnek:

class CAggrExample : public CCmdTarget
{
public:
    CAggrExample();

protected:
    LPUNKNOWN m_lpAggrInner;
    virtual BOOL OnCreateAggregates();

    DECLARE_INTERFACE_MAP()
    // "native" interface part macros may be used here
};

CAggrExample::CAggrExample()
{
    m_lpAggrInner = NULL;
}

BOOL CAggrExample::OnCreateAggregates()
{
    // wire up aggregate with correct controlling unknown
    m_lpAggrInner = CoCreateInstance(CLSID_Example,
        GetControllingUnknown(), CLSCTX_INPROC_SERVER,
        IID_IUnknown, (LPVOID*)&m_lpAggrInner);

    if (m_lpAggrInner == NULL)
        return FALSE;
    // optionally, create other aggregate objects here
    return TRUE;
}

BEGIN_INTERFACE_MAP(CAggrExample, CCmdTarget)
    // native "INTERFACE_PART" entries go here
    INTERFACE_AGGREGATE(CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

m_lpAggrInner değişkeni oluşturucuda NULL olarak başlatılır. Çerçeve, QueryInterface'in varsayılan uygulamasında null üye değişkenini yoksayar. OnCreateAggregates toplama nesnelerinizi oluşturmak için iyi bir yerdir. nesnesini MFC uygulamasının COleObjectFactorydışında oluşturuyorsanız açıkça çağırmanız gerekir. 'de CCmdTarget::OnCreateAggregates toplama oluşturmanın nedeni ve CCmdTarget::GetControllingUnknown kullanımı, bir araya toplanabilir nesneler oluşturulurken görünür hale gelir.

Bu teknik, nesnenize toplama nesnesinin desteklediği tüm arabirimleri ve yerel arabirimlerini verir. Toplamanın desteklediği arabirimlerin yalnızca bir alt kümesini istiyorsanız, öğesini geçersiz kılabilirsiniz CCmdTarget::GetInterfaceHook. Bu, QueryInterface'e benzer şekilde çok düşük düzeyli kancalılık sağlar. Genellikle, toplamanın desteklediği tüm arabirimleri istersiniz.

Nesne Uygulamasını Toplayıcı Hale Getirme

Bir nesnenin birleştirilebilir olması için AddRef, Release ve QueryInterface uygulamasının "bilinmeyeni denetleme" için temsilci seçmesi gerekir. Başka bir deyişle, nesnenin parçası olması için AddRef, Release ve QueryInterface'i IUnknown'dan türetilmiş farklı bir nesneye devretmesi gerekir. Bu "bilinmeyeni denetleme" nesnesi oluşturulduğunda, yani uygulamasına COleObjectFactorysağlanır. Bunun uygulanması az miktarda ek yük taşır ve bazı durumlarda istenmez, bu nedenle MFC bunu isteğe bağlı hale getirir. Bir nesnenin birleştirilebilir olmasını sağlamak için, nesnenin oluşturucusundan çağrısı CCmdTarget::EnableAggregation yapın.

Nesne de toplamalar kullanıyorsa, toplama nesnelerine doğru "bilinmeyeni denetleme" geçirmeyi de unutmayın. Toplama oluşturulduğunda genellikle bu IUnknown işaretçisi nesneye geçirilir. Örneğin, pUnkOuter parametresi ile CoCreateInstanceoluşturulan nesneler için "bilinmeyeni denetleme" parametresidir. Doğru "bilinmeyeni denetleme" işaretçisi çağrılarak CCmdTarget::GetControllingUnknownalınabilir. Ancak bu işlevden döndürülen değer oluşturucu sırasında geçerli değildir. Bu nedenle, toplamalarınızı yalnızca uygulamasından oluşturulmuş COleObjectFactory olsa bile, döndürülen değerin GetControllingUnknown güvenilir olduğu bir geçersiz kılmada CCmdTarget::OnCreateAggregatesoluşturmanız önerilir.

Yapay başvuru sayıları eklerken veya yayınlarken nesnenin doğru başvuru sayısını işlemesi de önemlidir. Bu durumdan emin olmak için her zaman ve yerine ve InternalAddRefExternalRelease öğesini çağırın ExternalAddRefInternalRelease. Toplamayı destekleyen bir sınıfta veya InternalAddRef çağrısı InternalRelease yapmak nadirdir.

Başvuru Malzemesi

Ole'nin kendi arabirimlerinizi tanımlama veya çerçevenin OLE arabirimleri uygulamasını geçersiz kılma gibi gelişmiş kullanımı, temel arabirim eşleme mekanizmasının kullanılmasını gerektirir.

Bu bölümde, bu gelişmiş özellikleri uygulamak için kullanılan her makro ve API ele alınmaktadır.

CCmdTarget::EnableAggregation — İşlev Açıklaması

void EnableAggregation();

Açıklamalar

Bu türdeki nesneler için OLE toplamayı desteklemek istiyorsanız, türetilmiş sınıfın oluşturucusunda bu işlevi çağırın. Bu, birleşebilir nesneler için gereken özel bir IUnknown uygulaması hazırlar.

CCmdTarget::ExternalQueryInterface — İşlev Açıklaması

DWORD ExternalQueryInterface(
    const void FAR* lpIID,
    LPVOIDFAR* ppvObj
);

Parametreler

lpIID
IID'ye uzak bir işaretçi (QueryInterface için ilk bağımsız değişken)

ppvObj
IUnknown* işaretçisi (QueryInterface için ikinci bağımsız değişken)

Açıklamalar

Sınıfınızın uyguladığı her arabirim için IUnknown uygulamanızda bu işlevi çağırın. Bu işlev, nesnenizin arabirim eşlemesine göre QueryInterface'in standart veri temelli uygulamasını sağlar. Dönüş değerini bir HRESULT'a ataması gerekir. Nesne toplanırsa, bu işlev yerel arabirim eşlemesini kullanmak yerine "denetimli IUnknown"u çağırır.

CCmdTarget::ExternalAddRef — İşlev Açıklaması

DWORD ExternalAddRef();

Açıklamalar

Sınıfınızın uyguladığı her arabirim için IUnknown::AddRef uygulamanızda bu işlevi çağırın. Dönüş değeri, CCmdTarget nesnesinde yeni başvuru sayısıdır. Nesne toplanırsa, bu işlev yerel başvuru sayısını işlemek yerine "IUnknown'u denetleme" çağrısı yapacaktır.

CCmdTarget::ExternalRelease — İşlev Açıklaması

DWORD ExternalRelease();

Açıklamalar

Sınıfınızın uyguladığı her arabirim için IUnknown::Release uygulamanızda bu işlevi çağırın. Dönüş değeri, nesnedeki yeni başvuru sayısını gösterir. Nesne toplanırsa, bu işlev yerel başvuru sayısını işlemek yerine "IUnknown'u denetleme" çağrısı yapacaktır.

DECLARE_INTERFACE_MAP — Makro Açıklaması

DECLARE_INTERFACE_MAP

Açıklamalar

Bu makro bir arabirim eşlemesi olacak türetilmiş CCmdTarget herhangi bir sınıfta kullanın. DECLARE_MESSAGE_MAP ile aynı şekilde kullanılır. Bu makro çağrısı, sınıf tanımına, genellikle bir üst bilgi içine ( yerleştirilmelidir. H) dosyası. DECLARE_INTERFACE_MAP olan bir sınıf, uygulama dosyasında arabirim eşlemesini tanımlamalıdır (. CPP) BEGIN_INTERFACE_MAP ve END_INTERFACE_MAP makroları ile.

BEGIN_INTERFACE_PART ve END_INTERFACE_PART — Makro Açıklamaları

BEGIN_INTERFACE_PART(localClass, iface);
END_INTERFACE_PART(localClass)

Parametreler

localClass
Arabirimini uygulayan sınıfın adı

yüz
Bu sınıfın uyguladığı arabirimin adı

Açıklamalar

Sınıfınızın uygulayacağı her arabirim için bir BEGIN_INTERFACE_PART ve END_INTERFACE_PART çiftiniz olması gerekir. Bu makrolar, tanımladığınız OLE arabiriminden türetilmiş bir yerel sınıfı ve bu sınıfın katıştırılmış üye değişkenini tanımlar. AddRef, Release ve QueryInterface üyeleri otomatik olarak bildirilir. Uygulanan arabirimin parçası olan diğer üye işlevleri için bildirimleri eklemeniz gerekir (bu bildirimler BEGIN_INTERFACE_PART ve END_INTERFACE_PART makrolar arasına yerleştirilir).

iface bağımsız değişkeni, veya (veya IPersistStorage kendi özel arabiriminiz) gibi IAdviseSinkuygulamak istediğiniz OLE arabirimidir.

localClass bağımsız değişkeni, tanımlanacak yerel sınıfın adıdır. Adın başına otomatik olarak bir 'X' eklenecektir. Bu adlandırma kuralı, aynı ada sahip genel sınıflarla çakışmaları önlemek için kullanılır. Buna ek olarak, ekli üyenin adı, localClass adıyla aynıdır, ancak adı 'm_x' ön ekindedir.

Örnek:

BEGIN_INTERFACE_PART(MyAdviseSink, IAdviseSink)
    STDMETHOD_(void, OnDataChange)(LPFORMATETC, LPSTGMEDIUM);
    STDMETHOD_(void, OnViewChange)(DWORD, LONG);
    STDMETHOD_(void, OnRename)(LPMONIKER);
    STDMETHOD_(void, OnSave)();
    STDMETHOD_(void, OnClose)();
END_INTERFACE_PART(MyAdviseSink)

, IAdviseSink'ten türetilen XMyAdviseSink adlı yerel bir sınıfı ve m_xMyAdviseSink olarak bildirildiği sınıfın bir üyesini tanımlar.Not:

Dekont

_ ile STDMETHODbaşlayan satırlar temelde OLE2'den kopyalanır. H ve biraz değiştirildi. Bunları OLE2'den kopyalama. H, çözülmesi zor olan hataları azaltabilir.

BEGIN_INTERFACE_MAP ve END_INTERFACE_MAP — Makro Açıklamaları

BEGIN_INTERFACE_MAP(theClass, baseClass)
END_INTERFACE_MAP

Parametreler

theClass
Arabirim eşlemesinin tanımlandığı sınıf

Baseclass
Sınıfın türetildiği sınıf.

Açıklamalar

BEGIN_INTERFACE_MAP ve END_INTERFACE_MAP makroları, arabirim eşlemesini tanımlamak için uygulama dosyasında kullanılır. Uygulanan her arabirim için bir veya daha fazla INTERFACE_PART makro çağrısı vardır. Sınıfın kullandığı her toplama için bir INTERFACE_AGGREGATE makro çağrısı vardır.

INTERFACE_PART — Makro Açıklaması

INTERFACE_PART(theClass, iid, localClass)

Parametreler

theClass
Arabirim eşlemesini içeren sınıfın adı.

ııd
Katıştırılmış IID sınıfla eşlenecek olan.

localClass
Yerel sınıfın adı (daha az 'X').

Açıklamalar

Bu makro, nesnenizin destekleeceği her arabirim için BEGIN_INTERFACE_MAP makro ile END_INTERFACE_MAP makro arasında kullanılır. Bir IID'yiClass ve localClass tarafından belirtilen sınıfın bir üyesiyle eşlemenizi sağlar. 'm_x', localClass'a otomatik olarak eklenir. Tek bir üyeyle birden IID fazla ilişkili olabileceğini unutmayın. Bu, yalnızca "en çok türetilen" bir arabirim uyguladığınızda ve tüm ara arabirimleri de sağlamak istediğinizde çok kullanışlıdır. Bunun iyi bir örneği arabirimdir IOleInPlaceFrameWindow . Hiyerarşisi şöyle görünür:

IUnknown
    IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Bir nesne uygularsa IOleInPlaceFrameWindow, bir istemci şu arabirimlerden herhangi birinde olabilir QueryInterface : IOleUIWindow, IOleWindowveya IUnknown, "en çok türetilen" arabirimin (aslında uyguladığınız) IOleInPlaceFrameWindow yanı sıra. Bunu işlemek için, her bir temel arabirimi arabirimle IOleInPlaceFrameWindow eşlemek için birden fazla INTERFACE_PART makro kullanabilirsiniz:

sınıf tanımı dosyasında:

BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)

sınıf uygulama dosyasında:

BEGIN_INTERFACE_MAP(CMyWnd, CFrameWnd)
    INTERFACE_PART(CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP

Çerçeve, her zaman gerekli olduğundan IUnknown ile ilgilenir.

INTERFACE_PART — Makro Açıklaması

INTERFACE_AGGREGATE(theClass, theAggr)

Parametreler

theClass
Arabirim eşlemesini içeren sınıfın adı,

theAggr
Toplanacak üye değişkeninin adı.

Açıklamalar

Bu makro, çerçeveye sınıfın bir toplama nesnesi kullandığını söylemek için kullanılır. BEGIN_INTERFACE_PART ve END_INTERFACE_PART makroları arasında görünmelidir. Toplama nesnesi, IUnknown'dan türetilen ayrı bir nesnedir. Toplama ve INTERFACE_AGGREGATE makro kullanarak, toplamanın desteklediği tüm arabirimlerin nesne tarafından doğrudan destekleniyor gibi görünmesini sağlayabilirsiniz. theAggr bağımsız değişkeni yalnızca sınıfınızın IUnknown'dan türetilen bir üye değişkeninin adıdır (doğrudan veya dolaylı olarak). Tüm INTERFACE_AGGREGATE makroların arabirim haritasına yerleştirildiğinde INTERFACE_PART makroları izlemesi gerekir.

Ayrıca bkz.

Sayıya Göre Teknik Notlar
Kategoriye Göre Teknik Notlar