⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch16.htm

📁 用VC开发activeX控件的电子书,很不错的
💻 HTM
📖 第 1 页 / 共 5 页
字号:
ULONG m_cRef;<BR>
double m_value; <BR>
};</TT></FONT></P>
<P><TT>CNumber</TT>'s vtable precisely matches the definition of the <TT>INumber</TT>
interface. Because <TT>INumber</TT> is derived from <TT>IUnknown</TT> (you should
never encounter an interface that isn't), the vtable for <TT>CNumber</TT> can be
used as the <TT>IUnknown</TT> interface as well. In Listing 16.8, you can see that
<TT>QueryInterface</TT> returns <TT>this</TT> whether the <TT>INumber</TT> or <TT>IUnknown</TT>
interface is requested (when <TT>riid</TT> is <TT>IID_IUnknown</TT>).
<H3><A NAME="Heading17"></A>Listing 16.8 <SPACER TYPE="HORIZONTAL" SIZE="10">UMBER.CPP--CNumber::QueryInterface</H3>
<P><FONT COLOR="#0066FF"><TT>HRESULT CNumber::QueryInterface(REFIID riid,<BR>
LPVOID* ppvInterface)<BR>
{<BR>
if(IsEqualIID(riid, IID_IUnknown) ||<BR>
IsEqualIID(riid, IID_INumber))<BR>
{<BR>
*ppvInterface = this;<BR>
AddRef();<BR>
return NOERROR;<BR>
}<BR>
else<BR>
{<BR>
return E_NOINTERFACE;<BR>
} <BR>
}</TT></FONT></P>
<P>In some cases, you'll need more than one vtable. For example, if you need to implement
multiple interfaces, such as <TT>INumber</TT> and <TT>IPersistStorage</TT>, you simply
can't define a vtable that satisfies both interfaces. Or if you're going to make
your object aggregatable, you need to have a separate vtable for the true <TT>IUnknown</TT>
interface. This is discussed in more detail in the section about Aggregation later
in this chapter. <BR>
<BR>
<B><I>CNumber1</I>: Separate vTables </B><SPACER TYPE="HORIZONTAL" SIZE="10">The
<TT>CNumber1</TT><B> </B>class is another COM Object that implements the <TT>INumber</TT>
interface (see Listing 16.9). The <TT>CNumber1</TT><B> </B>class complicates things
a little by implementing the <TT>INumber</TT> interface using the vtable of an embedded
class.
<H3><A NAME="Heading18"></A>Listing 16.9<SPACER TYPE="HORIZONTAL" SIZE="10"> UMBER1.H--The
CNumber1 Class: Multiple vtables</H3>
<P><FONT COLOR="#0066FF"><TT>class CNumber1 : IUnknown<BR>
{<BR>
public:<BR>
// IUnknown methods<BR>
HRESULT __stdcall QueryInterface(REFIID riid,<BR>
LPVOID* lpInterface);<BR>
ULONG __stdcall AddRef();<BR>
ULONG __stdcall Release(); <BR>
class ImpINumber : INumber<BR>
{<BR>
// IUnknown methods<BR>
HRESULT __stdcall QueryInterface(REFIID riid,<BR>
LPVOID* lpInterface);<BR>
ULONG __stdcall AddRef();<BR>
ULONG __stdcall Release(); <BR>
// ILrsInetUnlock methods<BR>
HRESULT __stdcall GetNumber(double* pValue);<BR>
HRESULT __stdcall SetNumber(double value); <BR>
// A macro to gain access to the CNumber1 &quot;this&quot;<BR>
// pointer from within the embedded class<BR>
#define GET_CNUMBER1(pThis) \<BR>
CNumber1* pThis = \<BR>
((CNumber1*)((BYTE*)this - \<BR>
offsetof(CNumber1, m_impINumber)));<BR>
} m_impINumber;<BR>
friend class ImpINumber; <BR>
// Constructors and destructor<BR>
CNumber1();<BR>
~CNumber1(); <BR>
private:<BR>
ULONG m_cRef;<BR>
double m_value; <BR>
};</TT></FONT></P>
<P>Here the <TT>ImpINumber</TT> class is defined within the <TT>CNumber1</TT> class,
and the instance <TT>m_impINumber</TT> is declared as a member of the <TT>CNumber1</TT>
class. The vtable of <TT>CNumber1</TT> matches the <TT>IUnknown</TT> interface, and
the vtable of <TT>ImpINumber</TT> matches the <TT>INumber</TT> interface. <TT>CNumber1</TT>'s
<TT>QueryInterface</TT> returns <TT>this</TT> when <TT>IUnknown</TT> is requested
and the address of <TT>m_impINumber</TT> when <TT>INumber</TT> is requested. Listing
16.10 shows the implementation of <TT>CNumber1::QueryInterface</TT>.
<H3><A NAME="Heading19"></A>Listing 16.10 <SPACER TYPE="HORIZONTAL" SIZE="10">UMBER1.CPP--CNumber1::QueryInterface</H3>
<P><FONT COLOR="#0066FF"><TT>HRESULT CNumber1::QueryInterface(REFIID riid,<BR>
LPVOID* ppvInterface)<BR>
{<BR>
if(IsEqualIID(riid, IID_IUnknown))<BR>
{<BR>
*ppvInterface = this;<BR>
AddRef();<BR>
return NOERROR;<BR>
}<BR>
else if(IsEqualIID(riid, IID_INumber))<BR>
{<BR>
*ppvInterface = &amp;m_impINumber;<BR>
AddRef();<BR>
return NOERROR;<BR>
}<BR>
else<BR>
{<BR>
return E_NOINTERFACE;<BR>
} <BR>
}</TT></FONT></P>
<P>One complicating factor is that the class <TT>ImpINumber</TT> doesn't have immediate
access to the members of <TT>CNumber1</TT>. In Listing 16.9, you see that <TT>ImpINumber</TT>
is declared as a friend of <TT>CNumber1</TT>. The friend declaration gives <TT>ImpINumber</TT>
access to <TT>CNumber1</TT>'s members, but <TT>ImpINumber</TT> still doesn't have
a pointer to <TT>CNumber1</TT>'s members. Within the <TT>ImpINumber</TT> class, you
declared the macro <TT>GET_CNUMBER1</TT> that calculates the address of <TT>CNumber1</TT>
based on the address of the embedded class<TT> ImpINumber</TT>. Using <TT>GET_CNUMBER1</TT>,
you can get a pointer to the <TT>CNumber1</TT> object from within the methods of
the <TT>ImpINumber</TT> class. This macro applies the technique used by MFC's <TT>METHOD_PROLOGUE</TT>
set of macros. <BR>
<BR>
<IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>


<BLOCKQUOTE>
	<P><B>The <I>GET_CNUMBER1</I> Macro</B><BR>
	The <TT>GET_CNUMBER1</TT> macro is used by the <TT>ImpINumber</TT> class, which is
	embedded in the <TT>CNumber1</TT> class, to gain access to the <TT>CNumber1</TT>
	class's members. <TT>GET_CNUMBER1</TT> subtracts the offset of the <TT>ImpINumber</TT>
	class within the <TT>CNumber1</TT> class from the address of the <TT>ImpINumber</TT>
	object (<TT>this</TT>) to determine the address of the <TT>CNumber1</TT> object.

</BLOCKQUOTE>

<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"><BR>
<BR>
Another complicating factor of this implementation is that <TT>INumber</TT> is derived
from <TT>IUnknown</TT>, so <TT>INumber</TT> includes <TT>QueryInterface</TT>, <TT>AddRef</TT>,
and <TT>Release</TT> methods. The embedded <TT>ImpINumber</TT> class must implement
these methods. It does so by calling <TT>CNumber1</TT>'s corresponding methods. Listing
16.11 shows <TT>ImpINumber</TT>'s implementation of <TT>QueryInterface</TT>.
<H3><A NAME="Heading20"></A>Listing 16.11 <SPACER TYPE="HORIZONTAL" SIZE="10">UMBER1.CPP--CNumber1::ImpINumber::QueryInterface</H3>
<P><FONT COLOR="#0066FF"><TT>HRESULT CNumber1::ImpINumber::QueryInterface(REFIID
riid,<BR>
LPVOID* ppvInterface)<BR>
{<BR>
GET_CNUMBER1(pThis);<BR>
return pThis-&gt;QueryInterface(riid, ppvInterface); <BR>
}</TT></FONT></P>
<P>Now that you've established how C++ vtables can be used to implement interfaces
in different ways, take a look at how vtables are used in aggregation.
<H3><A NAME="Heading21"></A>Reusing ActiveX Objects with Aggregation</H3>
<P>COM Objects don't use inheritance to reuse the implementations of existing objects.
<I>Aggregation</I> is used instead of inheritance. In aggregation, one object creates
another object and reuses its interface implementations. Where traditional inheritance
has a base class and a subclass, aggregation has an aggregated object and outer object.
The outer and aggregated objects are presented to the rest of the system as if they
were a single object.</P>
<P>In this sample, you create <TT>CNumber2</TT>, which is an aggregatable implementation
of <TT>INumber</TT>. Then you create <TT>CNumber3</TT>. <TT>CNumber3</TT> will implement
the <TT>IWholeNumber</TT> interface and aggregate a <TT>CNumber2</TT> object. The
aggregated <TT>CNumber2</TT> object will provide the implementation of the <TT>INumber</TT>
interface for the <TT>CNumber3</TT> object (see fig. 16.1). <B><I><BR>
<BR>
</I></B><A HREF="Art/16/q_fig01n.jpg"><B>FIG. 16.1</B></A> <I><TT><BR>
CNumber3</TT> aggregates <TT>CNumber2</TT> to reuse its implementation of the <TT>INumber</TT>
interface.<B><BR>
<BR>
CNumber2</B></I><B>: An Aggregatable Object </B><SPACER TYPE="HORIZONTAL" SIZE="10">An
object is aggregatable if it follows some rules in its implementations of the <TT>IUnknown</TT>
interface.

<UL>
	<LI>The aggregatable object must handle the <TT>punkOuter</TT> argument of the class
	factory's <TT>CreateInstance</TT> function. The <TT>punkOuter</TT> argument is the
	pointer to the <TT>IUnknown</TT> interface of the outer object.
	<P>
	<LI>The aggregatable object must delegate <TT>QueryInterface</TT> calls to the outer
	object.
	<P>
	<LI>The exception to the preceding rule is that the <TT>QueryInterface</TT> method
	of the aggregatable object's <I>true</I> <TT>IUnknown</TT> interface does not delegate
	to the outer object.
</UL>

<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>


<BLOCKQUOTE>
	<P><B>The True <I>IUnknown</I> Interface</B><BR>
	Every COM Object has an <TT>IUnknown</TT> interface implementation. When you use
	aggregation, you create a complex COM Object. This complex object presents only one
	<TT>IUnknown</TT> interface to the rest of the system. The aggregated object's <TT>IUnknown</TT>
	is hidden, known only to the outer object. The hidden <TT>IUnknown</TT> of the aggregated
	object is known as its <I>true</I> <TT>IUnknown</TT> interface.

</BLOCKQUOTE>

<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>


<BLOCKQUOTE>
	<P><B>NOTE:</B> In a typical implementation of an aggregatable object, the <TT>AddRef</TT>
	and <TT>Release</TT> methods of interfaces other than the true <TT>IUNKNOWN</TT>
	are delegated to the outer object, but alternative reference counting schemes are
	possible.

</BLOCKQUOTE>

<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"><BR>
<BR>
In order to meet these requirements, the aggregatable object must have a separate
vtable for its true <TT>IUnknown</TT> interface. <TT>CNumber1</TT> already has separate
vtables for <TT>IUnknown</TT> and <TT>INumber</TT>, so start building <TT>CNumber2</TT>
by modifying <TT>CNumber1</TT>. Provide a constructor that takes the outer object's
<TT>IUnknown</TT> interface (see Listing 16.12 and Listing 16.13), add a member variable
to store that <TT>IUnknown</TT> interface (see Listing 16.12), and use that member
variable to delegate <TT>QueryInterface</TT>, <TT>AddRef</TT>, and <TT>Release</TT>
calls to the outer object (see Listing 16.14).
<H3><A NAME="Heading22"></A>Listing 16.12<SPACER TYPE="HORIZONTAL" SIZE="10"> UMBER2.H--CNumber2</H3>
<P><FONT COLOR="#0066FF"><TT>class CNumber2 : IUnknown<BR>
{<BR>
public:<BR>
// IUnknown methods<BR>
HRESULT __stdcall QueryInterface(REFIID riid,<BR>
LPVOID* lpInterface);<BR>
ULONG __stdcall AddRef();<BR>
ULONG __stdcall Release(); <BR>
class ImpINumber : INumber<BR>
{<BR>
// IUnknown methods<BR>
HRESULT __stdcall QueryInterface(REFIID riid,<BR>
LPVOID* lpInterface);<BR>
ULONG __stdcall AddRef();<BR>
ULONG __stdcall Release(); <BR>
// ILrsInetUnlock methods<BR>
HRESULT __stdcall GetNumber(double* pValue);<BR>
HRESULT __stdcall SetNumber(double value); <BR>
// A macro to gain access to the CNumber2 &quot;this&quot;<BR>
// pointer from within the embedded class<BR>
#define GET_CNUMBER2(pThis) \<BR>
CNumber2* pThis = \<BR>
((CNumber2*)((BYTE*)this - \<BR>
offsetof(CNumber2, m_impINumber)));<BR>
} m_impINumber;<BR>
friend class ImpINumber; <BR>
// Constructors and destructor<BR>
CNumber2();<BR>
CNumber2(LPUNKNOWN);<BR>
~CNumber2(); <BR>
private:<BR>
ULONG m_cRef;<BR>
double m_value;<BR>
LPUNKNOWN m_pUnkOuter; <BR>
};</TT></FONT></P>
<H3><A NAME="Heading23"></A>Listing 16.13 <SPACER TYPE="HORIZONTAL" SIZE="10">UMBER2.CPP--CNumber2::CNumber2(LPUNKNOWN)</H3>
<P><FONT COLOR="#0066FF"><TT>CNumber2::CNumber2(LPUNKNOWN pUnkOuter)<BR>
{<BR>
m_cRef = 0;<BR>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -