Geoff Chappell - Software Analyst
This function creates a DPA and loads it with items read from a specially formatted stream.
HRESULT DPA_LoadStream ( HDPA *ppdpa, PFNDPASTREAM pfn, IStream *pstm, PVOID pvInstData);
The callback function has the prototype:
typedef HRESULT (*PFNDPASTREAM) ( DPASTREAMINFO *pinfo, IStream *pstm, PVOID pvInstData);
The information structure for the callback function has the form:
typedef struct _DPASTREAMINFO { int iPos; PVOID pvItem; } DPASTREAMINFO;
The stream is expected to begin, at its current seek pointer, with the following structure:
typedef struct _DPASTREAMHEADER { DWORD dwSize; DWORD dwVersion; int cpItems; } DPASTREAMHEADER;
The ppdpa argument provides the address of a variable that is to receive a handle to the DPA.
The pfn argument provides the address of a callback function that loads successive items from the stream.
The pstm argument provides the address of an IStream interface for access to the stream.
The pvInstData argument provides a caller-specific context for each invocation of the callback function.
The function returns S_OK (zero) for success, with all items loaded from the stream. It returns S_FALSE (1) for a partial success in which loading was aborted. In both these cases, the variable at the address given by the ppdpa argument receives a handle to the DPA that lists whatever items did get loaded.
Any other value is an error code to describe a failure, with no items loaded and no DPA. The variable at the address given by the ppdpa argument may be, but is not necessarily, cleared to NULL.
The function expects that the given stream, at its current seek position, begins with a 12-byte DPASTREAMHEADER structure The dwSize member gives the size of the stream (or, strictly speaking, of as much of the stream as is to concern this function), measured in bytes, counting the header and whatever arbitrary-sized items follow. The cpItems member gives the number of those items. The function creates a DPA with sufficient memory for representing this number of items, and then calls the given callback function repeatedly to get this many items loaded from the stream.
Each invocation of the callback function is responsible for loading one item. The 0-based index for this item is provided as the iPos member of a DPASTREAMINFO structure. Also provided as arguments to the callback function are the pstm and pvInstData arguments, as given to to the DPA_LoadStream function.
To indicate success at loading the desired item, the callback function returns S_OK and enters the address of the loaded item into the pvItem member of the DPASTREAMINFO structure. The address may be NULL, to represent an empty item. Whatever the address, DPA_LoadStream accepts it into the DPA and advances to the next item. A successful load of the last expected item results in DPA_LoadStream declaring a full success, returning S_OK.
The callback function returns any negative error code to report its failure at loading the desired item and to direct that DPA_LoadStream not ask to load any more items. This causes DPA_LoadStream to declare a partial success, returning S_FALSE.
Behaviour if the callback function returns any positive value may best be treated as undefined. If for a particular iPos in the DPASTREAMINFO structure, the callback function always returns a positive value without changing the iPos, then DPA_LoadStream will hang as it calls the callback function repeatedly with this same iPos.
Historically, the function returns E_INVALIDARG if the ppdpa argument is not a valid address for writing the 4 bytes of a DPA handle, or if either the pfn or pstm arguments are not valid as addresses for reading at least one byte. These checks are removed entirely in versions 5.82 and 6.0 starting from Windows XP SP2. However, less strict defences are reinstated in the version 5.82 from Windows Vista and higher, and in version 6.10 and higher: the function returns E_INVALIDARG if any of the ppdpa, pfn and pstm arguments are NULL.
The function returns error codes from the given stream’s Seek or Read methods, if these fail when noting the stream’s initial seek pointer (in anticipation of having to restore it) or when reading the expected header.
The function returns E_FAIL if the stream, at its current seek position, is readable but fails to supply 12 bytes for interpretation as a DPASTREAMHEADER. Successful interpretation requires all the following:
The function returns E_OUTOFMEMORY if a DPA cannot be created with its allocation unit set to the number of items in the stream (from cpItems in the DPASTREAMHEADER), or if the DPA cannot be grown to accommodate this many items. It probably does not matter much in practice, but if the creation succeeds and the expansion fails, then the DPA ought to be destroyed but is not.
In all but the E_INVALIDARG case above, the function sets NULL as the DPA handle at the address given by the ppdpa argument.
In the E_FAIL and E_OUTOFMEMORY cases noted above, the function resets the given stream’s seek position to wherever it was before any read was attempted from the stream. In the case of partial success, with the function returning S_FALSE, the function advances the seek position as if the whole stream (i.e., the number of bytes given by dwSize in the DPASTREAMHEADER) had been read successfully.
The DPA_LoadStream function is exported from COMCTL32.DLL as ordinal 9 in version 4.71 and higher. The implementation for version 6.10 and higher is built into a statically linked library and thence is also exported from the Internet Explorer module IERTUTIL.DLL as ordinal 86 in version 7.0 and higher.
Though this function dates from as long ago as 1997, it was still not documented by Microsoft in the MSDN Library at least as late as the CD edition dated January 2004.
Microsoft documented this function for the MSDN on-line in April 2005, apparently after a contributor to a blog by Dave Massy (at Microsoft) cited this function as a counter-example to Massy’s claim that Internet Explorer uses no Windows API functions that were not by then documented by Microsoft. This article now follows Microsoft’s nomenclature as much as possible.
Microsoft’s name for the DPASTREAMINFO structure has long been knowable even without formal documentation from Microsoft. The DPA_LoadStream function is called from BROWSEUI.DLL, SHDOCVW.DLL and SHELL32.DLL, almost certainly from one source file used for all three executables. The callback function is written in C++ and the decorated name, as seen in Microsoft’s symbol files for these DLLs, therefore includes the name of the information structure.
The DPASTREAMHEADER structure and its members are not documented, and the names used here are invented. Indeed, the naming of the dwVersion member as providing some sort of version number is mere supposition. That the header is left undocumented is not unreasonable if the point is made clear that DPA_LoadStream expects to work only with a stream that has been written by DPA_SaveStream, for the internal structure of the header would then be an implementation detail of this pair of DPA functions.