FORMATETC Enumerators and Format Ordering

IDataObject::EnumFormatEtc is responsible for creating and returning an enumerator object for FORMATETC structures, in which the object implements IEnumFORMATETC. The data consumer can ask EnumFormatEtc for an enumerator that knows the formats obtainable from IDataObject::GetData—the get direction—or the formats that can be sent to IDataObject::SetData—the set direction. Directions are taken from the DATADIR enumeration:


typedef enum tagDATADIR
{
DATADIR_GET = 1,
DATADIR_SET = 2
} DATADIR;

As with all enumerators we've seen, IEnumFORMATETC implements the same member functions as all other enumerators: Next, Skip, Reset, and Clone. They merely deal with FORMATETC structures instead of some other type.

What is most important about a FORMATETC enumerator is its logical equivalence to the Windows API function EnumClipboardFormats, meaning that the order of formats enumerated through IEnumFORMATETC should proceed from the highest-detail, highest-fidelity formats to the lowest. A consumer will enumerate the available formats, looking for an acceptable one; the first such format is assumed to be the best the consumer can obtain from the source. The enumerated formats usually start with the most precise private data structures and proceed through other standard interchange formats (such as those for compound documents), picture or graphic formats such as CF_METAFILEPICT and CF_BITMAP, and simple link information such as a moniker. Link information is always considered to be the lowest-priority format so that the user has to explicitly ask for a link in order to have the consumer use such a format.

IEnumFORMATETC is one of the few enumerators that you may actually need to implement yourself in a data source unless you have a fixed list of formats. In that case, you can let OLE implement the enumerator for you provided you have registry entries describing the formats available in both get and set directions. Because the registry holds somewhat fixed information, the available formats are also relatively fixed. If you can live with this restriction, which many data sources can, you can use OLE's services provided through the API function OleRegEnumFormatEtc, which has the following signature:


HRESULT OleRegEnumFormatEtc(CLSID clsid, DWORD dwDirection
, LPENUMFORMATETC *ppEnum);

Here clsid identifies where to find the appropriate registry entries, dwDirection identifies the direction of the enumeration, and ppEnum is where the enumerator's IEnumFORMATETC pointer is returned. With this function and appropriate registry entries, a data object's implementation of IDataObject::EnumFormatEtc is reduced to the following:


HRESULT CImpIDataObject::EnumFormatEtc(DWORD dwDirection
, LPENUMFORMATETC *ppEnum);
{
//m_clsID is the object's CLSID.
return OleRegEnumFormatEtc(m_clsID, dwDirection, ppEnum);
}

This API function will look for registry entries in the following format:


\
CLSID
{<CLSID>} = <Name>
DataFormats
GetSet
0 = <format,aspect,medium,direction>
1 = <format,aspect,medium,direction>
2 = <format,aspect,medium,direction>
<n> = <format,aspect,medium,direction>

You can list as many formats as you want, provided each is given a unique integer key name under GetSet. Note that DataFormats and GetSet are literal keywords in registry entries of this type.

Each format entry is made of a clipboard format value or string, followed by any combination of DVASPECT values (-1 means "all"), then by any combination of TYMED values (be specific; no wildcards allowed), and finally by any one of the DATADIR values. Take, for example, the following entry:


1 = 3,-1,32,1

This entry describes CF_METAFILEPICT (3) for all aspects (-1) on the TYMED_MFPICT medium (32) available in the get direction (DATADIR_GET, which equals 1). Another example is the following entry:


2 = 2,1,16,1

This entry describes CF_BITMAP (2) for only DVASPECT_CONTENT (1) in TYMED_GDI (16) for the get direction. Remember that the format can also be a string (spaces allowed) to identify a registered clipboard format. You can see this in the following entry:


0 = Polyline Figure,3,5,3

Here a registered format "Polyline Figure" for DVASPECT_CONTENT ¦ DVASPECT_THUMBNAIL (3) in TYMED_HGLOBAL ¦ TYMED_ISTREAM (5) is available in both get and set directions (DATADIR_GET ¦ DATADIR_SET, which equals 3).

The integer key given to each format determines their order. The format described by key 0 is the preferred format, followed by key 1, then key 2, and so on. In the three examples shown here, we have the format order of "Polyline Figure," CF_METAFILEPICT, and CF_BITMAP. This is the information that a consumer would obtain through the IEnumFORMATETC interface that it would obtain through IDataObject::EnumFormatEtc.

It's not always the case that a data source can fix its available formats in stone. For example, a data object representing information on the clipboard has to work with a variable data set, so each enumerator it creates might have a different list of formats. Even so, given an array of FORMATETC structures, you can use a reasonably standard implementation—such as that found in the CEnumFormatEtc class in INTERFAC\IENUMFE.CPP—which is also used in some of this chapter's samples. This particular implementation takes an array of FORMATETC structures in its constructor, which it copies as the list to enumerate.


CEnumFormatEtc::CEnumFormatEtc(ULONG cFE, LPFORMATETC prgFE)
{
UINT i;

m_cRef=0;

m_iCur=0;
m_cfe=cFE;
m_prgfe=new FORMATETC[(UINT)cFE];

if (NULL!=m_prgfe)
{
for (i=0; i < cFE; i++)
m_prgfe[i]=prgFE[i];
}

return;
}

CEnumFormatEtc::~CEnumFormatEtc(void)
{
if (NULL!=m_prgfe)
delete [] m_prgfe;

return;
}

As an independent object, CEnumFormatEtc enjoys the luxury of managing its own private reference count because it doesn't depend on the data object to stick around to provide the list of current formats. If you implement an enumerator that has such a dependency, it's best to have the enumerator hold a single reference count to the data object until the enumerator itself is destroyed. This is merely an implementation technique and has no impact whatsoever on the consumer, which will consider the data object destroyed when it releases its final pointer to that object.

Between OleRegEnumFormatEtc and this standard implementation, you shouldn't have to spend much time writing your own enumerator except in very special circumstances. Just remember to get the formats ordered the way you want consumers to see them.