Using the CABINET.DLL Interface - ' Handling Errors in the ' (
Page 3 of 3 )
Sample Programs">
Handling Errors in the Sample Programs
Even though I made the callback functions return exception handles in the ErrorType field of the CabError structure, that doesn't relieve the client program from having to check FCI and FDI return codes. Those functions don't throw exceptions, so you have to check the return code and then throw the exception that your callback function created and stored. If it exists.
ADVERTISEMENT
It turns out that sometimes FCI or FDI overwrites the ErrorType value. In particular, when FCICreate attempts to create temporary files, the function doesn't abort immediately if it can't create one of the files. Instead, it tries to create all of them, and then aborts if any one of the files wasn't created. And the ErrorType property of the CabError structure will be zero.
The result is that any exceptions you store in the ErrorType property are lost. Worse, you can't free the GCHandle on those exception objects, so the garbage collector won't clean them up. This is less than ideal, but I've decided to accept it because the objects are cleaned up when the program stops executing. I figured that a little memory leak for the short duration of the program is acceptable. I would revisit that decision if my program was a server process that was expected to remain running for an
extended period.
The trick to handling Cabinet API errors is to check the return code and throw an exception if the return code indicates failure. In the FCI sample program, I have a single function that examines the ErrorType property in the CabError structure and throws the
exception if it exists (is non-null). If ErrorType is zero, then I throw an ApplicationException for which I've created a custom message that contains the FciErrorCode value and its description. This makes it possible to wrap the entire main program in a single try/catch block so I can handle errors in one place.
You'll notice that a very large part of the code in the callback functions is error handling. Every callback is wrapped in a try/catch block and exceptions are either returned in the field provided, or swallowed because there is no way to return error information. It is especially important to handle all exceptions that are thrown in the callbacks. If you fail to catch an exception in a callback, the program will abort with a
System.ExecutionEngineException, possibly corrupting files that remain open.
That's A Lot of Work!
The testfdi and testfci sample programs weigh in at about 350 and 550 lines of code, respectively. Most of that code is the callback delegates—things that probably won't change from one program to the next. The memory allocation and file I/O delegates, in particular, probably won't change at all for most .NET programs that use the Cabinet API.
In addition, much of the setup code, creating contexts, and just normal stuff will be the same for almost all programs. As it stands right now, you'd need to duplicate all of that code if you wanted to create a new program that reads or writes cabinet files. That's a lot of unnecessary work.
In my next article, I'll discuss the development of two classes, CabCompressor and CabDecompressor, that encapsulate all of the common code and expose an event interface to clients. The resulting classes make it possible to implement the sample programs in much less code than I used in this article.
You can find the code and project files for this article
here. That archive includes all of the code from the previous article, as well as the code for this article.