Instead of adding COM support programmatically through C++ templates, you can employ a more declarative approach using attributed programming. Whereas classic ATL programming involves acquiring IUnknown support through template classes and interface map macros, attributed programming simply involves declaring a class as a COM class directly in the source code.
Let's create the same spaceship server using attributed programming. First, create a new ATL project, and this time select the Attributed check box on the Application Settings page of the ATL Project Wizard. Then use the ATL Simple Object Wizard to add an attributed class. Call the class AttributedATLSpaceship. When you page through the class options, you'll notice that the options are the same. That is, your class might be Apartment Threaded, Free Threaded, Both, or Neutral. You can also create support for ISupportErrorInfo and enable connection points.
However, when you look at the code emitted from the wizard, it will look quite a bit different from classic ATL-based code. Here's what you'll get:
// IAttributedATLSpaceShip [ object, uuid("4B8685BD-00F1-4D38-AFC1-3012C786480D"), dual, helpstring("IAttributedATLSpaceShip Interface"), pointer_default(unique) ] __interface IAttributedATLSpaceShip : IDispatch { }; // CAttributedATLSpaceShip [ coclass, threading("apartment"), vi_progid("AttributedATLSpaceShipSvr.AttributedATL"), progid("AttributedATLSpaceShipSvr.AttributedA.1"), version(1.0), uuid("CE07EBA4-0858-4A81-AD1C-C12710B4A1A2"), helpstring("AttributedATLSpaceShip Class") ] class ATL_NO_VTABLE CAttributedATLSpaceShip : public IAttributedATLSpaceShip { public: CAttributedATLSpaceShip() { } DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { return S_OK; } void FinalRelease() { } public: };
All the COM support brought in as C++ templates in classic ATL is brought into the ATL server through provider DLLs. The square-braced attributes at the top of the file instruct the compiler to add the COM infrastructure to the CAttributedATLSpaceShip class. That's a whole lot easier than keeping track of classes such as CComObjectRootEx and CComCoClass and macros such as BEGIN_COM_MAP.
Developing the COM class further is almost easy. For example, say you want to add the IMotion and IVisible interfaces to the class. In attributed ATL, you just put the interfaces directly into the ATL source code, like this:
[ object, uuid("692D03A4-C689-11CE-B337-88EA36DE9E4E"), dual, helpstring("IMotion interface") ] __interface IMotion : IDispatch { HRESULT Fly(); HRESULT GetPosition([out,retval]long* nPosition); }; [ object, uuid("692D03A5-C689-11CE-B337-88EA36DE9E4E"), helpstring("IVisual interface") ] __interface IVisual : IUnknown { HRESULT Display(); }; // More code
The attributes in front of the __interface keyword describe the interfaces as COM interfaces—dual interfaces, to be exact. Once these interfaces are described in the source code, you can implement the interfaces on the class by right-clicking on the class name in Class View, choosing Add, and then selecting Implement Interface. You can select the interfaces from registered type libraries or from the interfaces listed in the source code (IMotion and IVisual). Visual Studio .NET stubs out the functions for you; you need to fill them in.
The resulting DLL is a full-fledged COM DLL complete with the expected entry points: DllMain, DllGetClassObject, DllCanUnloadnow, DllRegisterServer, and DllUnregisterServer.