Don’t get to excited, we aren’t talking about the END: of Uncle Pete! We are talking about the END: statement in WX. Notice the : , read on to find out what the heck that is all about.
What we are really talking about today, is cleaning up after yourself, something my wife doesn’t think I am very good at. From the online help:
“The “END:” statement is used to define a code that will be systematically run at the end of the process execution”
Well with great frenglish explanations like that no wonder there is need for blogs and webinars! In short by prefixing your code with an END: tag, you ensure that code will run as the last code before returning to the calling procedure. Even if an RETURN or RESULT statement is issued before the statements are reached, they will be ran. Its very similar to a destructor method in class code.
So how about a couple of practical examples.
Restore File Pointers
Say for example on your Product update form, you have a button that runs a procedure that looks up a different Product Record, perhaps to display inventory levels in similar products or check for duplicates, or kit parts etc. One of the challenges that this type of code always has is making sure that the file buffer is still correct when you return to the update form, otherwise you are likely to update the wrong record and have a mess on your hands.
You could run the procedure in a different thread and memory context, but what a PITA and opening up the possibilities for GPFs, memory leaks, etc. You could save the SysID of the product record (all your tables have a Unique SysID as the primary key, right!!!) Then at the end of the procedure make sure to refetch the record. But now your adding extra code, and making sure that even if your procedure exits early because of some error trapping code or something that your code to refetch the record, still runs.
A couple of Hyperfile commands and the END: statement to the rescue.
PROCEDURE CountSimilar() SavePosition is int = HSavePosition(Product) // Do some stuff with the file HReadSeekFirst(Product,ProductID,5) RETURN END: HRestorePosition(SavePosition)
In the above procedure the first statement uses the Hyperfile command HSavePosition to save the current information for the Product table. It returns as integer that is used when restoring the buffer later.
The next statements are obviously not production code, but just an example to prove that everything is working. The HReadSeekFirst, resets the file buffer to PRoductID =5, and the RETURN statement returns to the calling procedure. In this example removing the RETURN would make sure our restore code always ran but in a production application you might have a couple hundred lines of code with multiple exit points and it might not be as easy to make sure the restore code always runs.
The END: statement signals that the remaining code should always run at the completion of the procedure. And the HRestorePosition uses the integer value returned from HSavePosition to restore the Product table to the correct buffer.
So now even though there is a RETURN (or RESULT) statement in your code the Restore code still executes.
Processing an CSV File
Here is a little longer example that is closer to production code that I generally run into a few times on every project. The project will have a requirement to read and process a CSV file. Of course you can never count on the file being correctly formatted or have valid data. So your procedure ends up having a bunch of error validation code, many of which halt the execution of the procedure and return an error to the caller.
In the dark ages before I had the END: statement I would likely code this by having an ErrorMessage variable, storing the error in that variable and preform a break to exit the processing loop. In some instances there are several inner loops that also have to detect that they need to do an early exit. And when the code finally reaches the end of the processing loop, it test for a value in the ErrorMessage variable to know if the operation is a success or not in order to do the remaining processing. This can lead to lots of extra code, and as I like to preach, every line of code you write is another opportunity to introduce a bug! If not done perfectly best case scenario the ASC file doesn’t get closed and the user can’t figure out why the can’t edit and fix it, worst case your procedure exits in a half processed state and corrupts data.
The END: statement again comes to the rescue and cleans up the code considerably.
PROCEDURE Import() Seperator is string = "," Row is string ImportFile is int LineCount is int FileName is string = "C:temptest.csv" ImportFile = fOpen(FileName,foRead) IF ImportFile = -1 THEN RESULT("File to import was not found") ELSE // Skip Header Row FileSize is int = fSize(FileName) Row = fReadLine(ImportFile) LineCount++ LOOP Row = fReadLine(ImportFile) LineCount++ IF Row = EOT THEN BREAK END IF ExtractString(Row,firstRank,Seperator) = 0 THEN RESULT "Invalid or Inactive Company on Line " + LineCount END IF ExtractString(Row,nextRank,Seperator) = 0 THEN RESULT "Invalid or Inactive Branch on Line " + LineCount END IF ExtractString(Row,nextRank,Seperator) = 0 RESULT "Invalid or Inactive Account on Line " + LineCount END // Process Line Code goes Here WL.Trace(Row) END END RESULT "Success" END: WL.Trace("Going to Close the File") IF ImportFile > 0 fClose(ImportFile) END
Notice that there are several RESULT statements in the error processing code, however because of the END: statement the code to close the CSV file will always get fired. Much cleaner, fewer lines of code, less opportunity for bugs! I like it!!!
Be sure to go over to wxLive.us and watch the Uncle Pete’s corner webinar that is the companion to this article.
Uncle Pete’s Corner is weekly webinar on all things WX every Friday 7:15 AM CST (5:15 PST), to watch the recorded version of this webinar, many other WX related webinars or to watch future ones live go to wxLive.us