previous page
next page

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.

Figure 1.10. 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:

public IAdvertiseMyself

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."


previous page
next page
Converted from CHM to HTML with chm2web Pro 2.75 (unicode)