9 Redirected IO
This chapter describes Redirected IO. Anywhere a file specification (dwSpecType and pSpec parameters) is passed to a function in the product, the developer may use Redirected IO to completely take over responsibility for the low level IO calls of that particular file. The source file template files and all output files can be redirected in this way.
Redirected IO allows the developer great flexibility in the storage of, and access to, converted documents. For example, documents may be stored on file systems not supported natively by the software, or in a unique directory tree structure determined by the type of file.
When using HTML Export, redirected IO can also be used in conjunction with callbacks (discussed in Callbacks).
This chapter contains the following sections:
9.1 Using Redirected IO
A developer can redirect the IO for an input or output file by providing a data structure that contains pointers to custom IO routines for reading and writing. This data structure is passed in place of a typical file specification. The developer must set the dwSpecType parameter of the DAOpenDocument call to IOTYPE_REDIRECT when the DAOpenDocument call is sent.
When dwSpecType is set this way, the pSpec element must contain a pointer to a developer-defined data structure that begins with a BASEIO structure (defined in baseIO.H). The BASEIO structure contains pointers to the basic IO functions for the IO system such as Read, Seek, Tell, etc. The developer must initialize these function pointers to their own functions that perform IO tasks. Beyond the BASEIO element, the developer may place any data he or she likes. For instance, a developer's structure may be similar to the following:
typedef struct MYFILEtag { BASEIO sBaseIO; /* must be the first element */ VTDWORD dwMyInfo1; VTDWORD dwMyInfo2; . . . } MYFILE;
Because the pSpec passed is essentially the "file handle" used by the software, the developer can redirect the IO on a file-by-file basis while still exporting "regular" disk-based files.
The BASEIO structure is defined as follows:
typedef struct BASEIOtag { IOCLOSEPROC pClose; IOREADPROC pRead; IOWRITEPROC pWrite; IOSEEKPROC pSeek; IOTELLPROC pTell; IOGETINFOPROC pGetInfo; IOOPENPROC pOpen; /* pOpen *MUST* be set to NULL. */ #ifndef NLM IOSEEK64PROC pSeek64; IOTELL64PROC pTell64; #endif VTVOID *aDummy[3]; } BASEIO, * PBASEIO;
The developer must implement the Close, Read, Write, Seek, Tell and GetInfo routines. The Open routine must be set to NULL. The first parameter to each of these routines is called hFile and is of the type HIOFILE. HIOFILE is simply the VTLPVOID to your data structure that was passed in the pSpec parameter of the DAOpenDocument call.
The sample source code for a simple implementation of Redirected IO is in the samples directory. This sample redirects the technology's IO through the fopen, fgetc, fseek, ftell and fclose run-time library routines.
Note:
Redirected IO does not cache the whole file. Seeks can occur throughout the file during the course of conversion. If the developer is implementing redirected IO on a slow or sequential link, it is the developer's responsibility to cache the file locally.
9.2 Opening Files
The developer does not see a call to pOpen when using redirected IO. When IOTYPE_REDIRECT is used, the structure passed in pSpec is defined to represent a file that is already open. The software can immediately call the pRead, pSeek, pTell and pWrite functions.
Files specified as using redirected IO must be open by the time they are handed off to the software.
9.3 IOClose
Closes the file identified by hFile and cleans up all memory associated with the file.
If you dynamically allocate your own file structures (MYFILE in the preceding discussion) it is required that the memory allocated be freed inside the call to IOClose or sometime thereafter.
Prototype
IOERR IOClose( HIOFILE hFile);
Parameters
-
hFile: Identifies the file to be closed. Should be cast into a pointer to your data structure (MYFILE in the preceding discussion).
Return Values
-
IOERR_OK: Close was successful.
-
IOERR_UNKNOWN: Some error occurred on close.
9.4 IORead
Reads data from the current file position forward and resets the position to the byte after the last byte read.
Prototype
IOERR IORead( HIOFILE hFile, VTBYTE * pData, VTDWORD dwSize, VTDWORD * pCount);
Parameters
-
hFile: Identifies the file to be read. Should be cast into a pointer to your data structure (MYFILE in the preceding discussion).
-
pData: Points to the buffer into which the bytes should be read. Will be at least dwSize bytes big.
-
dwSize: Number of bytes to read.
-
pCount: Points to the number of bytes actually read by the function. This value is only valid if the return value is IOERR_OK.
Return Values
-
IOERR_OK: Read was successful. pCount contains the number of bytes read and pData contains the bytes themselves.
-
IOERR_EOF: Read failed because the file pointer was beyond the end of the file at the time of the read.
-
IOERR_UNKNOWN: Read failed for some other reason.
9.5 IOWrite
Writes data from the current file position forward and resets the position to the byte after the last byte written.
Prototype
IOERR IOWrite( HIOFILE hFile, VTBYTE * pData, VTDWORD dwSize, VTDWORD * pCount);
Parameters
-
hFile: Identifies the file where the data is to be written. Should be cast into a pointer to your data structure (MYFILE in the preceding discussion).
-
pData: Points to the buffer from which the bytes should be written. It must be at least dwSize bytes big. It is good practice to treat the data passed in by pData as "read only." This helps prevent unexpected behavior elsewhere in the system.
-
dwSize: Number of bytes to write.
-
pCount: Points to the number of bytes actually written by the function. This value is only valid if the return value is IOERR_OK.
Return Values
-
IOERR_OK: Write was successful, pCount contains the number of bytes written.
-
IOERR_UNKNOWN: Write failed for some reason.
9.6 IOSeek
Moves the current file position.
Prototype
IOERR IOSeek( HIOFILE hFile, VTWORD wFrom, VTLONG lOffset);
Parameters
-
hFile: Identifies the file to be read. Should be cast into a pointer to your data structure (MYFILE in the preceding discussion).
-
wFrom: One of the following values:
-
IOSEEK_TOP: Move the file position lOffset bytes from the top (beginning) of the file.
-
IOSEEK_BOTTOM: Move the file position lOffset bytes from the bottom (end) of the file.
-
IOSEEK_CURRENT: Move the file position lOffset bytes from the current file position.
-
-
lOffset: Number of bytes to move the file pointer. A positive value moves the file pointer forward in the file and a negative value moves it backward. If a requested seek value would move the file pointer before the beginning of the file, the file pointer should remain unchanged and IOERR_UNKNOWN should be returned. Seeking past EOF is allowed. In that case IOERR_OK should be returned. IOTell would return the requested seek position and IORead should return IOERR_EOF and 0 bytes read.
Return Values
-
IOERR_OK: Seek was successful.
-
IOERR_UNKNOWN: Seek failed for some reason.
9.7 IOTell
Returns the current file position.
Prototype
IOERR IOTell( HIOFILE hFile, VTDWORD * pOffset);
Parameters
-
hFile: Identifies the file to be read. Should be cast into a pointer to your data structure (MYFILE in the preceding discussion).
-
pOffset: Points to the current file position returned by the function.
Return Values
-
IOERR_OK: Tell was successful.
-
IOERR_UNKNOWN: Tell failed for some reason.
9.8 IOGetInfo
Returns information about an open file.
Prototype
IOERR IOGetInfo( HIOFILE hFile, VTDWORD dwInfoId, VTVOID * pInfo);
Parameters
-
hFile: Identifies the file to be read. Should be cast into a pointer to your data structure (MYFILE in the previous discussion).
-
dwInfoId: One of the following values:
-
IOGETINFO_FILENAME: pInfo points to a string that should be filled with the base file name (no path) of the open file (for example TEST.DOC). If you do not know the file name, return IOERR_UNKNOWN. Certain file types (such as DataEase) must know the original file name in order to open secondary files required to correctly view the original file. If you return IOERR_UNKNOWN, these file types do not convert. See IOGENSECONDARY and IOGENSECONDARYW Structures.
-
IOGETINFO_PATHNAME: pInfo points to a string that should be filled with the fully qualified path name (including the file name) of the open file. For example, C:\MYDIR\TEST.DOC. If you do not know the path name, return IOERR_UNKNOWN.
-
IOGETINFO_PATHTYPE: pInfo points to a DWORD that should be filled with the IOTYPE of the path returned by IOGETINFO_PATHNAME. For instance, if you return a DOS path name in the Unicode character set, you should return IOTYPE_UNICODEPATH. Even if redirected IO is in use, this should not be set to IOTYPE_REDIRECT. The value should reflect the style of path to be returned or any other values detailed in EXOpenExport..
-
IOGETINFO_ISOLE2STORAGE: Must return IOERR_FALSE. pInfo is not used.
-
IOGETINFO_GENSECONDARY: pInfo points to a structure of type IOGENSECONDARY. Some file types require supporting files to be opened. These supporting files may contain formatting information or extra data. When using HTML Export, templates may link to other templates, and the paths to those templates must be resolved. Correct handling of IOGETINFO_GENSECONDARY is critical to the operation of the Outside In technology. For a list of these file types, see File Types That Cause IOGETINFO_GENSECONDARY.
Because the developer is in total control of the IO for the primary file, the technology does not know how to generate a path to these secondary files or even if the secondary files are accessible through the regular file system. The IOGETINFO_GENSECONDARY call gives the developer a chance to resolve this problem by generating a new IO specification for the secondary file in question. The developer gets just the base file name (often embedded in the original document or generated from the primary file's name) of the secondary file.
The developer may either use one of the standard Outside In IO types or totally redirect the IO for the secondary file, as well. For more details, see IOGENSECONDARY and IOGENSECONDARYW Structures.
-
IOGETINFO_SUBDOC_SPEC: This message should be handled only if the currently open file is an archive and a particular item within the archive is intended to be specified as the input file in a call to DAOpenDocument. In this case, pInfo points to a single-byte character string that should be filled with the subdocument specification of an item within the open file. For example, item.2 specifies item 2 within the archive file. When specifying a subdocument specification, return IOERR_OK. Any other return values cause the results of this message to be ignored.
-
IOGETINFO_64BITIO: For redirected I/O that wishes to use 64-bit seek/tell functions, your IOGetInfo function must respond IOERR_TRUE to this dwInfoId. In addition, the pSeek64/pTell64 items in the baseio structure must be valid pointers to the proper function types.
-
IOGETINFO_DPATHNAME: pInfo points to a structure of type DPATHNAME, which should be filled with the fully qualified path name (including the file name) of the open file, for example, C:\MYDIR\TEST.DOC. If you do not know the path name, return IOERR_UNKNOWN. The dwPathLen element contains the size of the buffer pointed to by the pPath element. If the buffer size is too small to contain the full path, modify dwPathLen to be the correct size of the buffer required to hold the path name in its IOTYPE character width including the NULL terminator and return IOERR_INSUFFICIENTBUFFER.
The following is a C data structure defined in SCCIO.H:
typedef struct DPATHNAMEtag { VTDWORD dwPathLen; VTVOID *pPath; } DPATHNAME, * PDPATHNAME;
Parameters
dwPathLen: Will be set to the number of bytes in the buffer pointed to by pPath. If the size of the buffer is insufficient, reset this element to the number of bytes required and return IOERR_INSUFFICIENTBUFFER.
pPath: Points to the buffer to be filled with the path name.
-
IOGETINFO_GENSECONDARYDP: pInfo points to a structure of type IOGENSECONDARYDP. The dwSpecLen element contains the size of the buffer pointed to by the pSpec element. If the buffer size is too small to contain the spec, modify dwSpecLen to be the correct size of the buffer required to hold the path in its IOTYPE character width including the NULL terminator and return IOERR_INSUFFICIENTBUFFER.
The following is a C data structure defined in SCCIO.H:
typedef struct IOGENSECONDARYDPtag { VTDWORD dwSize; VTVOID * pFileName; VTDWORD dwSpecType; VTVOID * pSpec; VTDWORD dwSpecLen; VTDWORD dwOpenFlags; } IOGENSECONDARYDP, * PIOGENSECONDARYDP;
Parameters
dwSize: Will be set to sizeof (IOGENSECONDARYDP)
pFileName: A pointer to a string representing the file name of the secondary file that the technology requires. It is usually a name stored in the primary file (such as MYSTYLE.STY for a Word for DOS file) or a name generated from the primary file name. The primary file for a DataEase database has a .dba extension. The secondary name is the same file name but with a .dbm extension.
dwSpecType: The developer must fill this with the IOSPEC for the secondary file.
pSpec: On entry, this pointer points to an array of bytes or may be NULL (see dwSpecLen below). If the dwSpecType is set a regular IOTYPE such as IOTYPE_ANSIPATH, the developer may fill this array with the path name or structure required for that IOTYPE. If the developer is redirecting access to the secondary file, then dwSpecType will be IOTYPE_REDIRECT and the developer should replace pSpec with a pointer to a developer-defined structure that begins with the BASEIO structure (see Using Redirected IO).
The file is supposed to be opened by the OEM's redirected IO code by the time they return the BASEIO struct. This is because the pOpen routine in the BASEIO struct is supposed to be NULL.
dwSpecLen: On entry, this is set to the size of the pSpec buffer. If the size of the buffer is insufficient, replace the value with the number of bytes required and return IOERR_INSUFFICIENTBUFFER.
dwOpenFlags: Set by the technology. A set of bit flags describing how the secondary file should be opened. Multiple flags may be used by bitwise OR-ing them together. The following flags are currently used:
- IOOPEN_READ: The secondary file should be opened for read.
- IOOPEN_WRITE: The secondary file should be opened for write. If the specified file already exists, its contents are erased when this flag is set.
- IOOPEN_CREATE: The secondary file should be created (if it does not already exist) and opened for write.
Any other value should return IOERR_BADINFOID.
-
-
pInfo: The size of the pInfo buffer depends on the dwInfoId selected. For IOGETINFO_FILENAME and IOGETINFO_PATHNAME, the buffer is of size MAX_PATH characters (each character is either one byte or two, depending on PATHTYPE). The IOGETINFO_PATHTYPE buffer is the size of a VTDWORD.
Return Values
-
IOERR_OK: GetInfo was successful.
-
IOERR_TRUE: Affirmative response from a true or false GetInfo.
-
IOERR_FALSE: Negative response from a true or false GetInfo.
-
IOERR_BADINFOID: dwInfoId can not be handled by this file type.
-
IOERR_INVALIDSPEC: The file spec is bad for this type.
-
IOERR_UNKNOWN: GetInfo failed for some other reason.
9.8.1 IOGENSECONDARY and IOGENSECONDARYW Structures
These structures are passed to the developer through the IOGetInfo function. They allow the developer to tell the technology where a secondary file, needed by the conversion process, is located.
The SpecType of the original file determines which of these two structures is used. If the SpecType is IOTYPE_UNICODEPATH, IOGENSECONDARYW is used. pFileName points to a Unicode string terminated with a NULL WORD. For all other SpecTypes, IOGENSECONDARY is used and pFileName points to a string terminated with a NULL BYTE.
When using HTML Export, consider the situation where the software must access a secondary template file. In that case, the SpecType of the original template specified by the option SCCOPT_EX_TEMPLATE determines which of the two structures is used.
The following is a C data structure defined in SCCIO.H:
typedef struct { VTDWORD dwSize; VTLPBYTE pFileName; VTDWORD dwSpecType; VTLPVOID pSpec; VTDWORD dwOpenFlags } IOGENSECONDARY, * PIOGENSECONDARY; typedef struct { VTDWORD dwSize; VTLPWORD pFileName; VTDWORD dwSpecType; VTLPVOID pSpec; VTDWORD dwOpenFlags } IOGENSECONDARYW, * PIOGENSECONDARYW;
Parameters
-
dwSize: Will be set to sizeof (IOGENSECONDARY) or sizeof (IOGENSECONDARYW) (both of these values are the same).
-
pFileName: A pointer to a string representing the file name of the secondary file that the technology requires. It is usually a name stored in the primary file (such as MYSTYLE.STY for a Word for DOS file) or a name generated from the primary file name. The primary file for a DataEase database has a .dba extension. The secondary name is the same file name but with a .dbm extension.
-
dwSpecType: The developer must fill this with the IOSPEC for the secondary file.
-
pSpec: On entry, this pointer points to an array of 1024 bytes. If the dwSpecType is set a regular IOTYPE such as IOTYPE_ANSIPATH, the developer may fill this array with the path name or structure required for that IOTYPE. If the developer is redirecting access to the secondary file, then dwSpecType will be IOTYPE_REDIRECT and the developer should replace pSpec with a pointer to a developer-defined structure that begins with the BASEIO structure (see Using Redirected IO).
The file is supposed to be opened by the OEM's redirected IO code by the time they return the BASEIO struct. This is because the pOpen routine in the BASEIO struct is supposed to be NULL.
-
dwOpenFlags: Set by the technology. A set of bit flags describing how the secondary file should be opened. Multiple flags may be used by bitwise OR-ing them together. The following flags are currently used:
-
IOOPEN_READ: The secondary file should be opened for read.
-
IOOPEN_WRITE: The secondary file should be opened for write. If the specified file already exists, its contents are erased when this flag is set.
-
IOOPEN_CREATE: The secondary file should be created (if it does not already exist) and opened for write.
-
9.8.2 File Types That Cause IOGETINFO_GENSECONDARY
The following file types cause IOGETINFO_GENSECONDARY:
-
Microsoft Word for DOS Versions 4, 5 and 6: Used to open and read the style sheet file associated with the document. The filter degrades if the style sheet is not present.
-
Harvard Graphics DOS 3.x: Used to open and read the individual slides within ScreenShow and palette files. Files with the extension .ch3 are individual graphics or slides that can be opened using no secondary files. Files with the extension .sy3 are ScreenShows that reference a list of .ch3 files via the secondary file mechanism. There is also an optional palette file that can be referenced from a .ch3 file, but the filter degrades if the palette file is not present.
-
R:Base: Used to open and read required schema file. The R:Base data files are named ????2.rbf but the data is useless without the schema file named ????1.rbf. There is also a ????3.rbf file associated with each database, but it is not used.
-
Paradox 4.0 and Above: Used to open and read memo field data file. Paradox uses a separate file for all memo field data larger than 32 bytes.
-
DataEase: Used to open and read the data file. DataEase databases include a .dba file that contains the schema (the file that the technology can identify as DataEase) and a .dbm file that contains the actual data.
-
Templates (HTML Export): Any template that contains a
{## link}
will need to open the linked files. Additionally, when the root template is opened using redirected IO, each{## copy}
macro in the template will result in a IOGETINFO_GENSECONDARY call, as well.
9.9 IOSEEK64PROC / IOTELL64PROC
These functions are for seek/tell using 64-bit offsets. These functions are not used by default. Rather, they are used if the IOGETINFO_64BITIO message returns IOERR_TRUE. This is so redirected I/O using strictly 32-bit I/O is unaffected.
9.9.1 IOSeek64
Moves the current file position.
Prototype
IOERR IOSeek64( HIOFILE hFile, VTWORD wFrom, VTOFF_T offset);
Parameters
The parameter information is the same as for IOSeek(). However, the size of the VTOFF_T offset for IOSeek64() is 64-bit unlike the 32-bit offset in IOSeek().