荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Deny (冬天来咯), 信区: Program
标  题: 三、COM_INTERFACE_ENTRY_TEAR_OFF(iid, x)
发信站: 荔园晨风BBS站 (Thu Nov 15 20:07:57 2001), 转信

三、COM_INTERFACE_ENTRY_TEAR_OFF(iid, x) 参考ATL例程Beeper、COMMAP

使用这个宏的目的就是为了把一些很少用到的接口放在一个单独的组件中实现,仅

当查询到这个接口时,才创建这个组件,并且当它的引用计数减为0时就会被释放
掉。我
们知道ATL中组件是通过多重继承实现的,每继承一个接口,在为它分配的内存块
中就会
多一个虚函数表指针,用这个宏就可以为每个组件的实例节省下这一个虚函数表指
针来
(一个指针4个字节,好象也不多啊,呵呵)
下面我们来看它的典型用法:
class CTearOff1: //该类是专门用来实现分割接口ITearOff1的
public IDispatchImpl,
public CComTearOffObjectBase //外部对象
{
public:
CTearOff1(){}
~CTearOff1(){}
BEGIN_COM_MAP(CTearOff1)
COM_INTERFACE_ENTRY(ITearOff1)
END_COM_MAP()
HRESULT STDMETHODCALLTYPE get_Name(BSTR* pbstrName)
{
*pbstrName = ::SysAllocString(L"ITearOff1");
return S_OK;
}
};

class COuter : public ..... //我们真正要实现的组件
{
public:
...........
BEGIN_COM_MAP(COuter)
...........
COM_INTERFACE_ENTRY_TEAR_OFF(IID_ITearOff1, CTearOff1)
END_COM_MAP()
...........
};
CTearOff1实现了Tear-off接口ITearOff1,实现方法与其他组件并无不同。唯一不
同的
是它从CComTearOffObjectBase继承,CComTearOffObjectBase定义如下:
template
class CComTearOffObjectBase : public CComObjectRootEx
{
public:
public:
typedef Owner _OwnerClass;
CComObject* m_pOwner;
CComTearOffObjectBase() {m_pOwner = NULL;}
};
我们又看到了我们熟悉的一个类CComObject,它是组件的真正生成类。从上面的定
义中
可知道CComTearOffObjectBase主要功能就是包含了一个指向外部对象(在这里就
是我们
的组件类CComObject)的指针。它的功能将在后面看到。
我们继续用我们的老办法来跟踪一下看看它的执行过程。假设pOuter是我们已经获
得的

组件的IOuter接口指针。
执行pOuter->QueryInterface(IID_ITearOff1, (void **)&pTear1);
函数堆栈一:
7.CTearOff1::_InternalQueryInterface(...)
6.ATL::CComInternalCreator >::
CreateInstance(...)
5.ATL::CComObjectRootBase::_Creator(...)
4.ATL::AtlInternalQueryInterface(...)
3.ATL::CComObjectRootBase::InternalQueryInterface(...)
2.COuter::_InternalQueryInterface(...)
1.ATL::CComObject::QueryInterface(...)

解释:
1--4:这些代码已经遇到过很多次了,我们还是集中精力看看核心代码:
ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis,
const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
{
//..........
while (pEntries->pFunc != NULL)
{
BOOL bBlind = (pEntries->piid == NULL);
if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))
{
if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY) //offset
{
//若是简单接口,....
}
else //actual function call
{
HRESULT hRes = pEntries->pFunc(pThis,
iid, ppvObject, pEntries->dw);
if (hRes == S_OK || (!bBlind && FAILED(hRes)))
return hRes;
}
}
}
pEntries++;
}
return E_NOINTERFACE;
}
当在COuter的接口映射数组中找到ITearOff1后,因为它不是一个简单接口,所以
要执行
pEntries->pFunc(....)。我们先来看看COM_INTERFACE_ENTRY_TEAR_OFF的定义:

#define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x)\
{&iid,\
(DWORD)&_CComCreatorData<\ CComInternalCreator< CComTearOffObject< x> >\

>::data,\
_Creator},
看不太明白,还是继续我们路由得了
5:原来_Creator是CComObjectRootBase的静态成员函数,它可是COuter的一个基类
啊,
所以才可以这样写而不会编译出错。看看它的实现吧:
static HRESULT WINAPI _Creator(void* pv, REFIID iid, void** ppvObject,
DWORD)
{
_ATL_CREATORDATA* pcd = (_ATL_CREATORDATA*)dw;
return pcd->pFunc(pv, iid, ppvObject);
}
struct _ATL_CREATORDATA
{
{
_ATL_CREATORFUNC* pFunc;
};
typedef HRESULT (WINAPI _ATL_CREATORFUNC)(void* pv, REFIID riid,
LPVOID* ppv);
template
_ATL_CREATORDATA _CComCreatorData::data =
{Creator::CreateInstance};
源代码都列出来了,不用我多说,大家也都能看懂了。继续路由吧
6:绕了一大圈,现在我们调用的应该是CComInternalCreator<...>::
CreateInstance
template
class CComInternalCreator
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)

{
ATLASSERT(*ppv == NULL);
HRESULT hRes = E_OUTOFMEMORY;
T1* p = NULL;
ATLTRY(p = new T1(pv))
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes == S_OK)
hRes = p->_InternalQueryInterface(riid, ppv);
if (hRes != S_OK)
delete p;
}
return hRes;
}
};
同我们所见到的大多数Creator类一样,它也只有一个静态CreateInstance函数。
现在
我们终于可以创建我们分割组件了,它不是CTearOff1,它也是经了一层包装的,

CComTearOffObject!现在我们再来看看它的构造函数干了些什么事:
CComTearOffObject(void* pv)
{
ATLASSERT(m_pOwner == NULL);
m_pOwner = reinterpret_cast*>(pv);
m_pOwner->AddRef();
}
还记得CTearOff1是从CComTearOffObjectBase继承的吗,这个基类包含了一个成员

量m_pOwner,现在它被赋值为指向它的外部对象的指针了。
7.现在终于把这个实现分割接口的组件创建了,剩下的在CTearOff1中查询
ITearOff1的
工作已经是重复劳动了,不再赘述。

执行pTear1->QueryInterface(ITearOff1, (void **)&pTear2)
一个实现分割接口的组件有可能包含多个分割接口,我们来检测一下它的查询过程

函数堆栈二:
4..............
3.COuter::_InternalQueryInterface(...)
2.ATL::CComObject::QueryInterface(...)
1.ATL::CComTearOffObject::QueryInterface(...)

解释:
1:STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
return m_pOwner->QueryInterface(iid, ppvObject);
}
还记得我们创建的分割组件是CComTearOffObject吗?现在执行查询操作
的是它的成员函数。它的实现很简单,事实上它什么也没做,仅仅是把它交给它的

部对象(即CComObject)去做了。还记得m_pOwner是在构造函数里赋值的吧。
现在是否感到有些不妙呢?呵呵
2、3:
果然,现在已经不用再看下去了,剩下的将是重复我们在调用第一条查询操作所做

一切。这个过程很简单,但它也隐含说明了一点:若对一个实现分割接口的组件每

询一次它的接口,就会调用一个新的实例!!!在上例中,最后的结果pTear2和
pTear1
是不一样的!!这显然是浪费!
在下一节中,我们将介绍一个可以解决这个问题的宏!


--
      ●     ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
   ● ┃ ●  ┃ 其实,我只是不再轻易感觉到快乐,但这并不意味我就投靠了悲伤 ┃
  ●─┼─● ┃ 我不会再刻意地去演奏和深化任何一种情绪,久而久之我会学会在 ┃
   ● ┃ ●  ┃ 生活面前宠辱不惊,处之泰然。并视这种心境为平静,且用心爱惜 ┃
      ●     ┃ 着它,因为在这种心情中去生活,我会渐渐地学会把苦难视为当然 ┃
     Deny    ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.1.201]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店