Creating an Object-Oriented Interface to CAB Files (
Page 1 of 3 )
Reading and Writing CAB Files from .NET, Part 3: Jim Mischel winds up his series on reading and writing cabinet files. He learned a few lessons — the hard way — about value semantics and .NET debugging. Gain wisdom from his experience, so you
Worse, that's not the .NET way of doing things. A better way would be to wrap the Cabinet SDK functionality into objects that let you
overload virtual methods and respond to events. That's the topic of this final article.
ADVERTISEMENT
Design Goals
When wrapping an API into a class library, one of the most important things to consider (perhaps the most important thing) is what benefit the proposed classes offer over the raw API calls. It doesn't make much sense to wrap an API into a class if there isn't a clear benefit.
In the case of the Cabinet API, wrapping its functionality into a class library provides these benefits:
Reduces the number of callbacks that the user must create.
Presents an event-oriented interface that is familiar to .NET programmers.
Relieves the user of many bookkeeping tasks, such as creating and destroying contexts.
Eliminates the need to work with IntPtr and other low-level types.
Provides flexibility to override default handling through the use of virtual functions.
The completed classes, CabCompressor and CabDecompressor, meet these goals quite well. The example programs that result from using these new classes are much smaller and easier to understand than the functional duplicates that I presented in the previous article.
Writing wrapper classes for the Cabinet API turned out to be a bit more difficult than I had originally expected. I honestly thought that this would be a fairly simple article to write, considering all the work I'd done up to this point. It turned out that I was wrong, and I ended up fighting a number of annoying issues, mostly having to do with value semantics and debugging. (We won't mention the fretting DevSource editor.)
Inside the CabWrapper Class Library
I originally intended to include the CabCompressor and CabDecompressor classes in the same DLL that contains the Cabinet API interface. That would have required that only one assembly, CabDotNet.dll, be distributed with an application. However, including those classes in CabDotNet.dll made it nearly impossible
to debug, because of the way the assembly is created.
If you recall, the build process first builds the DLL, then decompiles to intermediate language in order to change the callbacks' calling convention. The final step is to compile the resulting modified code with ilasm.exe, in order to create the executable file. The build process makes the debug information point to the intermediate language code during debugging, rather than at the C# code. After too many hours getting nowhere by single-stepping through IL code, I changed things slightly. Now, the object interface to the Cabinet SDK functions is implemented in a separate assembly and namespace called CabWrapper. Doing things
this way makes you include two different assemblies in your application, but it sure made debugging easier.
While I was at it, I moved the FCntl class from the CabDotNet namespace, which left CabDotNet with only the Cabinet structures and the CabSdk class that holds the managed prototypes for the unmanaged Cabinet DLL functions. This provides a much cleaner separation between the .NET interface and the unmanaged DLL interface, and is probably the way I should have gone since the start, as far as the namespaces are concerned. Ideally, I'd be able to include both namespaces in a single assembly, but debugging concerns made that impossible.
The CabWrapper namespace also includes a class called CabIO, which contains common memory and file I/O methods for the CabCompressor and CabDecompressor classes. Remember, the only difference between the way that the file compression interface (FCI) and file decompression interface (FDI) access files is that FCI passes two extra parameters to the callbacks. I wrote the methods in CabIO to accept the two extra parameters, and made the callback functions in CabDecompressor pass dummy values for those two parameters when it calls the I/O methods. I hate unnecessary code duplication.
The final result is that the CabWrapper namespace contains four classes:
CabCompressor contains methods and events that let you quickly and easily create cabinet files.
CabDecompressor contains methods and events that let you read cabinet files and extract files from them.
CabIO contains static methods that implement common file input and output functions. These methods are accessed by the CabCompressor and CabDecompressor classes.
FCntl contains definitions and methods that convert between .NET values and the Windows and DOS-based values that are used by the Cabinet SDK.
Manipulating Society through Technology
Jeremy Bailenson, Director of the Virtual Human Interaction Lab at Stanford University, talks about virtual reality, avatars, Moore's law, how real world behaviors influence online reality, and societal manipulation through technology! >> Play video >> Read article >> See all videos