Testing a VFP DLL

A DLL can't run by itself and you can only test it by making a call to the DLL. It's easiest to start by calling the class from within FoxPro itself. This will make sure that there are no internal errors of logic. Before doing that though we'll need to be able to handle the errors in the FoxPro code.

Errors

A DLL cannot have any user interface. If the DLL tries to produce any form of output you'll get an error 2031 "User-interface operation not allowed at this time". This of course includes any error message so the DLL must have comprehensive error-handling. Debugging becomes impossible if all error messages are suppressed so my own preference is to trap every error and to write details to a text file.

The Error method of the class will run whenever there is an error in the class and this code will write the basic details of the error to a text file:

Procedure Error
   Lparameters nError, cMethod, nLine

   Local lcError
   Local lcLine
   Local lcMarker
   Local lcMethod
   Local lcTimeStamp

   *-- Build the strings for the text file.  
   lcMarker = Replicate("-", 35)
   lcTimeStamp = "Error at …..:" + Ttoc(Datetime())
   lcLine = "Line ………:" + Alltrim(Str(nLine, 6, 0))
   lcError = "Error ……..:" + Alltrim(Str(nError, 6, 0))
   lcMethod = "Method …….:" + cMethod

   Set Textmerge On
   Set Textmerge To c:\Error.txt Additive
\<<lcMarker>>
\<<lcTimeStamp>>
\<<lcError>>
\<<lcLine>>
\<<lcMethod>>
   Set Textmerge To
   Set Textmerge Off

   Return
Endproc

I'm using TextMerge to add lines to the error log. Note that the lines within the TextMerge aren't indented. If they were then the indents would be output to the text file too. The code above generates error log entries looking like this:

   -----------------------------------
   Error at .....:19/11/10 11:56:46
   Error ........:36
   Line .........:20
   Method .......:init

With a bit more work you can use the Program() function to loop through the call stack and show how the program reached the error.

Testing the class from VFP

A simple program can create the class and call the two methods:

   Local lcCustID As String
   Local loCust As Customer

   Close Databases All
   Set Procedure To Customer.prg

   loCust = Createobject("customer")
   Messagebox(loCust.OpenTable())

   lcCustID = INPUTBOX("Enter customer ID")
   If (loCust.Getname(lcCustID))
     Messagebox(loCust.Company)
   Else
     Messagebox("Cannot find " + lcCustID)
   Endif

   Return

Run the program with a known customer ID such as "RANCH" and with one that doesn't exist. Edit the text of customer.prg with a silly syntax error to make sure that the error will be detected - I used SET SAFELY ON.

The next steps

The first step in this process was to write a class as the basis for a DLL. The next two steps are to build the DLL and then to call it from Visual Basic or C#.