📄 ch14.htm
字号:
#include "comutil.h"<BR>
#include "..\ifish\ifish.h"<BR>
#include "..\ifish\ibass.h"<BR>
#include "custombassid.h"<BR>
#include "server.h"<BR>
#include "cobass.h" <BR>
COBass::COBass(<BR>
IUnknown* pUnkOuter,<BR>
CServer* pServer) <BR>
{<BR>
// Zero the COM object's reference count.<BR>
m_cRefs = 0; <BR>
// No AddRef necessary if non-NULL, as we're nested.<BR>
m_pUnkOuter = pUnkOuter; <BR>
// Assign the pointer to the server control object.<BR>
m_pServer = pServer;<BR>
lstrcpy (m_zFishName, "Bass");<BR>
lstrcpy (m_zLocation, "Lilly Pads");<BR>
m_bEatsOtherFish = TRUE;<BR>
m_bFreshwater = TRUE;<BR>
} <BR>
COBass::~COBass(void)<BR>
{<BR>
} <BR>
STDMETHODIMP COBass::QueryInterface(<BR>
REFIID riid,<BR>
PPVOID ppv)<BR>
{<BR>
HRESULT hr = ResultFromScode(E_NOINTERFACE);<BR>
*ppv = NULL; <BR>
if (IID_IUnknown == riid || IID_IFish == riid)<BR>
{<BR>
*ppv = (IFish *)this;<BR>
}<BR>
else if (IID_IBass == riid)<BR>
{<BR>
*ppv = (IBass *)this;<BR>
} <BR>
if (NULL != *ppv)<BR>
{<BR>
// We've handed out a pointer to the interface so obey the COM rules<BR>
// and AddRef the reference count.<BR>
((LPUNKNOWN)*ppv)->AddRef();<BR>
hr = NOERROR;<BR>
}<BR>
return (hr); <BR>
}</TT></FONT></P>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>
<BLOCKQUOTE>
<P><B>CAUTION:</B><BR>
One important fact to remember is that <I>this</I> pointer is not a direct object
pointer to any interface, not even to the <TT>IUnknown</TT> interface. This fact
means that the interface pointer returned must be explicitly type-cast to the correct
pointer for each interface. Type-casting the pointer changes the pointer value. C++
overloads the type-cast operators to allocate the right vtable for the interface
type. Failure to <I>not</I> type-cast to the explicit interface yields unpredictable
results.
</BLOCKQUOTE>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"><BR>
<BR>
In looking at the <TT>QueryInterface</TT> implementation, the <TT>IFish</TT> interface
pointer is returned for the interface <TT>IID</TT>'s <TT>IFish</TT> and <TT>IUnknown</TT>.
Since the <TT>IFish</TT> interface is derived from <TT>Iunknown</TT>, it is perfectly
valid to return the <TT>IFish</TT> interface when the<TT> IUnknown</TT> interface
is asked for. <TT>IFish</TT> is a matter of preference; the <TT>IBass</TT> interface
can be chosen as well.</P>
<P>The other COM interfaces are implemented as methods of the <TT>COBass</TT> class.
Listing 14.4 illustrates the COM interface implementations.
<H3><A NAME="Heading9"></A>Listing 14.4 <SPACER TYPE="HORIZONTAL" SIZE="10">COBASS.CPP--COBass
Interface Implementations of the IFish and IBass Interfaces</H3>
<P><FONT COLOR="#0066FF"><TT>STDMETHODIMP_(ULONG) COBass::AddRef(void)<BR>
{<BR>
m_cRefs++;<BR>
return m_cRefs;<BR>
} <BR>
STDMETHODIMP_(ULONG) COBass::Release(void)<BR>
{<BR>
m_cRefs--; <BR>
if (0 == m_cRefs)<BR>
{<BR>
// We've reached a zero reference count for this COM object.<BR>
// So we tell the server housing to decrement its global object<BR>
// count so that the server will be unloaded if appropriate.<BR>
if (NULL != m_pServer)<BR>
m_pServer->ObjectsDown(); <BR>
delete this;<BR>
} <BR>
return m_cRefs;<BR>
} STDMETHODIMP COBass::GetFishName( char *pStr)<BR>
{<BR>
LOG("COBass::GetFishName\n"); <BR>
if (pStr)<BR>
lstrcpy((char *)pStr, m_zFishName);<BR>
return (HRESULT)NOERROR;<BR>
} <BR>
STDMETHODIMP COBass::IsFreshwater( BOOL *pBool )<BR>
{<BR>
LOG("COBass::IsFreshwater\n"); <BR>
if (pBool)<BR>
{<BR>
*pBool = m_bFreshwater;<BR>
return S_OK;<BR>
} <BR>
return (HRESULT)NOERROR;<BR>
} STDMETHODIMP COBass::GetLocation( char *pStr)<BR>
{<BR>
LOG("COBass::GetLocation\n"); <BR>
if (pStr)<BR>
strcpy((char *)pStr, (LPCTSTR)m_zLocation);<BR>
return (HRESULT)NOERROR;<BR>
} <BR>
STDMETHODIMP COBass::SetLocation( char *pStr)<BR>
{<BR>
LOG("COBass::SetLocation\n"); <BR>
if (pStr)<BR>
strcpy(m_zLocation, (char *)pStr);<BR>
return (HRESULT)NOERROR;<BR>
} <BR>
STDMETHODIMP COBass::EatsOtherFish( BOOL *pBool )<BR>
{<BR>
LOG("COBass::EatsOtherFish\n"); <BR>
if (pBool)<BR>
{<BR>
*pBool = m_bEatsOtherFish;<BR>
return S_OK;<BR>
}<BR>
return (HRESULT)NOERROR; <BR>
}</TT></FONT></P>
<P>As shown in Listing 14.4, the interface implementations are straightforward C++
method implementations. Each method for the <TT>IFish</TT> and <TT>IBass</TT> interfaces
are implemented as a method of the <TT>COBass</TT> class. Unlike the MFC-based <TT>Cbass</TT>
implementation, there is no need to specify a specific interface for each method
since the <TT>COBass</TT> class is derived directly from the <TT>IFish</TT> and <TT>IBass</TT>
interfaces.</P>
<P>One final note on the <TT>COBass</TT> interface implementations concerns the <TT>AddRef</TT>
and <TT>Release</TT> methods implemented for the <TT>IUnknown</TT> interface. The
<TT>AddRef</TT> method simply increments a counter within the object, indicating
that another user is referencing the object.</P>
<P>When <TT>Release</TT> is called three actions are taken:
<UL>
<LI>The internal reference count is decremented.
<P>
<LI>If the reference count is zero, the main application server object is signaled
to decrement the global object counter.
<P>
<LI>If the internal reference count is zero, the object deletes itself; although
this procedure may seem unusual, it follows the COM rules that the object be responsible
for its own termination when it is no longer referenced.
</UL>
<P>The last piece of the <TT>COBass</TT> class is the unique <TT>CLSID</TT>. To create
the <TT>CLSID</TT>, run the tool GUIDGEN. Once the <TT>CLSID</TT> is created, it
must be placed in a header file that acts as a define for the class implementation.
For the <TT>COBass</TT> object, the file CUSTOMBASSID.H is created. The <TT>CLSID</TT>
is then pasted into the file and added to the macro <TT>DEFINE_GUID</TT> (see Listing
14.5).
<H3><A NAME="Heading10"></A>Listing 14.5<SPACER TYPE="HORIZONTAL" SIZE="10"> CUSTOMBASSID.H--Header
File CUSTOMBASSID.H, which Contains the Implementation of CLSID for the COBass Class</H3>
<P><FONT COLOR="#0066FF"><TT>#ifndef _CLSID_CustomBass<BR>
#define _CLSID_CustomBass <BR>
//{A1C19FC0-66D6-11d0-ABE6-D07900C10000}<BR>
DEFINE_GUID(CLSID_CustomBass,0xA1C19FC0,<BR>
0x66D6,0x11d0,0xAB,0xE6,0xD0,0x79,0x00,0xC1,0x00,0x00); <BR>
#endif</TT></FONT></P>
<P>The macro <TT>DEFINE_GUID</TT> assigns the name <TT>CLSID_CustomBass</TT> to the
class ID that was created via GUIDGEN. This macro is placed in a header file that
is used by all clients that need to invoke an instance of <TT>CLSID_CustomBass</TT>.
This file is <I>not</I> used by the server that implements the COM Object.
<H2><A NAME="Heading11"></A>Implementing the COBass Class Factory</H2>
<P>Now that the COM class <TT>COBass</TT> is implemented, a class factory that creates
the object must be implemented. A <I>class factory </I>is a COM interface (<TT>IClassFactory</TT>)
that is responsible for creating instances of <TT>COBass</TT>. When using a framework
such as MFC, the class factory for an object is implemented through the <TT>OLE_CREATE</TT>
macro. Without the benefit of a framework, this interface must be implemented by
the developer of the COM Object server.</P>
<P>The <TT>COBass</TT> class factory is implemented in the class <TT>CFBass</TT>
(see Listing 14.6).
<H3><A NAME="Heading12"></A>Listing 14.6<SPACER TYPE="HORIZONTAL" SIZE="10"> FACTORY.H--Class
Factory Definition File, FACTORY.H, for CFBass</H3>
<P><FONT COLOR="#0066FF"><TT>class CFBass : public IUnknown<BR>
{<BR>
public:<BR>
// Main Object Constructor & Destructor.<BR>
CFBass(IUnknown* pUnkOuter, CServer* pServer);<BR>
~CFBass(void); <BR>
// IUnknown methods. Main object, non-delegating.<BR>
STDMETHODIMP QueryInterface(REFIID, PPVOID);<BR>
STDMETHODIMP_(ULONG) AddRef(void);<BR>
STDMETHODIMP_(ULONG) Release(void); <BR>
private:<BR>
// We declare nested class interface implementations here. <BR>
// We implement the IClassFactory interface (ofcourse) in this class<BR>
// factory COM object class.<BR>
class CImpIClassFactory : public IClassFactory<BR>
{<BR>
public:<BR>
// Interface Implementation Constructor & Destructor.<BR>
CImpIClassFactory(<BR>
CFBass* pBackObj,<BR>
IUnknown* pUnkOuter,<BR>
CServer* pServer);<BR>
~CImpIClassFactory(void); <BR>
// IUnknown methods.<BR>
STDMETHODIMP QueryInterface(REFIID, PPVOID);<BR>
STDMETHODIMP_(ULONG) AddRef(void);<BR>
STDMETHODIMP_(ULONG) Release(void); <BR>
// IClassFactory methods.<BR>
STDMETHODIMP CreateInstance(IUnknown*, REFIID, PPVOID);<BR>
STDMETHODIMP LockServer(BOOL); <BR>
private:<BR>
// Data private to this interface implementation of IClassFactory.<BR>
ULONG m_cRefI; // Interface Ref Count (for debugging).<BR>
CFBass* m_pBackObj; // Parent Object back pointer.<BR>
IUnknown* m_pUnkOuter; // Outer unknown for Delegation.<BR>
CServer* m_pServer; // Server's control object.<BR>
}; <BR>
// Make the otherwise private and nested IClassFactory interface<BR>
// implementation a friend to COM object instantiations of this<BR>
// selfsame CFBass COM object class.<BR>
friend CImpIClassFactory; <BR>
// Private data of CFBass COM objects. <BR>
// Nested IClassFactory implementation instantiation.<BR>
CImpIClassFactory m_ImpIClassFactory; <BR>
// Main Object reference count.<BR>
ULONG m_cRefs; <BR>
// Outer unknown (aggregation & delegation). Used when this<BR>
// CFBass object is being aggregated. Otherwise it is used<BR>
// for delegation if this object is reused via containment.<BR>
IUnknown* m_pUnkOuter; <BR>
// Pointer to this component server's control object.<BR>
CServer* m_pServer;<BR>
}; <BR>
typedef CFBass* PCFBass;</TT></FONT></P>
<P>The class <TT>CFBass</TT> uses the technique of nested interfaces to implement
the <TT>IUnknown</TT> and the <TT>IClassFactory</TT> interfaces. With nested interfaces,
the second class definition, in this case <TT>CImpIClassFactory</TT>, is defined
<I>within</I> the definition of the class <TT>CFBass</TT>. This technique allows
the <TT>CImpIClassFactory</TT> object to be created when the constructor of <TT>CFBass</TT>
is called. Each class implements its own interface.</P>
<P>When the <TT>CFBass</TT> class constructor is called, the constructor for the
class member <TT>m_ImpIClassFactory</TT>, which is of class <TT>CImpIClassFactory</TT>,
is also called. Listing 14.7 illustrates the <TT>CFBass</TT> constructor as well
as the <TT>IUnknown</TT> interfaces implemented in <TT>CFBass</TT>. Notice that if
the <TT>IUnknown</TT> interface is called with an interface ID of <TT>IID_IClassFactory</TT>,
the address of member <TT>CImpIClassFactory</TT> is returned.
<H3><A NAME="Heading13"></A>Listing 14.7 <SPACER TYPE="HORIZONTAL" SIZE="10">FACTORY.CPP--Implementation
of the CFBass Class and IUnknown Interface</H3>
<P><FONT COLOR="#0066FF"><TT>#include <windows.h><BR>
#include <ole2.h><BR>
#include "comutil.h"<BR>
#include "server.h"<BR>
#include "factory.h"<BR>
#include "cobass.h" <BR>
CFBass::CFBass(<BR>
IUnknown* pUnkOuter,<BR>
CServer* pServer) :<BR>
m_ImpIClassFactory(this, pUnkOuter, pServer)<BR>
{<BR>
// Zero the COM object's reference count.<BR>
m_cRefs = 0; <BR>
// No AddRef necessary if non-NULL, as we're nested.<BR>
m_pUnkOuter = pUnkOuter; <BR>
// Init the pointer to the server control object.<BR>
m_pServer = pServer;<BR>
} <BR>
CFBass::~CFBass(void)<BR>
{<BR>
return;<BR>
} <BR>
STDMETHODIMP CFBass::QueryInterface(<BR>
REFIID riid,<BR>
PPVOID ppv)<BR>
{<BR>
HRESULT hr = E_NOINTERFACE;<BR>
*ppv = NULL; <BR>
if (IID_IUnknown == riid)<BR>
{<BR>
*ppv = this;<BR>
LOG("S: CFBass::QueryInterface. `this' pIUnknown returned.");<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -