Cabinet File Utility Classes

-->

Cabinet files are multivolume compressed archives To view what's in a cabinet file, you can install a cabinet viewer utility from PowerToys. And also, the latest WinZip 7 Beta can view cabinet files.

If you ever wondered how to create those compressed cabinet (.cab) files that lots of setup kits use to pack files? Then you might have found out that you can actually create a cabinet file using a command line tool, named cabarc.exe that comes with Visual C++ 5.0 and higher (just search your MSDN CD for cabarc.exe).

However, if you want to build cabinet files from within your program, this tool is not of much help. Sure, you could write a wrapper around it and lauch it in the background whenever you need to manufacture a cab file. Not very elegant tough. In this article I'll show you how you can do this with some C++ classes and without the use of any external tool.

The cabinet files are in fact multivolume compressed (encrypted) archives. They have lots of features, including code signing (well, the cabinet file itself is actually signed, with the meaning that what it contains comes from a well-known provider) file spanning from one cabinet to another, etc. Thus, you won't be surprised that their internal format is quite complicated. However, there is a library available from MS that knows how to deal with those cab files. It can both create them and extract files from them. The library is distributed as a .dll (cabinet.dll) along with documentation on how to use it. You can download this Cabinet SDK from http://msdn.microsoft.com/workshop/management/cab/cabdl.asp. The file cabinet.dll is also included in the Windows core system file list and is part of the Win98 and Windows NT 4.0 operating systems (see the Windows Logo Requirements for more info).

Now, once you got that Cabinet SDK, you can stop reading this article if wou're a convinced C programmer. For everyone else, I wrote some C++ classes that wrap all those plain-vanilla (boring and compicated enough) C funtions the file cabinet.dll exports. There are two classes, one for creating cabinets (CCabinetBuilder) and one for exracting files from a cabinet file (CCabinetExtractor). These classes are declared and implemented in the files Cabinet.Hpp and Cabinet.Cpp respectively.

The cabinet classes were written so that they can be used from both MFC and non-MFC projects. Simply define the symbol __MFC__ if you're goin to use them from MFC stuff. If is also possible that only the cab creation or extraction code be used (however, this will only exclude the unused C++ classes but you'll still need the file cabinet.dll) by defining the symbols:

__CAB_BUILD__
__CAB_EXTRACT__

Only define the one that you'll actually use. E.g. if you only want to create cab files, define __CAB_BUILD__.

I'll show you how to use the classes to build and extract files from a cabinet file. First, let's create a cabinet file and put some files in it. Creating a cabinet file is dumb easy: you create an instance of the CCabinetBuilder class telling it the attbibutes of your new cab file, init it using Init and specifying where you actually want the cabinet file(s) to be created and then addding files in the cabinet.

If the accumulated (compressed) size of the files you add will grow larger that what would fit in a single cabinet file (you specified the volume size when you created the instance of the class) a new cabinet file will automatically be created. This is useful for creating cabinet files that would fit on floppy disks. So, you can get multiple volume cabinet files automatically!

And now let's see how it's done. We'll make a cabinet with ID 12345, volume size is 1.44M:

CCabinetBuilder cb(12345,1440000,1440000,250000);
if(cb.InitCabinet("D:\\Temp\\Test","Setup1","Disk"))
{
 // And now we add the files
 if(!cb.AddFile("D:\\Temp\\Reboot.Log") ||
 !cb.AddFile("D:\\Temp\\sysreport.bmp") ||
 !cb.AddFile("D:\\Temp\\levyaway.txt") ||
 !cb.AddFile("D:\\Temp\\Blackbug.avi") ||
 !cb.AddFile("K:\\Microsoft\\Outlook 98 Beta 2\\mpi95_2s.cab"))
 // Some error
  TRACE("Could not add file(s) ([d][%d][%d])\n",
   cb.GetErrorCode(),
   cb.GetErrorCodeEx(),
   errno);
}

After you've got a cabinet file it is absolutely normal that at some point you will want to extract the files from it. This operation is also easy, thanks to the CCabinetExtractor class. Just make an instance of it, tell it where you want the files to be extracted using SetDefaultExtractPath and extract the files from some cab file with ExtractFiles.

The very simple sample code is here:

CCabinetExtractor ce;
ce.SetDefaultExtractPath("D:\\Temp\\Extract");

if(!ce.ExtractFiles("D:\\Temp\\Test\\Setup1.cab"))
 // Some error
 TRACE("Could not add file(s) ([d][%d][%d])\n",
  cb.GetErrorCode(),
  cb.GetErrorCodeEx(),
  errno);

With this simple samples you can create and extract files from cabinet file in a snap. However, the cabinet library sends notifications for almost every operation it does, so that clients of the library can alter its behaviour. It does this by calling some C funtions that clients (you, actually the cabinet classes) register with it. These notifications are wired to static members of the cabinet classes.

Because the notifications the compression library sends are quite complicated, I added code to handle them all resonably and then designed a few simple virtual notifications that you will probably want to use and override. I'll shortly descrube these simple notifications below.

For class CCabinetBuilder, the simple notifications are:

// Miscellaneous helpers
public:
 virtual void ManufactureDiskName(PCCAB cabinfo);
 virtual void ManufactureCabinetName(PCCAB cabinfo);

ManufactureDiskName and ManufactureCabinetName are called when the cabinet engine is about to create a new cabinet volume because it already reached the volume limit with the current one. You simply supply the name of the next cabinet file and its internal (disk) name in the passed structs.

For class CCabinetExtractor, the simple notifications are:

// Miscellaneous helpers
protected:
 virtual BOOL NotifyCabinetInfo(USHORT nID, LPCTSTR lpcszCabPath, LPCTSTR lpcszCabName, LPCTSTR lpcszDiskName, USHORT nCabIndex);
 virtual BOOL NotifyPartialFile(LPCTSTR lpcszFileName, LPCTSTR lpcszFirstCab, LPCTSTR lpcszFirstDisk);
 virtual BOOL NotifyFileCopy(LPCTSTR lpcszFileName, ULONG cbSize, string &strExtractTo, BOOL &bSkipFile);
 virtual BOOL NotifyFileCopied(LPCTSTR lpcszFileName, USHORT &nDate, USHORT &nTime, USHORT &nAttribs);
 virtual BOOL NotifyFileCopiedAndClosed(LPCTSTR lpcszFileName); 
 virtual BOOL NotifyEnumerate(USHORT nID, long &nCurrentPosition, USHORT &nFilesRemaining);
 virtual BOOL NotifyNextCabinet (LPCTSTR lpcszNextCab, LPCTSTR lpcszNextDisk, string &strNextCabPath, int nErrorCode);

NotifyCabinetInfo is called whenever the cab engine wants info about a cabinet file it is about to extract from. If the first file in the cabinet is continuation from some previous cabinet, then you're informed via NotifyPartialFile. NotifyFileCopy is called for each file in the cabinet so that you can decide whether or not to extract it. The default implementation of this member simply answers with "Yes, extract it" for every file. NotifyFileCopied and NotifyFileCopiedAndClosed are called when the cabinet engine finished extracting some file from the cabinet and when it closed it too, respectively. With NotifyEnumerate you can enumerate files in the cabinet and with NotifyNextCabinet you can tell the cabinet library the name of the cab file where the one currently being extracted (actually some file in it, that spans across cab file boundary) is continued.

By having some static functions in a class isn't really a very extensible solution, so I added virtual functions to each of the cabinet classes that return addresses of these statix. If you want to do something that I didn't designed provision for, you could derive your class from mine, write a whole new static funtion to handle a notification from the cabinet engine (you have all the necessary documentation in the Cabinet SDK), then return its address via the virtual function. Very poverful indeed!

If you, by any reason (possibly you derived your classes from mine and suddenly compression/decompression doesn't work anymore - nice, eh?), want to see how compression and/or decompression progresses, you might want to define one of these symbols:

__TRACE_CAB_MEMORY__
__TRACE_CAB_COMPRESSION__
__TRACE_CAB_EXTRACTION__

Download source - 142 KB



Comments

  • How to add path details of file added in cab file

    Posted by Legacy on 04/12/2002 12:00am

    Originally posted by: Dipesh

    Hi,
    I have created a cab file using CAB SDK's provided by Microsoft.
    I want to add the path details i.e where the file should get copied once it is unzipped.
    Winzip stores this path information.

    Please help me on this

    Thanks,
    Dipesh

    Reply
  • extract 1 file from .cab

    Posted by Legacy on 01/29/2002 12:00am

    Originally posted by: Dian Suharto

    Hi,

    Just want to know how to extract 1 file only from .cab file.

    Say that I want to read "readme.txt" from .cab file contains many files on it.

    Any ideas how to make it?

    Thank you in advance.

    Best regards, Dian Suharto.

    Reply
  • Can I write an ActiveX Component with this class?

    Posted by Legacy on 01/25/2002 12:00am

    Originally posted by: namkyu lee

    Please show me how to....

    Reply
  • Can I write an ActiveX Component with this class?

    Posted by Legacy on 01/25/2002 12:00am

    Originally posted by: namkyu lee

    Please show me how to....

    Reply
  • Copying cab file diskettes

    Posted by Legacy on 07/25/2001 12:00am

    Originally posted by: Dave Camp

    After a lot of searching I have finally got my hands on a copy of Win95 on diskette to upgrade my portable from Win3.1.  (No CD option)
    
    

    I would like to make a back-up copy to ensure I have a spare copy. At present I have copied the diskettes on to the hard disk of my desktop PC.

    Question? How do I copy these diskettes either diskette to diskette or hard disk to diskettte?

    Dave C


    Reply
  • How to list archive contents ?

    Posted by Legacy on 03/30/2001 12:00am

    Originally posted by: Miroslav Rajcic

    Is there a function to list archive contents?

    Reply
  • Okay... And now for the stupid question....

    Posted by Legacy on 12/28/2000 12:00am

    Originally posted by: Scott Pawluk

    Okay... I know this is a stupid one that's gonna get a lot of laughs... but here it is anyways...


    How would I use the cabinet.dll in a Visual Basic program?

    Anyone with any knowledge of how to do this.... email me directly at petskippy@hotmail.com

    I'd greatly appreciate this.

    Reply
  • Is there any good way to know the progressing percentage of compressing ?

    Posted by Legacy on 11/27/2000 12:00am

    Originally posted by: DongSoo Kim

    I can see that

    CCabinetBuilder::NotifyCompress(ULONG cbOriginal, ULONG cbCompressed)

    is responsible for progressing percentage of compressing.

    But I can't figure out the cbOriginal.

    So I have to estimate the size of cbOriginal.

    Am I right ? Is there reasonable way to get cbOriginal ?


    Reply
  • Is there any good way to know the progressing percentage of compressing ?

    Posted by Legacy on 11/27/2000 12:00am

    Originally posted by: DongSoo Kim

    I can see that

    CCabinetBuilder::NotifyCompress(ULONG cbOriginal, ULONG cbCompressed)

    is responsible for progressing percentage of compressing.

    But I can't figure out the cbOriginal.

    So I have to estimate the size of cbOriginal.

    Am I right ? Is there reasonable way to get cbOriginal ?


    Reply
  • error C2065: 'CString' : undeclared identifier

    Posted by Legacy on 11/08/2000 12:00am

    Originally posted by: Matt

    I get this when compiling "stringhelper.hpp"

    How do I get rid of this error?

    I am building in VC++ v.6 using MFC and have defined __MFC__.

    Any help is greatly appreciated.

    Matt

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • CentreCorp is a fully integrated and diversified property management and real estate service company, specializing in the "shopping center" segment, and is one of the premier retail service providers in North America. Company executives travel a great deal, carrying a number of traveling laptops with critical current business data, and no easy way to back up to the network outside the office. Read this case study to learn how CentreCorp implemented a suite of business continuity services that included …

Most Popular Programming Stories

More for Developers

RSS Feeds