Communicating Exceptions
In the previous section, I wrote that a component's interface is made up of the classes it exposes, along with the properties, methods, and events for those classes. But there is another code communication mechanism that must be used, and this one is a little less obvious to developers who are just beginning to develop components.
In a typical Windows program, when anything abnormal occurs during processing, it is natural to display a message box to the user to notify him or her about it. However, when you create a COM component that might be used either from a Windows or Web UI, you must be sure to never code a call to the MsgBox function. If you do, the message box will be displayed on the computer where the component is installed and running. In the case of a Web application, this obviously means the message box will be displayed on a server where no one will see it, not displayed from the user's Web browser client.
So, instead of calling the MsgBox function from a component when an exception occurs, the proper thing to do is to use the Visual Basic Err object's Raise method, and hope that the code that the client code using the component has an appropriate error handler defined. Basically, when it comes to handling exceptions, COM components should have the "Something unexpected has happened and I can't continue processing, so whoever called me needs to do something about it" attitude.
The simple syntax of the Err object's Raise method is
Call Err.Raise(number, source, description)
where number is a unique identifier for this exception, source is the name of the object that generated the exception, and description is a user-friendly string that describes what has happened.
In the sample DHO, there is only one case where we really need to raise an error like this. The component's interface design includes the clsPublisherList.Fetch method to retrieve the Publisher from the database for the given PubID. If a record with a matching PubID isn't in the database, the component will have to raise an error that indicates the PubID was not found. The syntax of Err.Raise requires an error number to be supplied, so what value should be used? To guarantee that the number you choose for the error number isn't the same as an error number already used by something else, Visual Basic includes the vbObjectError constant, which you use to add to your own internal error number. For example, to raise an error indicating a Publisher with the given PubID wasn't found in the database, you might code the following:
Call Err.Raise(vbObjectError + 1, "clsPublisher", _ "PubID " & CStr(PubID) & " not found.")
But to give the code that uses your component an easy method of determining what kind of exception occurred, we should associate constants with the actual error number values and make these constants available from outside of the component. These constants then also become part of the component's interface. I find enumerated types work very well for this purpose.
I suggest that all errors that a given class might raise be defined as Public Enums in the class module's General Declaration section. For example, if clsPublisher could raise a not found error and a duplicate key error, we might define constants for these errors like this:
Public Enum PublisherErrors peNotFound = vbObjectError + 1 peDupPubID End Enum
And then raise an error like this:
Call Err.Raise(peNotFound, "clsPublisher", _ "PubID " & CStr(PubID) & " not found.")
Unfortunately, even if you define the error numbers as Public Enums in a class module, they don't really belong to that class module, and are instead treated as global scope. So if your code calls a method in a component and wants to test for a specific error, you write this:
If Err.Number = peNotFound Then ...Or prefix it with the component name like this:
If Err.Number = PublishersDHO.peNotFound Then ...But you don't prefix it with the class name like this:
If Err.Number = clsPublisher.peNotFound Then ...That's why you should use some sort of prefixing scheme to attempt to make your error number constants unique to the class and/or component they are raised from.