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

📄 ch05.htm

📁 本书详述了VC++环境下网络化多媒体对象技术编程
💻 HTM
📖 第 1 页 / 共 4 页
字号:
*RetVal = VARIANT_FALSE; <BR>
<BR>
// return the result <BR>
return hResult; </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
} </TT></FONT></P>
<P>Now that you have added a method, you will learn how to implement its counterpart,
the Property.
<H2><A NAME="Heading13"></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>As with methods, properties are also added to a server via the ODL file. Listing
5.8 shows the ODL declaration for the <TT>Indent</TT> property. As stated in <A HREF="ch03.htm">Chapters
3</A> and <A HREF="ch04.htm">4</A>, properties are implemented as a pair of methods,
one to get the value and the other to set the value. Since the server is dual-interface
by default, the methods must also conform to dual-interface declaration rules.
<H3><A NAME="Heading14"></A>Listing 5.8 BCFSERVER.ODL--Indent Property Added to the
ODL</H3>
<P><FONT COLOR="#0066FF"><TT>. . . <BR>
<BR>
interface ITracker : IDispatch { <BR>
<BR>
// properties <BR>
[id(1), propget] HRESULT Indent([out, retval] long * Value); <BR>
[id(1), propput] HRESULT Indent([in] long Value); <BR>
<BR>
// methods <BR>
[id(2)] HRESULT OutputLines([in] VARIANT * varOutputArray, <BR>
[in, optional] VARIANT varIndent, [out, retval] boolean * RetVal); <BR>
}; </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
</TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
. . . </TT></FONT></P>
<P>Compile the ODL file, and copy the function prototypes from the BCFServerInterfaces.h
file to the <TT>Tracker</TT> class definition (see Listing 5.9). Don't forget to
remove the <TT>PURE</TT> keyword from the new function declarations.
<H3><A NAME="Heading15"></A>Listing 5.9 TRACKER.H--Indent Property Function Prototypes
Added to the Class Definition</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
. . . <BR>
<BR>
CTracker(IUnknown *); <BR>
virtual ~CTracker(); <BR>
<BR>
// ITracker methods <BR>
STDMETHOD(get_Indent)(long FAR* Value); <BR>
STDMETHOD(put_Indent)(long Value); <BR>
STDMETHOD(OutputLines)(VARIANT FAR* varOutputArray, VARIANT varIndent, VARIANT_BOOL
FAR* RetVal); <BR>
<BR>
// creation method <BR>
// <BR>
static IUnknown *Create(IUnknown *); </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
</TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
. . . </TT></FONT></P>
<P>The actual implementation of the <TT>Indent</TT> property is very simple (see
Listing 5.10). <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="Heading16"></A>Listing 5.10 TRACKER.CPP--Indent Property Implementation</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
STDMETHODIMP CTracker::get_Indent(long FAR *Value) <BR>
{ <BR>
HRESULT hResult = S_OK; <BR>
<BR>
// return the member variable <BR>
*Value = m_lIndent; <BR>
<BR>
// return the result <BR>
return hResult; <BR>
} <BR>
<BR>
STDMETHODIMP CTracker::put_Indent(long Value) <BR>
{ <BR>
HRESULT hResult = S_OK; <BR>
<BR>
// if the new value is a least 0 <BR>
if(Value &gt;= 0) <BR>
// assign the value to our member variable <BR>
m_lIndent = Value; <BR>
<BR>
// return the result <BR>
return hResult; </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><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="Heading17"></A>Generating OLE Exceptions</H2>
<P>While executing a method call or some other action, it is necessary at times 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. It is necessary
to halt further processing until the error can be resolved. This is known as an <I>exception</I>.
Any type of error can be treated as an exception; it depends on 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 <I>C++ exception. </I>A C++ exception is a language mechanism used
to create critical errors of the type described earlier and that are confined to
the application in which they are defined. The second is an <I>OLE exception. </I>OLE
exceptions 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 communicate errors 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, which is provided by the operating system. A server need only support the
<TT>ISupportErrorInfo</TT> interface, which lets an automation controller know that
it should look at the <TT>IErrorInfo</TT> object for more information 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 ODL file (see Listing 5.11). This step has the effect of publishing
the error constants to the user of the automation server. The enumeration can be
added anywhere within the ODL file and still produce the proper C++ declaration in
the BCFServerInterfaces.h file. Remember to generate a new <TT>CLSID</TT> for the
enumeration.
<H3><A NAME="Heading18"></A>Listing 5.11 BCFSERVER.ODL--Error Enumeration in the
ODL File</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
coclass Tracker { <BR>
[default] interface ITracker; <BR>
}; <BR>
<BR>
typedef [uuid(AA3DFE23-5C1C-11d0-BEE7-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><FONT COLOR="#0066FF"><TT><BR>
}; </TT></FONT></P>
<P>The next step is to add the actual error generating code. Listing 5.12 shows the
additional code that is added to the server implementation of <TT>OutputLines</TT>
and <TT>put_Indent</TT> to enable error generation.</P>
<P>BaseCtl provides a helper function <TT>Exception</TT> for generating rich error
information. This function takes three parameters: an error number, a string resource
ID, and a help context ID. See <A HREF="ch03.htm">Chapters 3</A> and <A HREF="ch04.htm">4</A>
for more information regarding rich error information.
<H3><A NAME="Heading19"></A>Listing 5.12 TRACKER.CPP--Rich Error Information Added
to Tracker Implementation</H3>
<P><FONT COLOR="#0066FF"><TT><BR>
STDMETHODIMP CTracker::OutputLines(VARIANT FAR* varOutputArray, VARIANT varIndent,
VARIANT_BOOL FAR* RetVal) <BR>
{ <BR>
HRESULT hResult = S_OK; <BR>
<BR>
*RetVal = VARIANT_TRUE; <BR>
<BR>
// if we have a file and 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>
<BR>
// get the lower bound of the array <BR>
if(::SafeArrayGetLBound(varOutputArray-&gt;parray, 1, &amp;lLBound) == S_OK) <BR>
{ <BR>
LONG lUBound; <BR>
<BR>
// get the number of elements in the array <BR>
if(::SafeArrayGetUBound(varOutputArray-&gt;parray, 1, &amp;lUBound) <BR>
== S_OK) <BR>
{ <BR>
. . . <BR>
else <BR>
{ <BR>
*RetVal = VARIANT_FALSE; </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
</TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
// create the error message <BR>
hResult = this-&gt;Exception( <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, <BR>
MFCSERVER_E_NO_UBOUND), <BR>
IDS_E_NO_UBOUND, <BR>
NULL); <BR>
} <BR>
} <BR>
else <BR>
{ <BR>
*RetVal = VARIANT_FALSE; <BR>
<BR>
// create the error message <BR>
hResult = this-&gt;Exception( <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, <BR>
MFCSERVER_E_NO_LBOUND), <BR>
IDS_E_NO_LBOUND, <BR>
NULL); <BR>
} <BR>
<BR>
// unlock the array we don't need it anymore <BR>
::SafeArrayUnlock(varOutputArray-&gt;parray); <BR>
} <BR>
else <BR>
{ <BR>
*RetVal = VARIANT_FALSE; <BR>
<BR>
// create the error message <BR>
hResult = this-&gt;Exception( <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, MFCSERVER_E_NO_ARRAYLOCK), <BR>
IDS_E_NO_ARRAYLOCK, <BR>
NULL); <BR>
} <BR>
} <BR>
else <BR>
{ <BR>
*RetVal = VARIANT_FALSE; <BR>
<BR>
// if there wasn't a file <BR>
if(!m_fileLog) <BR>
// create the error message <BR>
hResult = this-&gt;Exception( <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, MFCSERVER_E_NO_FILE), <BR>
IDS_E_NO_FILE, <BR>
NULL); <BR>
else <BR>
// create the error message <BR>
hResult = this-&gt;Exception( <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, <BR>
MFCSERVER_E_BAD_ARRAY_PARAMETER), <BR>
IDS_E_BAD_ARRAY_PARAMETER, <BR>
NULL); <BR>
} <BR>
<BR>
// return the result <BR>
return hResult; <BR>
} <BR>
<BR>
STDMETHODIMP CTracker::get_Indent(long FAR *Value) <BR>
{ <BR>
HRESULT hResult = S_OK; <BR>
<BR>
// return the member variable <BR>
*Value = m_lIndent; <BR>
<BR>
// return the result <BR>
return hResult; <BR>
} <BR>
<BR>
STDMETHODIMP CTracker::put_Indent(long Value) <BR>
{ <BR>
HRESULT hResult = S_OK; <BR>
<BR>
// if the new value is at least 0 <BR>
if(Value &gt;= 0) <BR>
// assign the value to our member variable <BR>
m_lIndent = Value; <BR>
else <BR>
{ <BR>
// create the error message <BR>
hResult = this-&gt;Exception( <BR>
MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, MFCSERVER_E_INVALID_VALUE), <BR>
IDS_E_INVALID_VALUE, <BR>
NULL); <BR>
} <BR>
<BR>
// return the result <BR>
return hResult; </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
} </TT></FONT></P>
<P>Take care when defining your interfaces and how they communicate errors and invalid
conditions. The use of rich error information is very helpful to the user of the
component and can be as critical to the implementation as the methods and properties
that the server supports.
<H2><A NAME="Heading20"></A>Dual-Interface</H2>
<P>In <A HREF="ch03.htm">Chapter 3</A>, the basic MFC server is implemented as <TT>IDispatch</TT>
only, and you are required to add dual-interface support as an extra step. With BaseCtl,
dual-interface support is built in and implemented as a normal aspect of the server.
<H2><A NAME="Heading21"></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 BaseCtl, 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 as in MFC.
<H2><A NAME="Heading22"></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, creating and using automation servers is necessary from within the application
in which they are defined. Take, for example, a case where an application contains
three servers, only one of which is 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 were 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> determined whether a server is creatable by external
applications. For BaseCtl, it is a little simpler. All BaseCtl applications contain
a global variable called <TT>ObjectInfo</TT> for declaring all of the servers that
can be created via OLE. The <TT>ObjectInfo</TT> structure is declared in the main
application file:</P>
<P><FONT COLOR="#0066FF"><TT><BR>
OBJECTINFO g_ObjectInfo[] = { <BR>
EMPTYOBJECT </TT></FONT></P>
<P><FONT COLOR="#0066FF"><TT><BR>
}; </TT></FONT></P>
<P>Each OLE server implemented within the application will have a single entry within
the body of the <TT>ObjectInfo</TT> structure, thus identifying it as an exposed
OLE server. The <TT>AUTOMATIONOBJECT</TT> macro defines the C++ class of the server
that can be created:</P>

⌨️ 快捷键说明

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