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'i IID
ç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:
Sınıfında yerleşik olarak bulunan
CCmdTarget
standart bir IUnknown uygulaması.AddRef ve Release tarafından değiştirilen başvuru sayısının bakımı
QueryInterface'in veri temelli uygulaması
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
Doğrudan veya dolaylı olarak öğesinden bir sınıf türetme
CCmdTarget
.DECLARE_INTERFACE_MAP
Türetilmiş sınıf tanımında işlevini kullanın.Desteklemek istediğiniz her arabirim için sınıf tanımındaki BEGIN_INTERFACE_PART ve END_INTERFACE_PART makrolarını kullanın.
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.
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.
Desteklediğiniz arabirimleri temsil eden iç içe sınıfların her birini uygulayın.
Üst,
CCmdTarget
türetilmiş nesneye erişmek için METHOD_PROLOGUE makroyu kullanın.AddRef, Release ve QueryInterface bu işlevlerin
CCmdTarget
(ExternalAddRef
,ExternalRelease
veExternalQueryInterface
) 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 CCmdTarget
tü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_IUnknown
bir 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 COleServerDoc
bir ç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 CCmdTarget
kullanılması, eklenmiş nesnenin -derived nesnesinden CCmdTarget
sabit 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::XAdviseSink
COleClientItem
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 IID
işlevlerden biri değilse çerçeve, toplama nesnesinin IID
QueryInterface üye işlevini otomatik olarak çağırır.
INTERFACE_AGGREGATE makroyu kullanmak için
Toplama nesnesine yönelik bir
IUnknown*
işaretçi içeren bir üye değişkeni (bir ) bildirin.Arabirim haritanıza üye değişkenine ada göre başvuran bir INTERFACE_AGGREGATE makro ekleyin.
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 COleObjectFactory
dışı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 COleObjectFactory
sağ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 CoCreateInstance
oluşturulan nesneler için "bilinmeyeni denetleme" parametresidir. Doğru "bilinmeyeni denetleme" işaretçisi çağrılarak CCmdTarget::GetControllingUnknown
alı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::OnCreateAggregates
oluş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 InternalAddRef
ExternalRelease
öğesini çağırın ExternalAddRef
InternalRelease
. 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 IAdviseSink
uygulamak 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 STDMETHOD
baş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
, IOleWindow
veya 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.