One of the most important things your control will do is save and restore its persistent state. This typically is done in one of two ways: through the PropertyBag interfaces for text persistence, and through streams for binary persistence. With the latter, performance is absolutely critical to make your control load quickly.
The ActiveX controls framework requires your control to implement four member functions to support persistence. The control is required to implement LoadTextState, LoadBinaryState, SaveTextState, and SaveBinaryState. If you are certain that the control is never going to be in a host that uses IPersistPropertyBag, then you can ignore the two text interfaces, but this isn't recommended (they are easy to use).
Text persistence in the framework is done via IPersistPropertyBag and IPropertyBag. All ActiveX controls have an implementation of IPersistPropertyBag, and are given pointers to property bag objects to do their work.
Effectively, the IPropertyBag interface has two routines that you can use: Read and Write. In both, you pass in a VARIANT. With Read, the property is put in the VARIANT if it was saved out. If the property can’t be found (it was never saved out), the default value for that property should be used, and an error should probably not be returned. For Write, a VARIANT with the data is passed in. To persist out a collection or an object (such as a Font or Picture object), you can pass in a VT_UNKNOWN object, and the PropertyBag will use QueryInterface on that object for IPersistPropertyBag or IPersistStream and persist it. This actually proves effective for persisting collections; they can just support IPersistPropertyBag.
The accompanying samples show how to persist out properties using PropertyBags. For controls with many properties, it often makes sense to use a table-driven persistence to reduce code size and bug potential.
For additional information, see the MSDN and Inside OLE (2nd edition), by Kraig Brockschmidt, both of which have descriptions of the IPropertyBag interface.
It is critical to work on binary persistence when implementing an ActiveX control. Control load speed can be severely hampered by a poorly written LoadBinaryState routine, so it's critical to spend some time thinking about how to keep this routine fast. The binary persistence code is used by some hosts all the time, and by other hosts when including the control in a generated executable file.
In both binary routines, the control is handed a pointer to an IStream object. The key to load speed is to reduce the number of operations on the stream. For saving, this is slightly less critical.
Typically, a control saves the following information (often in the given order):
Most control writers should start the control's binary persistent state with a header structure that includes a "magic" number that the control can check when loading. You may also want to include some sort of version number so that future versions of the control can distinguish older versions. Finally, you may want the control to write out the number of bytes of data that were written.
The samples in the framework that have a binary persistent state use the following structure:
#define STREAMHDR_MAGIC 0x12345678
typedef struct {
DWORD dwMagic;
DWORD dwVersion;
DWORD cbSize;
} STREAMHDR;
The SaveBinaryState routine saves this information; the LoadBinaryState routine looks for it.
One way to write out all the fixed-size information (and therefore load it efficiently) is to do it all at once. If the control's fixed persistent state is in a structure in the control object, you can write out the structure in the persistence code. Controls generated by the control wizard have a structure defined for them called MYCTLNAMECTLSTATE, into which you can put the control's fixed-state data. Then, when saving, the control does the following:
hr = pStream->Write(&m_state, sizeof(m_state), NULL);
For loading, it becomes as simple as this:
hr = pStream->Read(&(m_state), sizeof(m_state), NULL);
It’s efficient and simple.
For fonts and pictures, it's slightly more complicated. The control has to use QueryInterface on those objects for IPersistStream, and then call the Load or Save method with the stream that the control has been given. Typically, this can be done after the control has written out all other information.
If you follow the above suggestions for persistent state structure, your control's load routine could look something like this:
IPersistStream *pps;
STREAMHDR sh;
HRESULT hr;
// First read in the streamhdr, and make sure we like what we're getting.
hr = pStream->Read(&sh, sizeof(sh), NULL);
RETURN_ON_FAILURE(hr);
// Sanity check.
if (sh.dwMagic != STREAMHDR_MAGIC || sh.cbSize != sizeof(m_state))
return E_UNEXPECTED;
// Read in the control state information.
hr = pStream->Read(&(m_state), sizeof(m_state), NULL);
RETURN_ON_FAILURE(hr);
// Now read in the font!
OleCreateFontIndirect(&_fdDefault, IID_IFont, (void **)&m_pFont);
RETURN_ON_NULLALLOC(m_pFont);
// QueryInterface it for IPersistStream and load it in.
hr = m_pFont->QueryInterface(IID_IPersistStream, (void **)&pps);
RETURN_ON_FAILURE(hr);
hr = pps->Load(pStream);
pps->Release();
return hr;
This is fast and robust.