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

📄 ch04.htm

📁 本书详述了VC++环境下网络化多媒体对象技术编程
💻 HTM
📖 第 1 页 / 共 5 页
字号:
Constructor</H3>
<P><FONT COLOR="#0066FF"><TT>. . . <BR>
fprintf(m_fileLog, _T(&quot;Start %s\n&quot;), <BR>
(LPCTSTR) oTimeStamp.Format(&quot;%B %#d, %Y, %I:%M %p&quot;)); <BR>
fprintf(m_fileLog, _T(&quot;\n&quot;)); <BR>
} <BR>
m_lIndent = 0; } </TT></FONT></P>
<P>Next you add the <TT>OutputLines</TT> implementation to the source file (see Listing
4.11). The implementation varies very little from the MFC sample. The only obvious
difference is the return type of the function, which is now the last parameter of
the function.</P>
<P>As with the MFC implementation, the ATL version checks the array parameter to
ensure its validity and, if valid, outputs the data to the file, indenting the text
if appropriate. See <A HREF="ch03.htm">Chapter 3</A> for more information regarding
the other implementation details.</P>
<P>For now the implementation returns <TT>VARIANT_FALSE</TT> in the cases where an
error has occurred. Later in this chapter, you will learn how to create rich error
information.
<H3><A NAME="Heading22"></A>Listing 4.11 TRACKER.CPP--OutputLines Function Implementation
Added to the Source File Tracker.cpp</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
STDMETHODIMP CTracker::OutputLines(VARIANT * varOutputArray, VARIANT varIndent, <BR>
VARIANT_BOOL * RetVal) <BR>
{ <BR>
AFX_MANAGE_STATE(AfxGetStaticModuleState()) <BR>
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; <BR>
// get the lower bound of the array <BR>
if(::SafeArrayGetLBound(varOutputArray-&gt;parray, 1, &amp;lLBound) == S_OK) <BR>
{ <BR>
LONG lUBound; <BR>
// 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; <BR>
// if we have an indent parameter <BR>
if(varIndent.vt != VT_I4) <BR>
{ <BR>
// get a variant that we can use for conversion purposes VARIANT varConvertedValue;
<BR>
// initialize the variant <BR>
::VariantInit(&amp;varConvertedValue); <BR>
// see if we can convert the data type to something useful <BR>
// - 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 <BR>
m_lIndent = varIndent.lVal; <BR>
// 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;); <BR>
// 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(); <BR>
// 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); <BR>
// store the last timer value <BR>
m_lLastHiResTime = m_lHiResTime; <BR>
// free the bstr <BR>
::SysFreeString(bstrTemp); <BR>
} <BR>
} <BR>
} <BR>
else <BR>
*RetVal = VARIANT_FALSE; <BR>
} <BR>
else <BR>
*RetVal = VARIANT_FALSE; <BR>
// unlock the array we don't need it anymore <BR>
::SafeArrayUnlock(varOutputArray-&gt;parray); <BR>
} <BR>
else <BR>
*RetVal = VARIANT_FALSE; <BR>
} <BR>
else <BR>
*RetVal = VARIANT_FALSE; <BR>
// return the result <BR>
return hResult; } </TT></FONT></P>
<P>Now you have added a method. In the following section, you will learn how to implement
its counterpart, the property.
<H2><A NAME="Heading23"></A>Adding Properties</H2>
<P>A <I>property</I> can be thought of as an exposed variable that is defined in
the Automation Server. Properties are useful for setting and retrieving information
about the state of the server.</P>
<P>The <TT>m_lIndent</TT> member variable that you added to the class definition
is a perfect candidate to be exposed as a property.</P>
<P>Properties are added in much the same way as methods. From the ClassView tab in
the Project Workspace window, select the <TT>ITracker</TT> class, click the right
mouse button, and select the Add <U>P</U>roperty menu item (see fig. 4.9). <BR>
<BR>
<A HREF="art/04/dfig09.jpg"><B>FIG. 4.9</B></A> <I><BR>
Add a new property to the Server with the ATL ClassWizard.</I></P>
<P>In the Add Property to Interface dialog, set the Property <U>T</U>ype to <TT>long</TT>,
type the Property <U>N</U>ame as <TT>Indent</TT>, and leave the remainder of the
settings at their default values (see fig. 4.10). Click OK to confirm the entry and
close the dialog. <A HREF="art/04/dfig10.jpg"><B><BR>
<BR>
FIG. 4.10</B></A> <I><BR>
Define the <TT>Indent</TT> property attributes.</I></P>
<P>Like the <TT>OutputLines</TT> method, the ATL ClassWizard added entries to the
IDL file (see Listing 4.12), the Tracker.h header file (see Listing 4.13), and the
Tracker.cpp source file (see Listing 4.14) to support the new property. As in <A
HREF="ch03.htm">Chapter 3</A>, properties are added as a pair of related functions,
and the same is true for the ATL server and ActiveX components.
<H3><A NAME="Heading24"></A>Listing 4.12 TRACKER.IDL--Indent Property Added to the
IDL</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
. . . interface ITracker : IDispatch <BR>
{ <BR>
[id(1), helpstring(&quot;method OutputLines&quot;)] HRESULT OutputLines( <BR>
[in] VARIANT * varOutputArray, [in,optional] VARIANT <BR>
varIndent, [out,retval] VARIANT_BOOL * RetVal); <BR>
[propget, id(2), helpstring(&quot;property Indent&quot;)] HRESULT Indent( <BR>
[out, retval] long *pVal); <BR>
[propput, id(2), helpstring(&quot;property Indent&quot;)] HRESULT Indent( <BR>
[in] long newVal); <BR>
}; . . .</TT></FONT></P>
<H3><A NAME="Heading25"></A>Listing 4.13 TRACKER.H--Indent Property Function Pair
Prototypes Added to the Tracker.h File</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
. . . // ITracker <BR>
public: <BR>
STDMETHOD(get_Indent)(/*[out, retval]*/ long *pVal); <BR>
STDMETHOD(put_Indent)(/*[in]*/ long newVal); <BR>
STDMETHOD(OutputLines)(/*[in]*/ VARIANT * varOutputArray, <BR>
/*[in,optional]*/ VARIANT varIndent, /*[out,retval]*/ VARIANT_BOOL * RetVal); . .
. </TT></FONT></P>
<H3><A NAME="Heading26"></A>Listing 4.14 TRACKER.CPP--Indent Property Function Pair
Implementation Added to the Tracker.cpp File</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
STDMETHODIMP CTracker::get_Indent(long * pVal) <BR>
{ <BR>
AFX_MANAGE_STATE(AfxGetStaticModuleState()) // TODO: Add your implementation code
here return S_OK; <BR>
} STDMETHODIMP CTracker::put_Indent(long newVal) <BR>
{ <BR>
AFX_MANAGE_STATE(AfxGetStaticModuleState()) // TODO: Add your implementation code
here return S_OK; } </TT></FONT></P>
<P>The actual implementation of the <TT>Indent</TT> property is very simple (see
Listing 4.15). <TT>get_Indent</TT> returns the value currently stored in the member
variable, and <TT>put_Indent</TT> stores the new value, after a little bit of error
checking, in the member variable.
<H3><A NAME="Heading27"></A>Listing 4.15 TRACKER.CPP--Indent Property Implementation</H3>
<P><FONT COLOR="#0066FF"><TT><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; // return the result <BR>
return hResult; <BR>
}</TT></FONT></P>
<P>Properties, like methods, also have a wide variety of implementation options,
including parameterized and enumerated values. See <A HREF="ch06.htm">Chapters 6</A>
through <A HREF="ch11.htm">11</A> on developing ActiveX controls for descriptions
of more options and features when creating properties.</P>
<P>You've added methods and properties to the server, but you haven't really dealt
with the issue of error handling in their implementation. In some cases, simply returning
success or failure is not enough information for the developer to understand that
an error occurred and what caused it. You will communicate more error information
through the use of OLE exceptions.
<H2><A NAME="Heading28"></A>Generating OLE Exceptions</H2>
<P>While executing a method call or some other action, at times you will find it
necessary to terminate the process due to some critical error that has occurred or
is about to occur. For example, a method is called to write data to a file, but the
method cannot open the file because there is not enough room on the hard disk to
do so. You must halt further processing until the error can be resolved. An error
of this kind is known as an <I>exception. </I>Any type of error can be treated as
an exception; it depends upon the requirements of your application and how you choose
to deal with the errors that may result.</P>
<P>You must become familiar with two forms of exceptions when creating ActiveX components.
The first is a C++ exception. A <I>C++ exception</I> is a language mechanism used
to generate critical errors of the type described earlier and is confined to the
application in which they are defined. The second is an OLE exception. <I>OLE exceptions</I>
are used to communicate the same kinds of errors externally to applications that
are using a component. The difference between the two is that C++ exceptions are
used internally to an application's implementation and OLE exceptions are used externally
to other applications.</P>
<P>The COM implementation of a method in a dual-interface server does not have the
same kind of error management features that <TT>IDispatch</TT> interfaces have. To
generate the proper error information, an application must use the <TT>IErrorInfo</TT>
object provided by the oper- ating system. A server need support only the <TT>ISupportErrorInfo</TT>
interface, which lets an automation controller know that it should look at the <TT>IErrorInfo</TT>
object for more infor-mation when an error occurs.</P>
<P>The first step is to add an enumeration of the types of errors that the server
can generate to the IDL file (see Listing 4.16). This step has the effect of publishing
the error constants to the user of the automation server. Unlike the <TT>ITracker</TT>
interface, the enumeration can be added anywhere within the IDL file and still produce
the proper C++ declaration in the ATLServer.h file. Remember to generate a new <TT>CLSID</TT>
for the enumeration using the GUIDGEN.EXE program. See <A HREF="ch02.htm">Chapter
2</A> for more information on how to use the GUIDGEN program.
<H3><A NAME="Heading29"></A>Listing 4.16 ATLSERVER.IDL--Error Enumeration Added to
the IDL File</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
. . . coclass CTracker <BR>
{ <BR>
[default] interface ITracker; <BR>
}; typedef [uuid(2B2AF9C9-5452-11D0-BEDE-00400538977D), <BR>
helpstring(&quot;Tracker Error Constants&quot;)] <BR>
enum tagTrackerError <BR>
{ <BR>
MFCSERVER_E_NO_UBOUND = 46080, <BR>
MFCSERVER_E_NO_LBOUND = 46081, <BR>
MFCSERVER_E_NO_ARRAYLOCK = 46082, <BR>
MFCSERVER_E_NO_FILE = 46083, <BR>
MFCSERVER_E_BAD_ARRAY_PARAMETER = 46084, <BR>
MFCSERVER_E_INVALID_VALUE = 46085 <BR>
}TRACKERERROR; }; </TT></FONT></P>
<P>Since ATL servers are dual-interface by default, you must implement all errors
using OLE rich error information and not with C++ exceptions.</P>
<P>The next step is to add the actual error-generating code (see Listing 4.17). ATL
provides the helper function <TT>AtlReportError</TT> for generating rich error information.
The function accepts four parameters: the <TT>CLSID</TT> of the server, an error
message, the <TT>IID</TT> of the interface, and the error code. Error codes must
be formatted error codes, as in the implementation, or a predefined OLE error codes;
simply returning <TT>S_FALSE</TT> is not enough to generate an error. See the VC++
books online for more information regarding OLE error codes and their use.
<H3><A NAME="Heading30"></A>Listing 4.17 TRACKER.CPP--Rich Error Information Added
to Tracker Implementation</H3>

⌨️ 快捷键说明

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