FORMATETC (pronounced "format et cetera") is a generalization—and an improvement—of the clipboard format and contains a rich description of data. The name comes from the idea that it contains a clipboard format and some more stuff—the et cetera:
typedef WORD CLIPFORMAT;
typedef struct tagFORMATETC
{
CLIPFORMAT cfFormat;
DVTARGETDEVICE *ptd;
DWORD dwAspect;
LONG lindex;
DWORD tymed;
} FORMATETC;
enum tagDVASPECT
{
DVASPECT_CONTENT = 1,
DVASPECT_THUMBNAIL = 2,
DVASPECT_ICON = 4,
DVASPECT_DOCPRINT = 8
} DVASPECT;
Its fields carry information as follows:
typedef struct FARSTRUCT tagDVTARGETDEVICE
{
DWORD tdSize;
WORD tdDriverNameOffset;
WORD tdDeviceNameOffset;
WORD tdPortNameOffset;
WORD tdExtDevmodeOffset;
BYTE tdData[1]; //Contains the names and DEVMODE
} DVTARGETDEVICE;
Obviously, filling out a structure like this every time you want to describe a data format will become tedious; you need five lines of code simply to fill the structure. To address this, I have defined two macros in INC\INOLE.H that facilitate filling a FORMATETC: SETFormatEtc, which allows you to set every field in a FORMATETC structure explicitly, and SETDefFormatEtc, which allows you to set cfFormat and tymed while filling the other fields with defaults (a common operation):
#define SETFormatEtc(fe, cf, asp, td, med, li) \
{\
(fe).cfFormat=cf;\
(fe).dwAspect=asp;\
(fe).ptd=td;\
(fe).tymed=med;\
(fe).lindex=li;\
};
#define SETDefFormatEtc(fe, cf, med) \
{\
(fe).cfFormat=cf;\
(fe).dwAspect=DVASPECT_CONTENT;\
(fe).ptd=NULL;\
(fe).tymed=med;\
(fe).lindex=-1;\
};
I encourage you to use these macros when dealing with FORMATETC because they are far more convenient than writing each line of code separately.
As described above, the ptd field of a FORMATETC structure is a pointer to a DVTARGETDEVICE structure. A NULL ptd always means "screen device," which is, of course, the easiest to handle. Printer devices, on the other hand, are more complex. Applications and components that deal with device-specific data will end up performing a few basic operations with such a structure: creating a structure, copying or comparing two structures, and creating a device context or an information context from a structure.
To that end, the sample code for this chapter provides a number of standard implementations of these functions, which you can find in CHAP10\TARGDEV\TARGDEV.CPP.2 In this file are the functions TargetDeviceToDC, TargetDeviceFromPrintDlg, TargetDeviceCopy, and TargetDeviceCompare. TargetDeviceToDC calls CreateDC or CreateIC with the information in a DVTARGETDEVICE structure. TargetDeviceFromPrintDlg creates a DVTARGETDEVICE structure (using the task memory allocator through CoTaskMemAlloc) from the PRINTDLG structure filled by the Windows PrintDlg API function. Applications generally use PrintDlg to obtain printer information in the first place, so it's convenient to be able to send that data to TargetDeviceFromPrintDlg and get back the OLE structure you need.
The other two functions are actually quite simple and manipulate the structure on little more than a binary level. In any case, I hope you find this code useful in your own work.
Whenever you want to copy or compare FORMATETC structures, keep the following in mind. When copying one structure to another, be sure to copy the entire DVTARGETDEVICE structure as well, using code such as that found in the sample TargetDeviceCopy function. Other than that, copying is a straightforward duplication of each field.
Comparing two FORMATETC structures presents a slightly different problem. First of all, the cfFormat and lindex fields must match exactly for equivalence. In addition, both structures must have the same target device structure, which is where the sample TargetDeviceCompare function comes in handy. Both dwAspect and tymed require special handling, however. You can compare these either for exact equivalence or on a subset basis. For example, if one FORMATETC has DVASPECT_CONTENT ¦ DVASPECT_THUMBNAIL, another with only DVASPECT_CONTENT could be considered equivalent but not an exact match. The same applies to TYMED_* flags. Why you'd choose either form of comparison depends on why you're making the comparison in the first place. If you want to determine whether one FORMATETC is a subset of a supported set of formats, use a subset comparison. If you need an exact match for a specific rendering, you want an exact comparison.
1 Page layout capabilities might not be supported in the version of OLE you are working with. In the original OLE 2 and all shipping versions up to the time of writing in early 1995, lindex must always be -1. Newer interfaces will be using this field in the near future according to its original purpose described here. See your current OLE Programmer's Reference for more information. |
2 These functions are portable between ANSI (16-bit) and Unicode (32-bit) to handle both versions of DVTARGETDEVICE. |