views:

112

answers:

0

I have a custom button control created using ATL. This control is used by some composite controls and lots of dialog boxes. I just added some new properties to the button control and found that I then had to update all the controls and dialogs that used it. This is a really poor situation so I wondered if I could be doing something better.

Here are some relevant bits of code:

class ATL_NO_VTABLE CSMButton :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CStockPropImpl<CSMButton, ISMButton>,
    public IPersistStreamInitImpl<CSMButton>,
    public IOleControlImpl<CSMButton>,
    public IOleObjectImpl<CSMButton>,
    public IOleInPlaceActiveObjectImpl<CSMButton>,
    public IViewObjectExImpl<CSMButton>,
    public IOleInPlaceObjectWindowlessImpl<CSMButton>,
    public ISupportErrorInfo,
    public IConnectionPointContainerImpl<CSMButton>,
    public IConnectionPointImpl<CSMButton, &DIID__ISMButtonEvents>,
    public CProxy_ISMButtonEvents<CSMButton>,
    public IPersistStorageImpl<CSMButton>,
    public ISpecifyPropertyPagesImpl<CSMButton>,
    public IQuickActivateImpl<CSMButton>,
#ifndef _WIN32_WCE
    public IDataObjectImpl<CSMButton>,
#endif
    public IProvideClassInfo2Impl<&CLSID_SMButton, &__uuidof(_ISMButtonEvents), &LIBID_BaseControlsLib>,
#ifdef _WIN32_WCE // IObjectSafety is required on Windows CE for the control to be loaded correctly
    public IObjectSafetyImpl<CSMButton, INTERFACESAFE_FOR_UNTRUSTED_CALLER>,
#endif
    public CComCoClass<CSMButton, &CLSID_SMButton>,
    public CComControl<CSMButton>
{ 
...
BEGIN_COM_MAP(CSMButton)
    COM_INTERFACE_ENTRY(ISMButton)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IViewObjectEx)
    COM_INTERFACE_ENTRY(IViewObject2)
    COM_INTERFACE_ENTRY(IViewObject)
    COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
    COM_INTERFACE_ENTRY(IOleInPlaceObject)
    COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless)
    COM_INTERFACE_ENTRY(IOleInPlaceActiveObject)
    COM_INTERFACE_ENTRY(IOleControl)
    COM_INTERFACE_ENTRY(IOleObject)
    COM_INTERFACE_ENTRY(IPersistStreamInit)
    COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
    COM_INTERFACE_ENTRY(IConnectionPointContainer)
    COM_INTERFACE_ENTRY(ISpecifyPropertyPages)
    COM_INTERFACE_ENTRY(IQuickActivate)
    COM_INTERFACE_ENTRY(IPersistStorage)
#ifndef _WIN32_WCE
    COM_INTERFACE_ENTRY(IDataObject)
#endif
    COM_INTERFACE_ENTRY(IProvideClassInfo)
    COM_INTERFACE_ENTRY(IProvideClassInfo2)
#ifdef _WIN32_WCE // IObjectSafety is required on Windows CE for the control to be loaded correctly
    COM_INTERFACE_ENTRY_IID(IID_IObjectSafety, IObjectSafety)
#endif
END_COM_MAP()

BEGIN_PROP_MAP(CSMButton)
    PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
    PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
    PROP_ENTRY_TYPE("Caption", DISPID_CAPTION, CLSID_NULL, VT_BSTR)
    PROP_ENTRY_TYPE("Colour", DISPID_COLOUR, CLSID_NULL, VT_COLOR)
    PROP_ENTRY_TYPE("ButtonType", DISPID_BUTTONTYPE, CLSID_NULL, VT_I4)
    PROP_ENTRY_TYPE("Toggle", DISPID_TOGGLE, CLSID_NULL, VT_BOOL)
    PROP_ENTRY_TYPE("Down", DISPID_DOWN, CLSID_NULL, VT_BOOL)
    // Example entries
    // PROP_ENTRY_TYPE("Property Name", dispid, clsid, vtType)
    // PROP_PAGE(CLSID_StockColorPage)
END_PROP_MAP()

The two VT_BOOLs are what I added. After doing that and building the control, I got an assert at runtime when showing a dialog and when I opened the dialog in visual studio I got a warning and I had to resave the dialog box. The dialog then worked ok at runtime.

I believe the reason for this is that the dialog has saved the properties as a binary stream in the resource and when new properties are added there is no longer enough data to fully initialize the control. The behaviour I would like in this case is for property values that are present to be set and any remaining properties should have default values. How can this be achieved?

And no, I can't use .net :-(