📄 ch03.htm
字号:
<BR>
// set the source file <BR>
pOleDispExcep->m_strSource = __FILE__; <BR>
// format the error description <BR>
pOleDispExcep->m_strDescription = _T("Unable to retrieve the lower bound
dimension of the array."); <BR>
// the function call failed cause an ole exception <BR>
throw(pOleDispExcep); <BR>
} <BR>
// unlock the array we don't need it anymore <BR>
::SafeArrayUnlock(varOutputArray->parray); <BR>
} <BR>
else <BR>
{ <BR>
bResult = VARIANT_FALSE; <BR>
// unable to get a record based on the sql statement - throw an exception <BR>
COleDispatchException * pOleDispExcep = new COleDispatchException(_T(""),
NULL, 0); <BR>
// format the error code <BR>
pOleDispExcep->m_scError = MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, MFCSERVER_E_NO_ARRAYLOCK);
<BR>
// set the source file <BR>
pOleDispExcep->m_strSource = __FILE__; <BR>
// format the error description <BR>
pOleDispExcep->m_strDescription = _T("Unable to lock the array memory.");
<BR>
// the function call failed cause an ole exception <BR>
throw(pOleDispExcep); <BR>
} <BR>
} <BR>
else <BR>
{ <BR>
bResult = VARIANT_FALSE; <BR>
// unable to get a record based on the sql statement - throw an exception <BR>
COleDispatchException * pOleDispExcep = new COleDispatchException(_T(""),
NULL, 0); <BR>
// if we didn't have a file handle <BR>
if(!m_fileLog) <BR>
// format the error code <BR>
pOleDispExcep->m_scError = MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, MFCSERVER_E_NO_FILE);
<BR>
else <BR>
// format the error code <BR>
pOleDispExcep->m_scError = MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, MFCSERVER_E_BAD_ARRAY_PARAMETER);
<BR>
// set the source file <BR>
pOleDispExcep->m_strSource = __FILE__; <BR>
// if we didn't have a file handle <BR>
if(!m_fileLog) <BR>
// format the error description <BR>
pOleDispExcep->m_strDescription = _T("Invalid File Handle. File could not
be opened for output."); <BR>
else <BR>
// format the error description <BR>
pOleDispExcep->m_strDescription = _T("The first parameter must be a string
array passed by reference."); <BR>
// the function call failed cause an ole exception <BR>
throw(pOleDispExcep); <BR>
} <BR>
// return the result <BR>
return bResult; <BR>
} <BR>
long CTracker::GetIndent() <BR>
{ <BR>
// return the member variable <BR>
return m_lIndent; <BR>
} <BR>
void CTracker::SetIndent(long nNewValue) <BR>
{ <BR>
// if the new value is a least 0 <BR>
if(nNewValue >= 0) <BR>
// assign the value to our member variable <BR>
m_lIndent = nNewValue; <BR>
else <BR>
{ <BR>
// unable to get a record based on the sql statement - throw an exception <BR>
COleDispatchException * pOleDispExcep = new <BR>
<BR>
<BR>
<BR>
COleDispatchException(_T(""), NULL, 0); <BR>
// format the error code <BR>
pOleDispExcep->m_scError = MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, MFCSERVER_E_INVALID_VALUE);
<BR>
// set the source file <BR>
pOleDispExcep->m_strSource = __FILE__; <BR>
// format the error description <BR>
pOleDispExcep->m_strDescription = _T("Invalid value. Value must be 0 or greater.");
<BR>
// the function call failed cause an ole exception <BR>
throw(pOleDispExcep); <BR>
} <BR>
<BR>
} </TT></FONT></P>
<P>Exceptions are useful for communicating error conditions and problems back to
the application and developer who are using an ActiveX component. Make use of them
whenever you can to further enhance your implementation.
<H2><A NAME="Heading24"></A>Dual-Interface</H2>
<P>Dual-interface is exactly what it sounds like: The server implementation supports
two interfaces with which to talk to the server. One interface, an IDispatch<I> interface,</I>
is what you have been working with so far. The other, a <I>custom interface, </I>is
a type of interface that you have not looked at yet. The dual portion refers to the
fact that no matter which interface you choose, you are always talking to the same
server, and you will always get the same response.</P>
<P>An <TT>IDispatch</TT>-based interface uses a generic mechanism for calling methods
and properties in a server. When a method in a server is called, you pass the ID
of the method to invoke and a structure describing its parameters and return type.
This data is packaged and sent to the server, which unpackages the data and calls
the appropriate method based on the ID supplied.</P>
<P>A custom interface, on the other hand, is very different. When using a custom
interface, you are talking directly to the server's functions and are not depending
on a generic mechanism for invoking the methods or properties. The packaging of the
parameters and return value are left to the compiler that created the applications.</P>
<P>Since the interfaces are written to access the same set of functions, the custom
interface portion of a dual-interface server must conform to the same data type restrictions
imposed by Automation. This way, you are not required to create your own code to
transfer data between the two applications: the controller and the server. OLE does
that for you with standard marshaling.</P>
<P>The major advantage to dual-interface is performance. The number of steps to call
a method using the custom interface is far less than the number needed to call a
method when using the <TT>IDispatch</TT> interface.</P>
<P>The main disadvantage to dual-interface support in MFC servers is that they are
not supported by the ClassWizard and will require manual changes to implement. The
code involved is not too difficult to maintain; the hardest part is probably remembering
to do it. <BR>
<BR>
<IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>
<BLOCKQUOTE>
<P><B>NOTE:</B> The advantage of using the custom interface of a dual-interface server
loses its luster when executing across process boundaries. The custom interface is
still faster, but not by much. The real performance benefit, a 25 to 50 percent improvement,
is when the server is in-process to the calling application. The amount of improvement
depends on the number and types of parameters that are being passed between the applications.
If you are interested in seeing actual numbers regarding the amount of performance
improvement, you can refer to recent Microsoft Systems Journal articles, which have
focused on performance differences between <TT>IDispatch</TT> and custom interfaces.
</BLOCKQUOTE>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"><BR>
<BR>
The first step when converting an MFC-based ActiveX server to dual-interface is to
change the ODL file. Listing 3.14 shows the changes that have been made to the server
to support dual-interface. It is not necessary to generate new <TT>UUID</TT>s because
the functionality of the server has not changed. You must add the <TT>oleautomation</TT>
and <TT>dual</TT> attributes to the interface class, though. You also add the <TT>hidden</TT>
attribute, so this interface will not be visible within VB. This is fine since VB
will display the <TT>CoClass</TT> interface and will also show all of the methods
and properties for the server. As a general rule, you should always hide your interfaces
and leave visible the <TT>CoClasses</TT> used to create them. This is because applications
like VB will display both interfaces, and the reality is that only the <TT>CoClass</TT>
name is valid in VB; if you try to reference an object by its interface name, you
will get an error.</P>
<P>Since the server supports dual-interface, you must change your interface declaration
to inherit from the <TT>IDispatch</TT> interface rather than declare the interface
as type <TT>dispinterface</TT>, as in the original implementation. Dual-interface
method and property declarations are different from <TT>dispinterface</TT> declarations,
which are more like standard C++. Note that keywords such as <TT>method</TT> and
<TT>properties</TT> are no longer within the interface declaration. Those terms are
keywords related to the <TT>dispinterface</TT> keyword. As we stated earlier, properties
are accessed using a pair of methods sharing the same dispid. The distinguishing
factors are the method attributes <TT>propget</TT> and <TT>propput,</TT> which denote
the direction of data flow. You must also change the <TT>CoClass</TT> to refer to
interface and not <TT>dispinterface</TT>.</P>
<P>All dual-interface methods must return an <TT>HRESULT</TT> data type. If a method
requires a return value, it must be specified as the last parameter of the method
and must have the parameter attributes of <TT>out</TT> and <TT>retval</TT>. All parameters
must have an attribute describing the direction of data flow. See Table 3.3 for a
complete description of the possible attributes and combinations.
<TABLE BORDER="1" WIDTH="100%">
<CAPTION><B>Table 3.3</B><SPACER TYPE="HORIZONTAL" SIZE="10"><B> Parameter Flow Attributes</B></CAPTION>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><B>Direction</B></TD>
<TD ALIGN="LEFT" VALIGN="TOP"><B>Description</B></TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>in</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">Parameter is passed from caller to callee.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>out</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">Parameter is returned from callee to caller.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>in</TT>, <TT>out</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">Parameter is passed from caller to callee, and the callee returns a parameter.</TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><TT>out</TT>, <TT>retval</TT></TD>
<TD ALIGN="LEFT" VALIGN="TOP">Parameter is the return value of the method and is returned from the callee to the
caller.</TD>
</TR>
</TABLE>
<H3><A NAME="Heading25"></A>Listing 3.14 MFCSERVER.ODL--ODL Changes to Support Dual-Interface</H3>
<P><FONT COLOR="#0066FF"><TT>[ uuid(11C82943-4EDD-11D0-BED8-00400538977D), version(1.0)
] <BR>
library MFCServer <BR>
{ <BR>
importlib("stdole32.tlb"); <BR>
<BR>
[ uuid(11C82946-4EDD-11D0-BED8-00400538977D), oleautomation, dual, hidden ] <BR>
interface ITracker: IDispatch <BR>
{ <BR>
[id(1), propget] HRESULT Indent([out, retval] long * Value); <BR>
[id(1), propput] HRESULT Indent([in] long Value); <BR>
[id(2)] HRESULT OutputLines([in] VARIANT * varOutputArray, [in, optional] VARIANT
varIndent, [out, retval] boolean * RetVal); <BR>
}; <BR>
// CoClass for CTracker <BR>
[ uuid(11C82947-4EDD-11D0-BED8-00400538977D) ] <BR>
coclass TRACKER <BR>
{ <BR>
[default] interface ITracker; <BR>
}; <BR>
typedef [uuid(11C82948-4EDD-11D0-BED8-00400538977D), helpstring("Tracker Error
Constants")] <BR>
#include "trackererror.h" <BR>
//{{AFX_APPEND_ODL}} <BR>
<BR>
}; </TT></FONT></P>
<P>The remainder of the ODL file entries do not have to be changed to support dual-interface.
<BR>
<BR>
<IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>
<BLOCKQUOTE>
<P><B>NOTE:</B> The changes that have been made to the ODL file will prevent the
ClassWizard from updating the ODL file automatically when new methods and properties
are added. You are now responsible for maintaining the entries manually.
</BLOCKQUOTE>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"><BR>
<BR>
The ODL compiler has the capability of generating a C++ header file that describes
all of the interfaces and enumerations that are contained in the type library that
you've created for your server. The ODL-generated header file is useful for creating
function prototypes that are required in the implementation of the server. You add
the entry to the ODL file and compile it into a type library. Next copy the new method
from the header file into your class definition and make some minor changes--and
everything is finished. In addition, you now have an interface file that can be used
by other applications to access the custom interface of your server as well as t
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -