Implementing
Additional Interfaces
Interfaces are the core of COM, and most COM
objects implement more than one. Even the wizard-generated ATL
Simple Object, shown earlier, implements four interfaces (one
custom interface and three standard interfaces). If you want your
ATL-based COM class to implement another interface, you must first
have a definition of it. For example, you can add the following
interface definition to your project's IDL file:
[
object,
uuid("27ABEF5D-654F-4D85-81C7-CC3F06AC5693"),
helpstring("IAdvertiseMyself Interface"),
pointer_default(unique)
]
interface IAdvertiseMyself : IUnknown {
[helpstring("method ShowAd")]
HRESULT ShowAd(BSTR bstrClient);
};
To implement this interface in your project, you
simply add the new interface to your C++ class inheritance list and
add the interface to the COM_MAP:
class ATL_NO_VTABLE CCalcPi :
public ICalcPi,
public IAdvertiseMyself {
BEGIN_COM_MAP(CCalcPi)
COM_INTERFACE_ENTRY(ICalcPi)
COM_INTERFACE_ENTRY(IAdvertiseMyself)
...
END_COM_MAP()
If methods in the IAdvertiseMyself
interface need to throw COM exceptions, the generated
implementation of ISupportErrorInfo must be modified as
well. This is accomplished by simply adding the IID to the array in
the generated implementation:
STDMETHODIMP CCalcPi::InterfaceSupportsErrorInfo(REFIID riid) {
static const IID* arr[] = {
&IID_ICalcPi,
&IID_IAdvertiseMyself
};
for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++) {
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}
After you make the updates, you have to implement
the new interface's methods.
STDMETHODIMP CCalcPi::ShowAd(BSTR bstrClient) {
CComBSTR bstrCaption = OLESTR("CalcPi hosted by ");
bstrCaption += (bstrClient && *bstrClient ?
bstrClient : OLESTR("no one"));
CComBSTR bstrText =
OLESTR("These digits of pi brought to you by CalcPi!");
MessageBox(0, COLE2CT(bstrText), COLE2CT(bstrCaption),
MB_SETFOREGROUND);
return S_OK;
}
VS provides a convenient wizard to make this
process simpler. Right-clicking the class from Class View and
selecting Implement Interface from the Add submenu brings up the
Implement Interface Wizard, shown in Figure 1.10. This wizard enables you to implement
interfaces defined in an existing type library. The wizard is smart
enough to pull type libraries from the current project.
Alternatively, you can define interfaces in IDL, compile them using
MIDL, and implement those interfaces by referencing the resulting
type library. The radio buttons enable you to use the type library
from the current project, registered type libraries (Registry), or
unregistered type libraries (File) that you locate with the Browse
button. For our PiSvr project, the type library built from the
generated IDL makes the interfaces we've defined available to the
Implement Interface Wizard.
Note that interfaces that have already been
implemented (ICalcPi, in our case) do not appear in the
list of implementable interfaces. Unfortunately, the Implement
Interface Wizard does not support interfaces that don't exist in
type libraries; this leaves out most of the standard COM
interfaces, such as IPersist, IMarshal, and
IOleItemContainer.
Unfortunately, there's a bug in this wizard. The
wizard did this to our base class list:
class ATL_NO_VTABLE CCalcPi :
... the usual stuff ...
public IDispatchImpl<ICalcPi, &IID_ICalcPi, &LIBID_PiSvrLib,
/*wMajor =*/ 1, /*wMinor =*/ 0>,
public IDispatchImpl<IAdvertiseMyself,
&__uuidof(IAdvertiseMyself), &LIBID_PiSvrLib,
/* wMajor = */ 1, /* wMinor = */ 0>
{
...
The added code is in bold. The wizard added the
IDispatchImpl template as a base class. This is used when
implementing dual interfaces. IAdvertiseMyself is
not a dual interface. The wizard
should have just derived from the interface directly. The fix is
easy: Change the previous bold line to this:
Even with this bug, the
Implement Interface Wizard is still worth using for large
interfaces. In addition to updating the base class list and the
COM_MAP, the wizard provides skeleton implementation for
all the methods in the interface; for a large interface, this can
save a ton of typing. Unfortunately, the skeletons are added only
to the header file, not to the .cpp file.
For more information about the various ways that
ATL allows your COM classes to implement interfaces, see Chapter 6, "Interface Maps."
For more information about CCom-BSTR and the
string-conversion routines used in the ShowAd method, see
Chapter 2, "Strings
and Text."
|