Visual Basic Shell Programming
only for RuBoard - do not distribute or recompile |
8.2 Data Handler Interfaces
Data handlers implement two interfaces: IPersistFile and IDataObject . Everything that needs to be said about IPersistFile has already been said (see Section 5.2.1 in Chapter 5). Conversely, we know almost nothing about IDataObject . And even when we are done with this chapter, there will still be much that has not been said about this interface. The world of IDataObject is huge. It is one of the fundamental interfaces involved in OLE data transfers.
8.2.1 IDataObject
Table 8.1 shows all nine methods of IDataObject , but only three are required when writing data handlers in Visual Basic: QueryGetData , EnumFormatEtc , and GetData .
Table8.1. The IDataObject Interface
Method | Description |
---|---|
GetData | Retrieves data from a data object, as defined by a FORMATETC structure. The data is then transferred through a STGMEDIUM structure. |
GetDataHere | Similar to GetData except the caller is responsible for allocating and freeing all memory associated with FORMATETC and STGMEDIUM . |
QueryGetData | Given a FORMATETC structure, this method determines if a resulting call to GetData will be successful. |
GetCanonicalFormatEtc | Determines if two different FORMATETC structures would produce the same data, providing a means to eliminate a second call to GetData . |
SetData | Allows another object to send data to the data object. |
EnumFormatEtc | Provides a means to enumerate all the ways a data object can describe data in a FORMATETC structure. |
DAdvise | Creates an event sink between the caller and the data object, allowing the data object to be notified when data changes. |
DUnadvise | Releases the event sink created by DAdvise . |
EnumDAdvise | Enumerates the active event sinks between the caller and the data object. |
8.2.1.1 QueryGetData
This method determines whether the data handler is capable of providing data in the format that is being requested by the shell. This method has the following syntax:
HRESULT QueryGetData(FORMATETC *pFormatEtc);
When a data handler is first loaded, the shell calls QueryGetData several times in order to determine what formats the handler can provide. You can picture the shell asking the handler, "Do you support text? Do you support bitmaps? Do you support wave files?" The query comes in the form of a FORMATETC structure. This structure is used to describe data that will be involved in a transfer. It is defined like this:
typedef struct tagFORMATETC { CLIPFORMAT cfFormat; DVTARGETDEVICE *ptd; DWORD dwAspect; LONG lindex; DWORD tymed; }FORMATETC, *LPFORMATETC;
Its members are as follows :
- cfFormat
-
Contains the particular clipboard format of interest. This can be one of the following values in the CLIPFORMAT enumeration (this is defined in vbshell.odl ) :
typedef enum { CF_TEXT = 1, 'Text format CF_BITMAP = 2, 'Handle to a bitmap CF_METAFILEPICT = 3, 'Handle to a metafile picture format CF_SYLK = 4, 'Microsoft Symbolic Link format CF_DIF = 5, 'Software Art's data interchange format CF_TIFF = 6, 'Tagged-image file format CF_OEMTEXT = 7, 'Text format in OEM character set CF_DIB = 8, 'Memory object containing BITMAPINFO CF_PALETTE = 9, 'Handle to a color palette CF_PENDATA = 10, 'Data for pen extensions CF_RIFF = 11, 'Audio data CF_WAVE = 12, 'Audio data in WAV format CF_UNICODETEXT = 13, 'Unicode text format CF_ENHMETAFILE = 14, 'Handle to enhanced metafile CF_HDROP = 15, 'Handle that identifies list of files CF_LOCALE = 16, 'Handle to locale identifier CF_MAX = 17, 'Undocumented???? CF_OWNERDISPLAY = 0x0080, 'Owner display format CF_DSPTEXT = 0x0081, 'Text in private format CF_DSPBITMAP = 0x0082, 'Bitmap display in private format CF_DSPMETAFILEPICT = 0x0083, 'Metafile in private format CF_DSPENHMETAFILE = 0x008E 'Enhanced metafile in private format } CLIPFORMAT;
- ptd
-
This member is a pointer to a DVTARGETDEVICE structure that contains information about the target device for which the data is being readied. We will not discuss this parameter for two reasons. First, it would really complicate our discussion. If you need to write a data handler that dumps data to a printer, then you would need this parameter (see the documentation for the Platform SDK). Second, the data handler we will write (and most of the ones you will probably write) are device-independent, so this parameter is meaningless. It will most likely always be 0.
- dwAspect
-
This member can be one of the following values from the DVASPECT enumeration, which is defined like so:
typedef enum tagDVASPECT { DVASPECT_CONTENT = 1, DVASPECT_THUMBNAIL = 2, DVASPECT_ICON = 4, DVASPECT_DOCPRINT = 8 } DVASPECT;
A single clipboard format can support multiple views, or aspects. Think about Explorer when the view is configured as "View as Web Page." If you select a graphics file in the shell, a thumbnail image is shown to the left of the files listing. The data handler involved in this process most likely received a request for the data with this parameter set to DVASPECT_THUMBNAIL .
The members of this enumeration have the following meaning:
Constant
Description
DVASPECT_CONTENT
Returns the data in a format that is ready for the screen or the printer.
DVASPECT_THUMBNAIL
Presents the data in a 120
120, 16-color, device-independent bitmap. DVASPECT_ICON
Provides an iconic representation of the data.
DVASPECT_DOCPRINT
Provides a view of the data on the screen as though it were printed on a printer using the File
Print command. The data might represent a series of pages. - lindex
-
Part of the aspect when the data is split across page boundaries. The most common value is -1, which identifies all of the data. For the aspects DVASPECT_THUMBNAIL and DVASPECT_ICON , this value is ignored.
- tymed
-
One of the TYMED enumeration constants used to indicate the type of storage medium being used to facilitate a data transfer. The enumeration is defined like so:
typedef enum tagTYMED { TYMED_HGLOBAL = 1, TYMED_FILE = 2, TYMED_ISTREAM = 4, TYMED_ISTORAGE = 8, TYMED_GDI = 16, TYMED_MFPICT = 32, TYMED_ENHMF = 64, TYMED_NULL = 0 } TYMED;
We will use the value TYMED_HGLOBAL to tell the shell that our data transfers will take place via global memory. But as you can see, there are many more options.
8.2.1.2 EnumFormatEtc
Before the shell can get data from the data object, it must retrieve the formats that the object supports. It does this by calling IDataObject::EnumFormatEtc . This method has the following definition:
HRESULT EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC ** ppenumFormatetc);
Its parameters are:
- dwDirection
-
This is a value from the following enumeration:
typedef enum tagDATADIR {DATADIR_GET = 1, DATADIR_SET = 2, } DATADIR;
If the value of dwDirection is DATADIR_GET , the shell is asking the data handler to supply all of the formats that can be passed to GetData successfully. Conversely, if dwDirection equals DATADIR_SET , then the shell wants to know which formats will work with a call to SetData . A data handler will not be asked for SetData formats.
- ppenumFormatetc
-
This is an IEnumFORMATETC reference provided by the data object that the shell will use to enumerate all of the formats that the object supports.
8.2.1.3 GetData
Called when the client (Explorer) is ready to receive the data. The function is defined as:
HRESULT GetData(FORMATETC * pFormatetc, STGMEDIUM * pmedium);
GetData returns the data in the format described by pFormatetc and transfers this data through pmedium .
FORMATETC should already be familiar to you from the discussion of the IDataObject interface's QueryGetData method. STGMEDIUM , however, requires some explanation. The structure looks like this:
typedef struct tagSTGMEDIUM { DWORD tymed; union { HBITMAP hBitmap; HMETAFILEPICT hMetaFilePict; HENHMETAFILE hEnhMetaFile; HGLOBAL hGlobal; LPWSTR lpszFileName; IStream *pstm; IStorage *pstg; }; IUnknown *pUnkForRelease; }STGMEDIUM;
Here's how the structure works: the tymed member contains a value from the TYMED enumeration, which has already been seen in our discussion of QueryGetData . This value determines which value of the union is valid. So if tymed is equal to TYMED_HGLOBAL , the hGlobal member of the union should contain the data for the transfer. If tymed is equal to TYMED_ISTREAM , the data should be made available through the IStream * member of the structure.
There is a problem with this structure, however: VB does not support unions. Remember, though, that members of a union occupy the same physical address in memory, so a workaround is fairly simple. We can define the structure like this:
typedef struct { TYMED tymed; long pData; IUnknown *pUnkForRelease; } STGMEDIUM;
This works because, naturally, every member of the union is essentially a 4-byte value (a pointer or a handle).
The last member of this structure that needs to be discussed is pUnkForRelease . Remember the ReleaseStgMedium function (see Chapter 4)? This function is called to free the storage allocated by STGMEDIUM . Well, if pUnkForRelease is NULL , then ReleaseStgMedium uses its default methods to release this memory. If it's not NULL , then ReleaseStgMedium uses the IUnknown pointer specified by this member to free the storage. It does this by calling IUnknown::Release .
The IDL listing for IDataObject is shown in Example 8.1.
Example 8.1. IDataObject IDL Definition
//--------------------------------------------------------- // IDataObject //--------------------------------------------------------- typedef enum { DV_E_FORMATETC = 0x80040064, DV_E_DVTARGETDEVICE = 0x80040065, DV_E_STGMEDIUM = 0x80040066, DV_E_STATDATA = 0x80040067, DV_E_LINDEX = 0x80040068, DV_E_TYMED = 0x80040069, DV_E_CLIPFORMAT = 0x8004006A, DV_E_DVASPECT = 0x8004006B, DV_E_DVTARGETDEVICE_SIZE = 0x8004006C, DV_E_NOIVIEWOBJECT = 0x8004006D } DV_ERROR; typedef enum tagDATADIR { DATADIR_GET = 1, DATADIR_SET = 2 } DATADIR; [ uuid(0000010e-0000-0000-C000-000000000046), helpstring("IDataObject Interface"), odl ] interface IDataObject : IUnknown { HRESULT GetData( [in] FORMATETC *pformatetcIn, [in,out] STGMEDIUM *pmedium); HRESULT GetDataHere( [in] FORMATETC *pformatetc, [in,out] STGMEDIUM *pmedium); HRESULT QueryGetData( [in] FORMATETC *pformatetc); HRESULT GetCanonicalFormatEtc( [in] FORMATETC *pformatectIn, [in,out] FORMATETC *pformatetcOut); HRESULT SetData( [in] FORMATETC *pformatetc, [in] STGMEDIUM *pmedium, [in] BOOL fRelease); HRESULT EnumFormatEtc( [in] long dwDirection, [in,out] IEnumFORMATETC **ppenumFormatEtc); HRESULT DAdvise( [in] FORMATETC *pformatetc, [in] long advf, [in] long pAdvSink, [in] long pdwConnection); HRESULT DUnadvise( [in] long dwConnection); HRESULT EnumDAdvise( [in] long ppenumAdvise); }
|
only for RuBoard - do not distribute or recompile |