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

📄 ch04.htm

📁 本书详述了VC++环境下网络化多媒体对象技术编程
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<P>
<P><FONT COLOR="#0066FF"><TT><BR>
STDMETHODIMP CTracker::OutputLines(VARIANT * varOutputArray, <BR>
VARIANT varIndent, VARIANT_BOOL * RetVal) <BR>
{ <BR>
AFX_MANAGE_STATE(AfxGetStaticModuleState()) HRESULT hResult = S_OK; *RetVal = VARIANT_TRUE;
// if we have a file a if the variant contains a string array <BR>
if(m_fileLog &amp;&amp; varOutputArray-&gt;vt == (VT_ARRAY | VT_BSTR)) <BR>
{ <BR>
// lock the array so we can use it <BR>
if(::SafeArrayLock(varOutputArray-&gt;parray) == S_OK) <BR>
{ <BR>
LONG lLBound; // get the lower bound of the array <BR>
if(::SafeArrayGetLBound(varOutputArray-&gt;parray, 1, &amp;lLBound) == S_OK) <BR>
{ <BR>
LONG lUBound; // get the number of elements in the array <BR>
if(::SafeArrayGetUBound(varOutputArray-&gt;parray, 1, &amp;lUBound) <BR>
== S_OK) <BR>
{ <BR>
CString cstrIndent; <BR>
CTime oTimeStamp; <BR>
BSTR bstrTemp; // if we have an indent parameter <BR>
if(varIndent.vt != VT_I4) <BR>
{ <BR>
// get a variant that we can use <BR>
// for conversion purposes <BR>
VARIANT varConvertedValue; // initialize the variant <BR>
::VariantInit(&amp;varConvertedValue); // see if we can convert the data type to
something <BR>
// useful - VariantChangeTypeEx() could also be used <BR>
if(S_OK == ::VariantChangeType (&amp;varConvertedValue, <BR>
(VARIANT *) &amp;varIndent, 0, VT_I4)) <BR>
// assign the value to our member variable <BR>
m_lIndent = varConvertedValue.lVal; <BR>
} <BR>
else <BR>
// assign the value to our member variable m_lIndent = varIndent.lVal; // if we have
to indent the text <BR>
for(long lIndentCount = 0; lIndentCount &lt; m_lIndent; <BR>
lIndentCount++) <BR>
// add a tab to the string <BR>
cstrIndent += _T(&quot;\t&quot;); // for each of the elements in the array <BR>
for(long lArrayCount = lLBound; lArrayCount &lt; <BR>
(lUBound + lLBound); lArrayCount++) <BR>
{ <BR>
// update the time <BR>
oTimeStamp = CTime::GetCurrentTime(); <BR>
m_lHiResTime = timeGetTime(); // get the data from the array <BR>
if(::SafeArrayGetElement (varOutputArray-&gt;parray, <BR>
&amp;lArrayCount, &amp;bstrTemp) == S_OK) <BR>
{ <BR>
// output the data <BR>
fprintf(m_fileLog, _T(&quot;%s(%10ld)-%s%ls\n&quot;), <BR>
(LPCTSTR) oTimeStamp.Format (&quot;%H:%M:%S&quot;), <BR>
m_lHiResTime - m_lLastHiResTime, <BR>
(LPCTSTR) cstrIndent, bstrTemp); // store the last timer value <BR>
m_lLastHiResTime = m_lHiResTime; // free the bstr <BR>
::SysFreeString(bstrTemp); <BR>
} <BR>
} <BR>
} <BR>
else <BR>
{ <BR>
*RetVal = VARIANT_FALSE; // create the error message <BR>
hResult = AtlReportError(CLSID_Tracker, <BR>
&quot;Unable to retrieve the upper bound dimension of the array.&quot;, <BR>
IID_ITracker, <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, <BR>
MFCSERVER_E_NO_UBOUND)); <BR>
} <BR>
} <BR>
else <BR>
{ <BR>
*RetVal = VARIANT_FALSE; // create the error message <BR>
hResult = AtlReportError(CLSID_Tracker, <BR>
&quot;Unable to retrieve the lower bound dimension of the array.&quot;, <BR>
IID_ITracker, <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, <BR>
MFCSERVER_E_NO_LBOUND)); <BR>
} // unlock the array we don't need it anymore <BR>
::SafeArrayUnlock(varOutputArray-&gt;parray); <BR>
} <BR>
else <BR>
{ <BR>
*RetVal = VARIANT_FALSE; // create the error message <BR>
hResult = AtlReportError(CLSID_Tracker, <BR>
&quot;Unable to lock the array memory.&quot;, <BR>
IID_ITracker, <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, <BR>
MFCSERVER_E_NO_ARRAYLOCK)); <BR>
} <BR>
} <BR>
else <BR>
{ <BR>
*RetVal = VARIANT_FALSE; // if there wasn't a file <BR>
if(!m_fileLog) <BR>
// create the error message <BR>
hResult = AtlReportError(CLSID_Tracker, <BR>
&quot;Invalid File Handle. File could not be opened for output.&quot;, <BR>
IID_ITracker, <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, MFCSERVER_E_NO_FILE)); <BR>
else <BR>
// create the error message <BR>
hResult = AtlReportError(CLSID_Tracker, <BR>
&quot;The first parameter must be a string array passed by reference.&quot;, <BR>
IID_ITracker, <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, <BR>
MFCSERVER_E_BAD_ARRAY_PARAMETER)); <BR>
} // return the result <BR>
return hResult; <BR>
} STDMETHODIMP CTracker::get_Indent(long * pVal) <BR>
{ <BR>
AFX_MANAGE_STATE(AfxGetStaticModuleState()) HRESULT hResult = S_OK; // return the
member variable <BR>
*pVal = m_lIndent; // return the result <BR>
return hResult; <BR>
} STDMETHODIMP CTracker::put_Indent(long newVal) <BR>
{ <BR>
AFX_MANAGE_STATE(AfxGetStaticModuleState()) HRESULT hResult = S_OK; // if the new
value is a least 0 <BR>
if(newVal &gt;= 0) <BR>
// assign the value to our member variable <BR>
m_lIndent = newVal; <BR>
else <BR>
{ <BR>
// create the error message <BR>
hResult = AtlReportError(CLSID_Tracker, <BR>
&quot;Invalid value. Value must be 0 or greater.&quot;, <BR>
IID_ITracker, <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, MFCSERVER_E_INVALID_VALUE)); <BR>
} // return the result <BR>
return hResult; } </TT></FONT></P>
<P>The use of C++ exceptions is still permitted with an ATL-implemented server. But
the exception cannot cross application boundaries, which is the case in any application
whether implemented in MFC, ATL, or some other framework.
<H2><A NAME="Heading32"></A>Dual-Interface</H2>
<P>In <A HREF="ch03.htm">Chapter 3</A>, the basic MFC server is implemented as <TT>IDispatch</TT>
only. You are required to add dual-interface support as an extra step. With the ATL,
dual-interface support is built-in and implemented as a normal aspect of the server.
<H2><A NAME="Heading33"></A>Generating Dual-Interface OLE Exceptions</H2>
<P>Again, as in <A HREF="ch03.htm">Chapter 3</A>, the basic MFC server is implemented
as <TT>IDispatch</TT> only, and it is possible to throw standard C++ exceptions and
have the basic MFC <TT>IDispatch</TT> support code translate the error into an OLE
exception. When an MFC server is converted to dual-interface, you must implement
the exception translation code yourself.</P>
<P>For ATL, the server has been implemented as dual-interface from the start, and
all error generation has been written as true OLE exceptions and does not require
translation.
<H2><A NAME="Heading34"></A>Server Instantiation Using C++</H2>
<P>OLE is not the only method for creating and using Automation Servers. This chapter
will show you how to create OLE servers using C++ syntax.</P>
<P>At times, you must create and use Automation Servers from within the application
in which they are defined. Take, for example, a case where an application contains
three servers, with only one being directly creatable by outside applications using
OLE. The remaining two servers can be created by the exposed server using C++ and
returned via a method call to another application, which then uses the server as
though it was created via OLE.</P>
<P>For an MFC server, the inclusion or exclusion of the macros <TT>DECLARE_OLECREATE</TT>
and <TT>IMPLEMENT_OLECREATE</TT> determines whether a server is creatable by external
applications. For ATL, it is a little simpler. All ATL applications contain a global
variable called an <TT>ObjectMap</TT> for declaring all of the servers that can be
created via OLE. The <TT>ObjectMap</TT> is declared in the main application file
(see the file ATLServer.cpp) as a pair of macros:</P>
<P><FONT COLOR="#0066FF"><TT>BEGIN_OBJECT_MAP(ObjectMap)<BR>
END_OBJECT_MAP() </TT></FONT>Each OLE server implemented within the application will
have a single entry within the body of the <TT>ObjectMap</TT> macro, thus identifying
the server as an exposed OLE server. The <TT>OBJECT_ENTRY</TT> macro defines the
<TT>CLSID</TT> and the C++ class of the server that can be created.</P>
<PRE></PRE>
<P><FONT COLOR="#0066FF"><TT>BEGIN_OBJECT_MAP(ObjectMap) <BR>
OBJECT_ENTRY(CLSID_Tracker, CTracker) END_OBJECT_MAP() </TT></FONT></P>
<P>To prevent an application from being exposed as an Automation Server, you just
remove or comment out the entry in the object map. When adding additional servers
to an application, you must add an entry for each new server to the <TT>ObjectMap</TT>
macro.</P>
<P>All ATL servers contain a static function <TT>CreateInstance</TT>, which is used
to instantiate instances of themselves. You must use only the <TT>CreateInstance</TT>
function to instantiate a server since it is implemented by the class factory of
the server and will manage the server instantiation correctly. This is very critical
in the cases where the server is shared among two or more applications, as you will
see in the following sections.</P>
<P>The first step is to declare the pointer to which you will store the reference
of the object when it is created. Because ATL servers are implemented using templates,
it may seem a little strange to declare the reference this way. However, this makes
sense when you see the architecture of ATL, which is documented fully in the VC++
books online.</P>
<P><FONT COLOR="#0066FF"><TT><BR>
CComObject&lt;CTracker&gt; * opTracker; </TT></FONT></P>
<P>The next step is to instantiate the server and store the reference of the new
object. Remember to check the return value of the function and the pointer to ensure
that the object was instantiated successfully.</P>
<P><FONT COLOR="#0066FF"><TT><BR>
CComObject&lt;CTracker&gt;::CreateInstance(&amp;opTracker); </TT></FONT></P>
<P>Once a server is instantiated this way, you can use it like any other C++ class
or OLE server. You can use <TT>QueryInterface</TT> to retrieve <TT>IDispatch</TT>
or custom interface pointers that can be passed to other applications. Refer to <A
HREF="ch03.htm">Chapter 3</A> for more information regarding the instantiation and
use of OLE servers with C++.</P>
<P>So far, you've only looked at how to create individual instances of objects. In
the following section, you will learn how to share objects.
<H2><A NAME="Heading35"></A>Shared Servers</H2>
<P>OLE defines a facility for sharing objects called the <I>Running Object Table.
</I>Essentially, a shareable object will publish its <TT>CLSID</TT> and an <TT>IUnknown</TT>
reference to itself in the Running Object Table. Any application that so desires
can ask for the running instance of the object rather than create a new instance.
Applications that may need to work with a single running instance of an application
may find it more useful to use shared objects than to create multiple copies. The
<TT>Tracker</TT> object is a perfect candidate for this kind of functionality. Multiple
applications could use the same <TT>Tracker</TT> object to log information, thus
saving on memory.</P>
<P>Unfortunately, the way ATL is implemented prevents you from adding shared object
support without actually creating new ATL template classes. This limitation occurs
because of dependence on the <TT>Release</TT> function implementation to revoke the
object from the Running Object Table, which you cannot override directly in the base
ATL classes.</P>
<P>Listing 4.18 shows the support code that has been added to the StdAfx.h file to
support shared objects. The new classes and macros are based on the original ATL
code and have been extended to register the server in the Running Object Table. The
code will also remove the server from the Running Object Table when the reference
count reaches 1.</P>
<P>The only real change made to the original ATL code is that a new class <TT>CComObjectShared</TT>
is added with an extra template parameter of the <TT>CLSID</TT> of the server. The
remaining changes to the code and macros are to reflect the use of the new class
versus its original implementation <TT>CComObject</TT>. Do note that the shared server
implementation is simple and does not support aggregatable objects. But that is not
to say it cannot be implemented; it just wasn't done for this sample.</P>
<P>The constructor of the <TT>CComObjectShared</TT> class adds the <TT>IUnknown</TT>
reference to the Running Object Table and stores the ID in a member variable to be
used later when revoking the server.</P>
<P>The <TT>Release</TT> function is implemented the same as the MFC sample in that
the <TT>Release</TT> implementation revokes the server from the Running Object Table.
The code must also protect the <TT>Release</TT> call by bumping up the reference
count of the server and clearing the member variable to prevent recursion.
<H3><A NAME="Heading36"></A>Listing 4.18 STDAFX.H--Shared Object Support Classes
and Macros</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
// ****** ATL 2.0 version - Added by Jerry Anderson for shared object support <BR>
// ** <BR>
#define DECLARE_NOT_AGGREGATABLE_SHARED(cBase, clsid) public:\ <BR>
typedef CComCreator2&lt; CComCreator&lt; CComObjectShared&lt;cBase, clsid&gt; &gt;,
CComFailCreator&lt;CLASS_E_NOAGGREGATION&gt; &gt; _CreatorClass; // if this object
was registered and the refcount is 1 (which is from the &quot;RegisterActiveObject&quot;)
<BR>
// then revoke the registration so the object can be destroyed properly - The AddRef/Release
pair <BR>
// is to protect the destruction and prevent the object from being deleted before
we are out of this call <BR>
// since the RevokeActiveObject is going to call &quot;Release&quot; also and the
refcount would be 0 if we didn't AddRef #define RELEASE_AND_D

⌨️ 快捷键说明

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