Creating an Attributed Control
Chapter 22 and Chapter 25 that COM development requires a fair amount of boilerplate code—code that remains the same from one COM implementation to another. Attributed programming pushes the boilerplate COM code (IUnknown implementations, DLL entry points, and so forth) out of C++ templates and moves the boilerplate into injected code. That is, by declaring a few attributes before some C++ code, you can have the compiler and linker provide the boilerplate code.If you go back and take a look at the listing for ClassicATLDiceControl, you'll see CComObjectRootEx and CComCoClass templates in the declaration. The following listing shows the attributed version of the same control (the dice control).
// IAttributedATLDiceControl
[
object,
uuid(5321A066-9E3A-4412-A11A-32D5ED060146),
dual,
helpstring("IAttributedATLDiceControl Interface"),
pointer_default(unique)
]
__interface IAttributedATLDiceControl : public IDispatch
{
[propput, bindable, requestedit, id(DISPID_BACKCOLOR)]
HRESULT BackColor([in]OLE_COLOR clr);
[propget, bindable, requestedit, id(DISPID_BACKCOLOR)]
HRESULT BackColor([out,retval]OLE_COLOR* pclr);
[propget, id(1), helpstring("property DiceColor")]
HRESULT DiceColor([out, retval] SHORT* pVal);
[propput, id(1), helpstring("property DiceColor")]
HRESULT DiceColor([in] SHORT newVal);
[propget, id(2), helpstring("property TimesToRoll")]
HRESULT TimesToRoll([out, retval] SHORT* pVal);
[propput, id(2), helpstring("property TimesToRoll")]
HRESULT TimesToRoll([in] SHORT newVal);
[id(3), helpstring("method RollDice")] HRESULT RollDice(void);
};
// _IAttributedATLDiceControlEvents
[
uuid("4AB0D205-044E-4641-A0A5-B606D8685FE5"),
dispinterface,
helpstring("_IAttributedATLDiceControlEvents Interface")
]
__interface _IAttributedATLDiceControlEvents
{
[id(1), helpstring("method Doubles")] HRESULT Doubles(SHORT n);
[id(2), helpstring("method SnakeEyes")] HRESULT SnakeEyes(void);
[id(3), helpstring("method DiceRolled")] HRESULT DiceRolled
(SHORT x, SHORT y);
};
// CAttributedATLDiceControl
[
coclass,
threading("apartment"),
vi_progid("AttributedATLDiceSvr.AttributedATLDiceC"),
progid("AttributedATLDiceSvr.AttributedATLDic.1"),
version(1.0),
uuid("48350572-BE82-4FBB-AA6F-B4691E30173A"),
helpstring("AttributedATLDiceControl Class"),
event_source("com"),
support_error_info(IAttributedATLDiceControl),
registration_script("control.rgs")
]
class ATL_NO_VTABLE CAttributedATLDiceControl :
public CStockPropImpl<CAttributedATLDiceControl,
IAttributedATLDiceControl>,
public IPersistStreamInitImpl<CAttributedATLDiceControl>,
public IOleControlImpl<CAttributedATLDiceControl>,
public IOleObjectImpl<CAttributedATLDiceControl>,
public IOleInPlaceActiveObjectImpl<CAttributedATLDiceControl>,
public IViewObjectExImpl<CAttributedATLDiceControl>,
public IOleInPlaceObjectWindowlessImpl<CAttributedATLDiceControl>,
public IPersistStorageImpl<CAttributedATLDiceControl>,
public ISpecifyPropertyPagesImpl<CAttributedATLDiceControl>,
public IQuickActivateImpl<CAttributedATLDiceControl>,
public IDataObjectImpl<CAttributedATLDiceControl>,
public CComControl<CAttributedATLDiceControl>
{
public:![]()
__event __interface _IAttributedATLDiceControlEvents;
//Fire events:
HRESULT Fire_Doubles(short x)
{
__raise Doubles(x);
return S_OK;
}
HRESULT Fire_DiceRolled(short x, short y)
{
__raise DiceRolled(x, y);
return S_OK;
}
HRESULT Fire_SnakeEyes()
{
__raise SnakeEyes();
return S_OK;
}![]()
}
The CComCoClass and the CComObjectRootEx template classes are missing from the attributed version of the control. Other COM boilerplate code pulled in through the attributes declared includes an implementation of ISupportErrorInfo, connection point support, and an implementation of IProvideClassInfo2. Otherwise, the rest of the control is pretty much the same with the exception of managing events.
Control Events in Attributed ATL
Take a look at the previous listing for the attributed ATL control. In addition to declaring the main dice interface, the listing declares an event interface (the one with methods for telling the client code about the dice being rolled, snake eyes, and doubles). To declare the _IAttributedATLDiceControlEvents interface as the control's event interface, attributed ATL uses the keywords __event __interface together.Unfortunately, the code wizards available from Class View's properties window won't write the event proxies for you—that you must do by hand. Notice the hand-coded methods Fire_DiceRolled, Fire_Doubles, and Fire_SnakeEyes in the previous code listing. To fire the event off to the client code, you simply raise the event using the __raise keyword before calling the event method.