Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Structured Error Handling in VFP 8 : Page 2

With the introduction of Visual FoxPro 3.0, error handling in VFP changed substantially. Rather than using "on error" statements, "state of the art" error events became available. Now, seven years later, more sophisticated error handling mechanisms take center stage as Visual FoxPro 8.0 introduces structured error handling.


advertisement

Introducing: Try/Catch
To solve these issues, Visual FoxPro 8.0 introduces "Structured Error Handling." This approach allows the developer to wrap a series of commands into a block that is handled by a local error handler. The advantage of this error handler is that it usually handles a very limited set of potential problems, making it simple and straightforward.

This is the basic syntax for structured error handling in Visual FoxPro:

   TRY
      * Do something
   CATCH
      * Handle a potential problem
   ENDTRY
Let's see how we could re-work the above example into a scenario handled by a Try/Catch block:

   DEFINE CLASS WordExport AS Custom
      FUNCTION Export(lcText1,lcText2)
         LOCAL lReturnValue
         lReturnValue = .T.
         TRY
            * We run the regular code
            LOCAL oWord as Word.Application
            oWord = CREATEOBJECT("Word.Application")
            oWord.Application.Visible = .T.
            oWord.Documents.Add()
            oWord.Selection.InsertAfter(lcText1)
            oWord.Selection.InsertAfter(lcText2)
         CATCH 
            lReturnValue = .F.
         ENDTRY
         RETURN lReturnValue
      ENDFUNC
   ENDDEFINE
As we can see, this is a much simpler way to implement the solution. First of all, it is simply much less "kludgy" and is a very clean implementation. But more importantly, it is a much superior implementation from a technical point of view. The solution is not influenced by outside error handling.

Also, we have full control over what is to happen if an error does occur. Unlike in the example with the error event, we can write code within our method that executes no matter whether an error occurred or not, making it easy to set the return value to our liking. (We were able to do this in the previous example, but the solution was error prone and easy to break by running it in different environments).

Try/Catch blocks can be nested to achieve more granular error handling.
I'm sure that by now you already have a good idea about what Try/Catch does: Whatever code we run inside a Try block will execute until an error occurs. If an error does in fact occur, the Catch block is executed. Note that the Try block stops executing as soon as an error occurs. There is no way to retry or ignore the error. If that's what you would like to do, Try/Catch error handling is not the right solution.

Note that the Catch block is never executed if no error occurs. Sometimes you might want to define code that runs as cleanup code, whether an error occurred or not. Here is an example:



   DEFINE CLASS WordExport AS Custom
      FUNCTION Export(lcText1,lcText2)
         LOCAL lReturnValue
         lReturnValue = .T.
         TRY
            * We run the regular code
            LOCAL oWord as Word.Application
            oWord = CREATEOBJECT("Word.Application")
            oWord.Application.Visible = .T.
            oWord.Documents.Add()
            oWord.Selection.InsertAfter(lcText1)
            oWord.Selection.InsertAfter(lcText2)
         CATCH 
            lReturnValue = .F.
         FINALLY
            IF VarType(oWord) = "O"
               oWord.Application.Quit()
            ENDIF
         ENDTRY
         RETURN lReturnValue
      ENDFUNC
   ENDDEFINE
In this example, we shut down Word, even if something went wrong. Note however, that the error may have occurred before Word ever got instantiated. Therefore we need to first check whether Word is an object. (Actually, things may be a little trickier with automation objects, especially Word, but for simplicity we'll leave it at that.)

At this point you may wonder why we need a Finally block. After all, we could have put that code after the ENDTRY and would have achieved an identical result. However, there are scenarios that can greatly benefit from using the Finally block (which we will examine further down), making the use of Finally a good idea in general.

One last remark about the basic Try/Catch structure: Each Try block needs to have at least a Catch or a Finally block. Therefore, you can not just say "try this, and I don't care of it works or not since I can't do anything about a potential problem anyway." If you would like to do that, you can create a Catch block that has nothing but a comment. A scenario like this may be desired within an error handler:

   TRY
      USE Customer
      LOCATE FOR LastName = "Gates"
      IF FOUND()
         StrToFile("Gates found!","customer.log")
      ENDIF
   CATCH
      TRY
         StrToFile("Error: "+Message(),"Error.log")
      CATCH
         * Nothing we can do now
      ENDTRY
   FINALLY
      IF Used("Customer")
         USE IN Customer
      ENDIF
   ENDTRY
This example also demonstrates one of the key features of structured error handling: nested Try/Catch blocks.

Nested Try/Catch Blocks
Try/Catch blocks can be nested to achieve more granular error handling. There may be a Try/Catch block around the entire application, there may be Try/Catch blocks wrapping entire methods, then there may be individual blocks, and so forth.

Let's enhance our Word example a little more and instead of creating a blank document, we will create a new one based on a certain template:

   FUNCTION Export(lcText1,lcText2)
      LOCAL lReturnValue
      lReturnValue = .T.
      TRY
         * We run the regular code
         LOCAL oWord as Word.Application
         oWord = CREATEOBJECT("Word.Application")
         oWord.Application.Visible = .T.
         TRY
            oWord.Documents.Add("MyTemplate.dot")
         CATCH
            oWord.Documents.Add()
         ENDTRY
         oWord.Selection.InsertAfter(lcText1)
         oWord.Selection.InsertAfter(lcText2)
      CATCH 
         lReturnValue = .F.
      ENDTRY
      RETURN lReturnValue
   ENDFUNC
In this example, the inner Try/Catch block traps only errors that may occur while a new document is created based on the specified template. Presumably, if that template doesn't exist, an error will be raised and caught by the Catch block, which will create a blank document. The code then proceeds as planned.

Note that the Catch block may raise another error that will then be handled by the "outer" Catch block (which simply sets the return value and gives up).

There is one potential problem here. We are assuming that the error has been caused by the fact that the template doesn't exist. But of course, there could be a number of other scenarios causing other problems. For instance, the problem could be caused by the user closing Word right after it became visible (yes, they'd have to be very quick, but hey, this is only an example!). In our little example, this wouldn't be a problem. Worst case, the Catch block fails again and defaults to the outer handler, which will handle the situation appropriately. However, in many complex scenarios, we would have to look at additional error information and handle the situation appropriately.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date