📄 ch16.htm
字号:
<TD ALIGN="LEFT" VALIGN="TOP"><TT>pUnknown</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">The aggregated object's true <TT>IUnknown</TT> interface. The outer object initializes
this to <TT>NULL</TT>, and the first query for one of the aggregated object's interfaces
creates the aggregated object.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>clsid</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">The ID of the class that is to be aggregated.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>cs</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">This is a critical section used for synchronization.</TD>
</TR>
</TABLE>
<B><I><BR>
<BR>
Tear-Off Interfaces</I></B><SPACER TYPE="HORIZONTAL" SIZE="10"><B><I> </I></B><I>Tear-off
interfaces </I>are similar to aggregation, except that the inner object is held only
until its interface is released. Remember that in aggregation, the aggregated (inner)
object is released in the destructor, or in <TT>FinalRelease</TT> for ATL aggregation.
This means that the aggregated object exists for the life of the outer object. Even
using ATL's automatic aggregation, the aggregated object, once created, remains until
the outer object is destroyed. Tear-off interfaces, on the other hand, are created
when they're needed and destroyed when they're released. They're implemented differently
from the way standard aggregation is implemented on both sides of the relationship.
The tear-off interface must be an object specifically written to provide tear-off
interfaces, and it can provide them only for a specific outer class. Now take a look
at how it's done.</P>
<P>First declare a new class that is derived from <TT>CComTearOffBase</TT> and the
interfaces that this class will provide. To do this, the tear-off class must specify
the owner, or outer, class. Listing 16.20 shows the declaration of a class that implements
a tear-off interface.
<H3><A NAME="Heading30"></A>Listing 16.20 <SPACER TYPE="HORIZONTAL" SIZE="10">Example
Declaration of a Tear-Off Class</H3>
<P><FONT COLOR="#0066FF"><TT>class CTearOff: public ISomeInterface,<BR>
public CComTearOffObjectBase<CSomeOuterClass><BR>
{ public:<BR>
CTearOff() {}<BR>
STDMETHOD(SomeMethod)()<BR>
{<BR>
return S_OK;<BR>
} <BR>
BEGIN_COM_MAP(CTearOff)<BR>
COM_INTERFACE_ENTRY(ISomeInterface)<BR>
END_COM_MAP() <BR>
}</TT></FONT></P>
<P>Next the outer class declares the tear-off interface in its <TT>COM_MAP</TT>.
The two macros listed below are available for doing this. The arguments to these
macros are described in Tables 16.5 and 16.6.</P>
<P><FONT COLOR="#0066FF"><TT>COM_INTERFACE_ENTRY_TEAR_OFF(iid, class)</TT></FONT></P>
<P>This macro is used to declare a true tear-off interface. <BR>
<BR>
<TABLE BORDER="1" WIDTH="100%">
<CAPTION><B>Table 16.5 </B><SPACER TYPE="HORIZONTAL" SIZE="10"><B><I>COM_INTERFACE_ENTRY_TEAR_OFF</I>
Arguments</B></CAPTION>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><B>Argument</B></TD>
<TD ALIGN="LEFT" VALIGN="TOP"><B>Meaning</B></TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>iid</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">The ID of the interface that is delegated to the tear-off interface object.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>class</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">The class of the tear-off interface object.</TD>
</TR>
</TABLE>
</P>
<P><FONT COLOR="#0066FF"><TT>COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, class, pUnknown,
cs)</TT></FONT></P>
<P>This macro is a variation of a tear-off interface in which the outer object caches
the tear-off interface by holding its <TT>IUnknown</TT> interface. The outer object
must release the tear-off interface in <TT>FinalRelease</TT>. This is functionally
equivalent to automatic aggregation, except that it uses a tear-off interface instead
of an aggregatable object. <BR>
<BR>
<TABLE BORDER="1" WIDTH="100%">
<CAPTION><B>Table 16.6</B><SPACER TYPE="HORIZONTAL" SIZE="10"><B> <I>COM_INTERFACE_ENTRY_CACHED_TEAR_OFF</I>
Arguments</B></CAPTION>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><B>Argument</B></TD>
<TD ALIGN="LEFT" VALIGN="TOP"><B>Meaning</B></TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>iid</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">The ID of the interface that is delegated to the tear-off interface object.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>class</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">The class of the tear-off interface object.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>pUnknown</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">The <TT>IUnknown</TT> interface of the tear-off interface object.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>cs</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">A critical section used for synchronization.</TD>
</TR>
</TABLE>
<BR>
<BR>
You've looked at aggregation up close, and you've looked at how to do aggregation
and tear-off interfaces with <TT>ATL</TT>. Next take a look at enumerators.
<H3><A NAME="Heading31"></A>Enumerators: An Interface Pattern for Sets</H3>
<P>An <I>enumerator</I> is an interface that provides access to a series of elements
and fits a specific pattern. The pattern is shown in Listing 16.21.
<H3><A NAME="Heading32"></A>Listing 16.21 <SPACER TYPE="HORIZONTAL" SIZE="10">Enumerator
Pattern</H3>
<P><FONT COLOR="#0066FF"><TT>interface IEnum<type> : <BR>
<BR>
{<BR>
STDMETHOD(Next)(ULONG celt, <type>* rgelt,<BR>
ULONG* pceltFetched);<BR>
STDMETHOD(Skip)(ULONG celt);<BR>
STDMETHOD(Reset)(void);<BR>
STDMETHOD(Clone)(IEnum<type>** ppEnum); <BR>
}</TT></FONT></P>
<P>An enumerator has four methods: <TT>Next</TT>, <TT>Skip</TT>, <TT>Reset</TT>,
and <TT>Clone</TT>.</P>
<P><TT>Next</TT> gets the next <TT>celt</TT> elements in the enumerator. <TT>rgelt</TT>
is the array of elements that is returned. The memory is provided by the caller and
must be large enough to hold the requested number of elements. <TT>pceltFetched</TT>
returns the number of elements that were fetched, which will always be equal to or
less than <TT>celt</TT>. It will be less than <TT>celt</TT> when the number of elements
from the current position to the end of the enumerator is less than the number of
requested elements. <TT>Next</TT> returns <TT>S_OK</TT> when the requested number
of elements are returned and <TT>S_FALSE</TT> when less than the requested number
are returned.</P>
<P><TT>Skip</TT> moves the current position by <TT>celt</TT> elements. <TT>Skip</TT>
returns <TT>S_OK</TT> when the requested number of elements are skipped and <TT>S_FALSE</TT>
when less than the requested number are skipped.</P>
<P><TT>Reset</TT> returns the enumerator to its original state, with the current
position at the beginning.</P>
<P><TT>Clone</TT> copies the enumerator in its current state.</P>
<P>In this sample, you implement an enumerator for the class IDs of the four classes
that you used to implement the <TT>INumber</TT> and <TT>IWholeNumber</TT> interfaces.
Because class IDs are <TT>GUID</TT>s, you implement the <TT>IEnumGUID</TT> interface,
which is defined in MSDEV\INCLUDE\COMCAT.H. The enumerator class, <TT>CNumbers</TT>,
will hold the class IDs in <TT>m_guids</TT>, and <TT>m_current</TT> will maintain
the current position. <TT>CNumbers</TT> is shown in Listing 16.22.
<H3><A NAME="Heading33"></A>Listing 16.22 <SPACER TYPE="HORIZONTAL" SIZE="10">UMBERS.H--The
CNumbers Enumerator Class</H3>
<P><FONT COLOR="#0066FF"><TT>class CNumbers : IEnumGUID<BR>
{<BR>
public:<BR>
// IUnknown methods<BR>
HRESULT __stdcall QueryInterface(REFIID riid,<BR>
LPVOID* lpInterface);<BR>
ULONG __stdcall AddRef();<BR>
ULONG __stdcall Release(); <BR>
// IEnumGUID methods<BR>
HRESULT __stdcall Next(ULONG celt, GUID* rgelt,<BR>
ULONG* pceltFetched);<BR>
HRESULT __stdcall Skip(ULONG celt);<BR>
HRESULT __stdcall Reset(void);<BR>
HRESULT __stdcall Clone(IEnumGUID** ppenum); <BR>
// Constructors and destructor<BR>
CNumbers();<BR>
~CNumbers(); <BR>
private:<BR>
ULONG m_cRef;<BR>
GUID m_guids[4];<BR>
int m_current; <BR>
};</TT></FONT></P>
<P>In the implementation of <TT>Next</TT>, you return the requested number of class
IDs from the current position, but not past the end of the array (see Listing 16.23).
<H3><A NAME="Heading34"></A>Listing 16.23 <SPACER TYPE="HORIZONTAL" SIZE="10">UMBERS.CPP--Next</H3>
<P><FONT COLOR="#0066FF"><TT>HRESULT CNumbers::Next(ULONG celt, GUID* rgelt,<BR>
ULONG* pceltFetched)<BR>
{<BR>
ULONG celtFetched = 0;<BR>
for(ULONG ii = 0; ii < celt; ii++)<BR>
{<BR>
if(m_current < 4)<BR>
{<BR>
rgelt[ii] = m_guids[m_current];<BR>
m_current ++;<BR>
celtFetched ++;<BR>
}<BR>
else<BR>
break;<BR>
} <BR>
if(pceltFetched)<BR>
*pceltFetched = celtFetched; <BR>
if(celtFetched == celt)<BR>
return S_OK;<BR>
else<BR>
return S_FALSE; <BR>
}</TT></FONT></P>
<P>Notice that the return argument <TT>pceltFetched</TT> is optional. When it's <TT>NULL</TT>,
the value isn't returned. Technically, the caller can send <TT>NULL</TT> only when
a single element is requested.</P>
<P><TT>Skip</TT> and <TT>Reset</TT> are straightforward. <TT>Skip</TT> moves the
current position forward, and <TT>Reset</TT> sets the current position to 0.</P>
<P><TT>Clone</TT> is a little different. It must create another object that is a
copy of itself, including the current position. One way to do this is to call <TT>CoCreateInstance</TT>,
which will go through the class factory so that object counts are handled there.
However, <TT>CoCreateInstance</TT> has some overhead that isn't necessary in this
case, so you do what the class factory does inside the <TT>Clone</TT> method; you
construct the enumerator and increment the object count. Having created the object,
use <TT>Skip</TT> to set its current position to the current position of the enumerator
being cloned. The <TT>Clone</TT> method is shown in Listing 16.24.
<H3><A NAME="Heading35"></A>Listing 16.24<SPACER TYPE="HORIZONTAL" SIZE="10"> UMBERS.CPP--Clone</H3>
<P><FONT COLOR="#0066FF"><TT>CNumbers* pNumbers = new CNumbers();<BR>
if(pNumbers == NULL)<BR>
{<BR>
_ASSERT(FALSE);<BR>
return E_OUTOFMEMORY;<BR>
}<BR>
IncrementObjectCount(); <BR>
HRESULT hr = pNumbers->QueryInterface(IID_IEnumGUID, (LPVOID*)ppenum);<BR>
if(FAILED(hr))<BR>
{<BR>
_ASSERT(FALSE);<BR>
delete pNumbers;<BR>
DecrementObjectCount();<BR>
*ppenum = NULL;<BR>
return E_UNEXPECTED;<BR>
} <BR>
hr = (*ppenum)->Skip(m_current);<BR>
if(hr != S_OK)<BR>
{<BR>
_ASSERT(FALSE);<BR>
delete pNumbers;<BR>
DecrementObjectCount();<BR>
*ppenum = NULL;<BR>
return E_UNEXPECTED; <BR>
}</TT></FONT></P>
<P>Any enumerator can be implemented pretty much the same way. Your internal data
structure can be an array, a list, or anything else that has a logical order.
<H3><A NAME="Heading36"></A>About the Samples</H3>
<P>The samples from this chapter are in the DLL project Number, described in Table
16.7.
<TABLE BORDER="1" WIDTH="100%">
<CAPTION><B>Table 16.7 </B><SPACER TYPE="HORIZONTAL" SIZE="10"><B>Sample Source Code</B></CAPTION>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><B>File</B></TD>
<TD ALIGN="LEFT" VALIGN="TOP"><B>Contents</B></TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP">Number.mdp</TD>
<TD ALIGN="LEFT" VALIGN="TOP">The project workspace.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -