Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Inside Windows Cabinet Files


Dr. Dobb's Journal May 1997: Inside Windows Cabinet Files

Sven, a developer in Herzogenaurach, Germany, can be contacted at [email protected].


Over the years, Windows has provided us with a long list of file formats, some of which have been well documented, others only sparsely. Windows 95 surprised us with yet another archive file format for compressed installation disks -- the cabinet file format. Subsequently, Microsoft has used this format in a number of packages, including Office 95, Internet Explorer, and the MSDN Library CD.

Generally, the only place you could get useful information on the cabinet file format was a shell extension demo for Windows 95 called "CabView," a utility available with full MFC source code at http://www.microsoft.com/win32dev/apiext/cabview.zip. The file FDI.h contains many (albeit not all) details about the internals of cabinet files. For instance, you learn that cabinets are processed using the Diamond File Decompression Interface (FDI) and File Compression Interface (FCI).

Microsoft has recently decided to give cabinets an important role in its Java strategy. On Microsoft's Java workshop web pages, cabinet files are touted as a means to reduce download times of Java class files (see cab.htm, overview.htm, and diamond2.htm at http://www.microsoft.com/ workshop/java/). Meanwhile, CAB development and resource kits are publicly available (cabdevkt.exe and cabrsckt.exe in the same directory). It's mainly the resource kit that's of interest to you, since it contains some .h and .lib files needed to write cabinet processing utilities.

If you are writing a CAB extraction utility, your source code must include TYPES.h and FDI_INT.h (in this order) from the resource kit. In addition, you have to add FDIZ.lib to the linker's list of object/library modules. For a CAB creation utility, you'll need TYPES.h, FCI_INT.h, and FCIZ.lib, respectively. At this writing, however, the CAB resource kit is still in prerelease, and I had considerable problems with its header and import-library files. I didn't get the header files to compile without error, and FDIZ.lib put code into my executable that made it unstable under Win32s. Therefore, I'm still using the FDI files from Microsoft's Win32 SDK and the CabView sample program, which work perfectly.

The resource kit's README file says: "Microsoft is committed to making CAB an open technology." And a bit later, "A detailed description of the CAB file format will be provided in the next release." That's good news, but who knows when the next release will arrive? After reading this article, however, you should know almost everything you need to know about the internals of cabinet files. Furthermore, I provide a reusable CAB management DLL and sample CAB extraction utility (available electronically; see "Availability" page 3).

Structure of a CAB File

A CAB file consists of a header and a data section. Although FDI.h from the CabView sample program gives some important insights into some CAB handling issues, it tells you nothing about the internal structure of these sections. Listing One an excerpt from one of the sample header files, shows some of the missing details. The member names used in the structures are actually the original ones, taken from the debug information of the FDI import library.

At the beginning, you'll always find a fixed-length, 36-byte main header structure, CAB_HEADER. To find out if a given file is a CAB, it usually suffices to check the first four bytes (that is, the sig member), which should consist of the characters MSCF (Microsoft CAB File). Additionally, it's a good idea to check the version member, currently set to 0x0103, which probably signifies Version 1.3 (see CAB_VERSION in Listing One). The remaining parts of the CAB file header section may vary in length, depending on what's in the file.

The CAB data is basically organized in folders. Unlike the folders of an operating-system file system, CAB folders can't contain subfolders. A folder is a logical group of files that share the same properties. All files in a folder are compressed using the same algorithm. Moreover, compression is applied to folders, not individual files -- it runs across all files in a folder. This means that extraction of a single file from a folder is possible only after all other files in the same folder have been decompressed.

If you examine the CAB files on the Windows 95 setup disks, it's striking that most of them are of exactly the same size. The CAB file format allows files to start in one CAB and continue in a subsequent CAB; CAB files contain a chaining mechanism that ties them together in a logical sequence.

In some cases, a CAB file may contain extra data for special purposes. For instance, Microsoft recommends reserving 6144 bytes for code signing of CABs containing Java classes. This extra data is inserted after the CAB_HEADER structure, and consists of a DWORD specifying the number of reserved bytes and the reserved area itself. To indicate the presence of extra data, the CAB_FLAG_ RESERVE bit (bit 2, see Listing One) in the flags member of the CAB_HEADER is set.

To implement chaining, the CAB_ HEADER structure and a potential reserved area can be followed by one or two link entries. To see if and how many link entries are present, check the flags member of the main header. In Listing One, the CAB_FLAG_HASPREV (bit 0) and CAB_ FLAG_HASNEXT (bit 1) bit masks are defined. Depending on which bits are set, you'll find none, one, or two link entries. In the latter case, the previous-CAB link will always precede the next-CAB link.

The link entries consist of two null-terminated strings, where the first is the filename (without path) of the preceding CAB file, and the second indicates the disk name. In Microsoft CABs, the latter is usually a disk number, used when the system is prompting you to insert a setup CD or diskette.

Folders and Files

Next are one or more CAB_FOLDER structures. If neither the CAB_FLAG_RESERVE nor the CAB_FLAG_HASPREV nor the CAB_FLAG_HASNEXT flags are set, the first CAB_FOLDER follows immediately after the CAB_HEADER. To find out how many folders are present, check the cFolders member of the CAB_HEADER.

The CAB_FOLDER structure indicates what compression method is used (member typeCompress). Microsoft currently uses the MSZIP algorithm, indicated by a value of 0x0001 (tcompTYPE_MSZIP in FDI.h), but other methods might come into play later. The coffCabStart member contains the file offset where the folder data starts. This member is also of considerable importance for quickly determining the size of the header portion of a CAB file.

The final part of the CAB header section is the file directory, and it is probably the most important subsection for CAB extraction utility writers. It specifies what files are in the CAB, and contains information on the individual files -- size, date/time stamp, and attributes. The file directory is a sequence of CAB_ENTRY structures (see Listing One), where each CAB_ENTRY is followed by a null-terminated filename string. The cFiles member of the main CAB header indicates how many entries are to be expected.

The cbFile member states the uncompressed size of a file. The date and time members contain the original date/time stamp of the file, as known from DOS. Similarly, the attribs member indicates the file's attributes in DOS format (see the CAB_ ATTRIB_* constants in Listing One for details). uoffFolderStart is the offset of the file after decompression. Remember that compression works on whole folders, not single files, so this value is needed to find out at which offset in the decompressor's data stream a given file is located.

Finally, there's the iFolder member in each CAB_ENTRY. Its value tells you how to handle this entry while extracting files. Usually, this member contains 0x0001 (CAB_ FILE_NEXT in Listing One), except for the first file in a folder, where it is set to 0x0000 (CAB_FILE_FIRST). However, you'll also observe values of 0xFFFE (CAB_ FILE_SPLIT) and 0xFFFD (CAB_FILE_CONTINUED). The former appears when a file starts in one CAB, but ends in another. In this case, the subsequent CAB file will contain a directory entry for the same file, but be tagged with an iFolder value of CAB_FILE_CONTINUED. That's the way the CAB file format allows files to span multiple CABs.

The file directory completes the header section of a CAB file. The compressed data follows immediately after the last CAB_ENTRY and its associated filename string. The offset of the data section is the value that's stored as the coffCabStart member of the CAB's first CAB_FOLDER structure and is of vital importance for CAB utility developers, since it tells you how many bytes must be read from the beginning of the CAB to catch all header information. Thus, to read the complete header section of a CAB, you should:

1. Read the CAB_HEADER.

2. Read more data up to the offset indicated by the coffFiles member. This member tells you at which offset the CAB directory starts.

3. Skip the reserved data area if the flags member has the CAB_FLAG_RESERVE bit set.

4. Skip the previous-CAB link if the flags member has the CAB_FLAG_HASPREV bit set.

5. Skip the next-CAB link if the flags member has the CAB_FLAG_HASNEXT bit set. This takes you to the first CAB_FOLDER structure inside the CAB.

6. Read more data up to the file offset indicated by the coffCabStart member.

After step 6, you can be sure that you've loaded all CAB_FOLDERs and the complete CAB directory in just three takes. The OpenCabinet() function from the sample DLL I present here does exactly that.

The File Decompression Interface

The data area is a sequence of compressed folders, where each folder is a concatenation of files. To decompress a folder, you need to know the algorithm used to compress them. Fortunately, it's not necessary to write the decompression code from scratch. The FDI.lib import library, distributed as part of the Win32T SDK for Windows NT 4.0, contains everything you need. If you don't have the Win32T SDK, use the FDI.lib that comes with Microsoft's CabView sample program.

Unlike many import libraries, FDI.lib does not simply contain stub entries that establish links to some external DLL. Instead, FDI.lib contains the FDI code itself. There's no such thing as FDI.dll. When you link FDI.lib into your program, the FDI code is thrown into your executable.

The FDI.lib import library contains a set of APIs, four of which are documented in FDI.h. FDICreate() must be called before any of the other APIs can be used. This function expects a series of callback functions and allocates 2052 bytes of heap memory as scratch space. The pointer to this storage area is called a "FDI Context Handle" (defined as HFDI in FDI.h). To destroy the context handle on cleanup, the FDIDestroy() API is available. This function closes all file handles that might still hang around, and deallocates all memory used by the FDI context, including the context handle block itself.

FDIIsCabinet() tests if the specified file handle refers to a CAB file. To do so, it tries to read the first 36 bytes (that is, the CAB_HEADER). If it succeeds, it examines the sig and version members. If this sanity check turns out okay, FDIIsCabinet() copies some values from the CAB_HEADER structure to the supplied FDICABINETINFO structure (defined in FDI.h). Since FDIIsCabinet() operates on file handles only, you have to open the file to be examined prior to calling this API. Finally, there's FDICopy(), a multipurpose function whose main task is to extract files from a series of CABs.

The FDI code doesn't use any Win32 APIs or C run-time library calls. Instead, it relies on callbacks into its host program when it has to interact with its environment. This is kind of a miniature client/server technology, with the FDI code acting as the client and the main program as the server. FDI uses callbacks to manipulate files, allocate and deallocate memory, and iterate through the directories of CAB files. Most callback functions are passed to the FDI client via the FDICreate() API. Example 1 is its prototype.

Arguments starting with "pfn" are callback function pointers, prototypes of which are defined in FDI.h. FDI accepts functions that conform to the standard C run-time library functions malloc(), free(), _open(), _read(), _write(), _close(), and _lseek(). It's up to you whether you want to use these standard functions or write your own. In CabLib.c (available electronically), I define them exclusively in terms of Win32 API calls, because this saves executable from being bloated with C run-time library code.

Callback implementation is trivial, except for the open function, where it is a game of patience until all possible _open() flags are mapped to their corresponding Win32 CreateFile() counterparts. This task is rendered more difficult because some CreateFile() flags don't work as expected in some networking environments. For instance, I recommend you refrain from using the TRUNCATE_EXISTING and FILE_FLAG_ WRITE_THROUGH flags. To work around TRUNCATE_EXISTING, use SetEndOfFile() immediately after opening the target file. This seems to work on all platforms.

The cpuType argument is ignored under 32-bit Windows and should be set to cpuUNKNOWN. perf is a pointer to a global ERF structure that contains error information after an FDI call has failed. ERF is defined in FDI.h, too.

Extracting Files

What seems to be missing in the previous FDI discussion is an API to scan the contents of a CAB file -- something analogous to the Win32 APIs FindFirstFile() and FindNextFile(). How are you supposed to find out what files are stored inside a CAB? As it turns out, scanning a CAB directory is a special case of extracting files without actually writing them to disk. Hence, FDICopy() is the function of choice; see Example 2.

Again, FDI uses callbacks to do the dirty work. The pfnfdin argument points to a notification function called by FDI whenever there's something to do. After calling FDICopy(), the calling program should be prepared to handle numerous notification callbacks. The callback function receives (among others) a notification for every file that starts in the CAB specified as pszCabinet (notification code 2 = fdintCOPY_FILE). The notification function can decide what to do with this information: It decompresses and writes this file to disk, or adds the file's name to a listbox control without actually extracting it.

The fully specified path of the cabinet file to be scanned is built from the pszCabPath and pszCabinet arguments. The former specifies the path without a filename, while the latter is just a filename without any path information. This separation makes sense when you remember that FDI sometimes has to extract a file that spans multiple CABs. In this case, FDI can read the name of the next cabinet from the CAB header section and simply prepend it with the path information. pszCabPath must always contain a trailing backslash; otherwise, FDICopy() will fail.

FDICopy() does not return until either the entire cabinet has been scanned or the operation is aborted. The notification function usually returns 1 to signal okay, or 0 if an error occurs. In the special case of fdintCOPY_FILE, 0 means that FDICopy() should proceed to the next file without extracting the current file. For all notifications not handled by the callback routine, it's best to return 1, since this will always be interpreted as success. Returning -1 aborts the callback immediately and forces FDICopy() to return an error code, which you may look up in the ERF structure you supplied earlier to FDICreate().

Along with the notification code, the FDICopy() callback routine receives a pointer to a FDINOTIFICATION structure (see FDI.h). This structure supplies data the callback function might need to complete its mission. For example, the psz1 member points to the name of the next file in the CAB's directory if the notification code is fdintCOPY_FILE. The callback function uses this information to decide whether this is one of the files it wants to extract. On fdintCLOSE_FILE_INFO, hf is the file handle to be closed, and date and time contain its time stamp information that should be put on the target file before it's closed.

The pv member of FDINOTIFICATION plays a crucial role. This is a user-defined PVOID that may point to anything the application would like to. Typically, this is a pointer to an application-dependent structure containing state information. One possible use might be a pointer to the name of the file to be extracted. The sample code I provide here uses this member as a pointer to a structure that holds, among other data, the main dialog's window handle, so the callback function is able to pop up modal error windows if it needs to.

In any case, the callback function should sometimes expect to receive an fdintNEXT_CABINET notification. This code is passed in if a file that's currently extracted is continued in a different CAB file. On this notification, the function should check if the next CAB file is available and return -1 if this is not the case. Failing to detect a missing CAB and returning okay blindly will most probably cause your application to hang. Don't expect to get a second chance for error handling!

The Sample Code

The complete source code for this article is available electronically (again, see "Availability," page 3). Listing Two is a typical FDICopy() callback function used to extract a single file from a CAB. Here, FDIOpen() and FDIClose() are FDI callbacks to open and close files, pointers to which are supplied to FDICreate() as pfnopen and pfnclose arguments. These could be the standard _open() and _close() C run-time functions as well. And printfMessage() is a convenient function that applies the features of printf() to message boxes. I use it here to format error messages.

The sample code is structured in two main parts: CabLib.dll, a general-purpose CAB extraction DLL you can reuse, as is, in your own applications, and a sample dialog-based Cabinet Manager application that shows how to use the various APIs exported from CabLib.dll. I thought about calling it CabMan.exe, but, with so many "male" managers available (ProgMan, TaskMan, and the like), I preferred to call it "CabWoman.exe." I've tested CabLib and CabWoman under Windows NT 4.0, Windows NT 3.51, Windows 95, and Windows 3.11 with Win32s 1.30c.

Table 1 lists the functions exported from CabLib.dll. CabWoman.c (available electronically) shows how the APIs interact in a real-world application. In short, the CabLib philosophy rests on the assumption that CAB file processing is done in two stages: The CAB directory is first obtained, then individual files inside the CAB are processed.

In stage one, OpenCabinet() prepares the CAB for processing. Then, ScanCabinet() is called until it returns False. On each ScanCabinet() call, directory information for one file is made available. In a GUI application, this info is typically stored in a dialog listbox. CloseCabinet() closes the CAB file and returns the number of bytes processed so far. This value will be needed later for FindCabinetEntry(). At the end of stage one, you have obtained a complete CAB directory.

In stage two, individual CAB contents are manipulated. You can use FindCabinetEntry() to retrieve directory data for a specified file, or ExtractFileFromCabinet() to write a file to disk. FindCabinetEntry() requires the return value of CloseCabinet() as its dOffset argument, which allows this function to read just as many data bytes as needed to find the required directory entry. ExtractFileFromCabinet() uses the FDI interface function FDICopy(), as well as the FDIExtract() callback function (see Listing Two) to do its job.

CabWoman.exe is a middle-of-the-road, dialog-based GUI application that acts like a simple file manager, restricted to CAB files. Unlike the CabView sample program from Microsoft, CabWoman does not operate on single CABs, but instead lists the contents of all CAB files found in the selected directory in a single listbox. This makes much sense, since CAB files are usually chained and constitute a single entity.

Navigation inside CabWoman's main dialog is simple. In all of the four listboxes, a double-click or the Enter key triggers some action. In the volume and directory lists, the respective item is selected. In the cabinet list, internal information about the CAB file is shown in a separate window (see Figure 1). In the file list, the double-clicked file is selected for extraction. This is the same as clicking on the Extract button. Moving the section bar in the volume and file listboxes updates the information fields at the right margin of the main dialog window. CabWoman stores the current navigation status in a file named CabWoman.ini in the Windows root directory, so you can continue where you left off in your next CabWoman session.

Windows NT 3.51 users need to install CTL3D32.dll in the %systemroot%\SYSTEM32 directory to get 3-D effects in CabWoman's dialog boxes. CabWoman.c contains extra code for this, trying to locate, load, and initialize CTL3D32.dll. If it doesn't succeed, no error is reported, but no 3-D effects will appear. If you don't have a CTL3D32.dll, try the one included in the sample code archive. Win32s users are out of luck -- there's no 3-D library for this flavor of Win32.

Summary

The CAB file format has been Microsoft's favorite archive format since Windows 95 was released. However, little information about CAB internals has been available so far. This is likely to change, since Microsoft promotes the CAB format as a means to bundle and compress Java class files to save download time. While prerelease versions of a CAB file SDK and resource kit are distributed now, the internals of CAB files are still undocumented. As this article shows, however, knowing about CAB's inner structure and support DLLs makes it easy to include CAB extraction support in your own applications.

DDJ

Listing One

// =================================================================// CAB FILE LAYOUT
// =================================================================
/*
(1) CAB_HEADER structure
(2) Reserved area, if CAB_HEADER.flags & CAB_FLAG_RESERVE
(3) Previous cabinet name, if CAB_HEADER.flags & CAB_FLAG_HASPREV
(4) Previous disk name, if CAB_HEADER.flags & CAB_FLAG_HASPREV
(5) Next cabinet name, if CAB_HEADER.flags & CAB_FLAG_HASNEXT
(6) Next disk name, if CAB_HEADER.flags & CAB_FLAG_HASNEXT
(7) CAB_FOLDER structures (n = CAB_HEADER.cFolders)
(8) CAB_ENTRY structures / file names (n = CAB_HEADER.cFiles)
(9) File data (offset = CAB_FOLDER.coffCabStart)
*/
// =================================================================
// MACROS
// =================================================================
#define SWAPWORD(x)  ((WORD) (((x) << 8) | ((x) >> 8)))
#define SWAPDWORD(x) ((SWAPWORD ((WORD) (x)) << 16) | \
                      (SWAPWORD ((WORD) ((x) >> 16))))
// =================================================================
// CONSTANTS
// =================================================================
#define CAB_SIGNATURE        SWAPDWORD ('MSCF')
#define CAB_VERSION          0x0103


</p>
#define CAB_FLAG_HASPREV     0x0001
#define CAB_FLAG_HASNEXT     0x0002
#define CAB_FLAG_RESERVE     0x0004


</p>
#define CAB_ATTRIB_READONLY  0x0001
#define CAB_ATTRIB_HIDDEN    0x0002
#define CAB_ATTRIB_SYSTEM    0x0004
#define CAB_ATTRIB_VOLUME    0x0008
#define CAB_ATTRIB_DIRECTORY 0x0010
#define CAB_ATTRIB_ARCHIVE   0x0020


</p>
#define CAB_FILE_FIRST       0x0000
#define CAB_FILE_NEXT        0x0001
#define CAB_FILE_SPLIT       0xFFFE
#define CAB_FILE_CONTINUED   0xFFFD


</p>
#define CAB_NOTIFY_OK        1
#define CAB_NOTIFY_ERROR     0
#define CAB_NOTIFY_SKIP      0
#define CAB_NOTIFY_ABORT     (-1)


</p>
// =================================================================
// CABINET STRUCTURES
// =================================================================
typedef struct _CAB_HEADER
    {
    DWORD sig;              // file signature 'MSCF' (CAB_SIGNATURE)
    DWORD csumHeader;       // header checksum (0 if not used)
    DWORD cbCabinet;        // cabinet file size
    DWORD csumFolders;      // folders checksum (0 if not used)
    DWORD coffFiles;        // offset of first CAB_ENTRY
    DWORD csumFiles;        // files checksum (0 if not used)
    WORD  version;          // cabinet version (CAB_VERSION)
    WORD  cFolders;         // number of folders
    WORD  cFiles;           // number of files
    WORD  flags;            // cabinet flags (CAB_FLAG_*)
    WORD  setID;            // cabinet set id
    WORD  iCabinet;         // zero-based cabinet number
    }
    CAB_HEADER, *PCAB_HEADER;
#define CAB_HEADER_ sizeof (CAB_HEADER)


</p>
//  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  -- -
typedef struct _CAB_FOLDER
    {
    DWORD coffCabStart;     // offset of folder data
    WORD  cCFData;          // ???
    WORD  typeCompress;     // compression type (tcomp* in FDI.h)


</p>
    }
    CAB_FOLDER, *PCAB_FOLDER;
#define CAB_FOLDER_ sizeof (CAB_FOLDER)


</p>
//  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  -- -
typedef struct _CAB_ENTRY
    {
    DWORD cbFile;           // uncompressed file size
    DWORD uoffFolderStart;  // file offset after decompression
    WORD  iFolder;          // file control id (CAB_FILE_*)
    WORD  date;             // file date stamp, as used by DOS
    WORD  time;             // file time stamp, as used by DOS
    WORD  attribs;          // file attributes (CAB_ATTRIB_*)
    }
    CAB_ENTRY, *PCAB_ENTRY;
#define CAB_ENTRY_ sizeof (CAB_ENTRY)

Back to Article

Listing Two

// =================================================================// FILE EXTRACTION CALLBACK
// =================================================================
typedef struct _EXTRACT_FILE
    {
    HWND hWnd;
    PBYTE pbEntry;
    PBYTE pbFile;
    }
    EXTRACT_FILE, *PEXTRACT_FILE;
#define EXTRACT_FILE_ sizeof (EXTRACT_FILE)


</p>
int DIAMONDAPI FDIExtract (FDINOTIFICATIONTYPE fdint,
                           PFDINOTIFICATION    pfdin)
    {
    FILETIME ft, lft;
    BYTE     abFile [MAX_PATH];
    int      hf, iResult;


</p>
    switch (fdint)
        {
        case fdintCOPY_FILE:
            if (lstrcmpi (pfdin->psz1,
                          ((PEXTRACT_FILE) pfdin->pv)->pbEntry))
                {
                iResult = CAB_NOTIFY_SKIP;
                }
            else
                {
                hf = FDIOpen (((PEXTRACT_FILE) pfdin->pv)->pbFile,
                              _O_RDWR | _O_CREAT | _O_TRUNC,
                              _S_IREAD | _S_IWRITE);
                if (hf != -1)
                    {
                    iResult = hf;
                    }
                else
                    {
                    printfMessage
                        (((PEXTRACT_FILE) pfdin->pv)->hWnd,
                          MB_ICONERROR | MB_OK,
                          "CabLib Error",
                          "Unable to create file \"%s\"",
                          ((PEXTRACT_FILE) pfdin->pv)->pbFile);
                    iResult = CAB_NOTIFY_ABORT;


</p>
                    }
                }
            break;
        case fdintNEXT_CABINET:
            if (lstrlen (pfdin->psz3) + lstrlen (pfdin->psz1)
                < MAX_PATH)
                {
                lstrcpy (abFile, pfdin->psz3);
                lstrcat (abFile, pfdin->psz1);
                hf = FDIOpen (abFile, _O_RDONLY, 0);
                }
            else
                {
                hf = -1;
                }
            if (hf != -1)
                {
                FDIClose (hf);
                iResult = CAB_NOTIFY_OK;
                }
            else
                {
                printfMessage (((PEXTRACT_FILE) pfdin->pv)->hWnd,
                               MB_ICONERROR | MB_OK,
                               "CabLib Error",
                               "Unable to open file \"%s%s\"",
                               pfdin->psz3,
                               pfdin->psz1);
                iResult = CAB_NOTIFY_ABORT;
                }
            break;
        case fdintCLOSE_FILE_INFO:
            DosDateTimeToFileTime (pfdin->date, pfdin->time, &lft);
            LocalFileTimeToFileTime (&lft, &ft);
            SetFileTime ((HANDLE) pfdin->hf, &ft, &ft, &ft);


</p>
            if (FDIClose (pfdin->hf) == -1)
                {
                printfMessage (((PEXTRACT_FILE) pfdin->pv)->hWnd,
                               MB_ICONERROR | MB_OK,
                               "CabLib Error",
                               "Unable to close file \"%s\"",
                               ((PEXTRACT_FILE) pfdin->pv)->pbFile);
                }
            iResult = CAB_NOTIFY_OK;
            break;
        default:
            iResult = CAB_NOTIFY_OK;
            break;
        }
    return iResult;
    }

Back to Article


Copyright © 1997, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.